Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 1 | #!/bin/bash |
| 2 | |
| 3 | # Usage: |
| 4 | # ./formula-fetch.sh <Formula URL> <Name> <Branch> |
| 5 | # |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 6 | # Example usage: |
| 7 | # FORMULA_SOURCES=https://github.com/epcim/my-salt-formulas https://github.com/salt-formulas https://github.com/saltstack-formulas |
| 8 | # SALT_ENV_PATH=.vendor/formulas |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 9 | # -- |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 10 | # ./formula-fetch.sh |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 11 | # xargs -n1 ./formula-fetch.sh < dependencies.txt |
| 12 | |
| 13 | |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 14 | ## DEFAULTS |
| 15 | # |
| 16 | # default sources |
| 17 | FORMULA_SOURCES="${SALT_FORMULA_SOURCES:-https://github.com/salt-formulas https://github.com/saltstack-formulas}" |
| 18 | FORMULA_VERSION="${SALT_FORMULA_VERSION:-master}" |
Petr Michalec | fbebe99 | 2018-03-29 10:07:48 +0200 | [diff] [blame] | 19 | # where to fetch formulas |
Petr Michalec | 7b8c20b | 2018-03-29 16:31:16 +0200 | [diff] [blame] | 20 | FORMULAS_BASE=${SALT_FORMULAS_BASE:-/srv/salt/formula} |
Petr Michalec | fbebe99 | 2018-03-29 10:07:48 +0200 | [diff] [blame] | 21 | # For better stability, skip formula repos without recognized CI |
| 22 | FORMULA_WITHOUT_CI=${SALT_FORMULA_WITHOUT_CI:-false} |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 23 | # salt env/root, where formulas are found |
| 24 | SALT_ENV_PATH=${SALT_ENV_PATH:-/srv/salt/env/prd} |
| 25 | #SALT_ENV_PATH=${SALT_ENV_PATH:-.vendor/formulas} |
| 26 | #SALT_ENV_PATH=${SALT_ENV_PATH:/usr/share/salt-formulas/env/_formulas} |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 27 | # reclass related |
| 28 | RECLASS_BASE=${RECLASS_BASE:-/srv/salt/reclass} |
Petr Michalec | fbebe99 | 2018-03-29 10:07:48 +0200 | [diff] [blame] | 29 | # env |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 30 | LC_ALL=en_US.UTF-8 |
| 31 | LANG=en_US.UTF-8 |
| 32 | |
| 33 | |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 34 | # Parse git dependencies from metadata.yml |
| 35 | # $1 - path to <formula>/metadata.yml |
| 36 | # sample to output: |
| 37 | # https://github.com/salt-formulas/salt-formula-git git |
| 38 | # https://github.com/salt-formulas/salt-formula-salt salt |
| 39 | function fetchDependencies() { |
| 40 | METADATA="$1"; |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 41 | grep -E "^dependencies:" "$METADATA" &>/dev/null || return 0 |
| 42 | (python3 - "$METADATA" | while read dep; do fetchGitFormula $dep; done) <<-DEPS |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 43 | import sys,yaml |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 44 | try: |
| 45 | for dep in yaml.load(open(sys.argv[1], "r"))["dependencies"]: |
| 46 | if len(set(('name', 'source')) & set(dep.keys())) == 2: |
| 47 | print("{source} {name}".format(**dep)) |
Petr Michalec | b29b9cd | 2018-03-29 15:49:28 +0200 | [diff] [blame] | 48 | except Exception as e: |
| 49 | print("[W] {}".format(e.__doc__)) |
| 50 | print("[W] {}".format(e.message)) |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 51 | pass |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 52 | DEPS |
| 53 | } |
| 54 | |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 55 | |
| 56 | # Read formula name from meetadata.yml |
| 57 | # $1 - path to <formula>/metadata.yml |
| 58 | function getFormulaName() { |
| 59 | python3 - "$1" <<-READ_NAME |
| 60 | try: |
| 61 | import sys,yaml;print(yaml.load(open(sys.argv[1], "r"))["name"]); |
Petr Michalec | b29b9cd | 2018-03-29 15:49:28 +0200 | [diff] [blame] | 62 | except Exception as e: |
| 63 | print("[W] {}".format(e.__doc__)) |
| 64 | print("[W] {}".format(e.message)) |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 65 | pass |
| 66 | READ_NAME |
| 67 | } |
| 68 | |
| 69 | |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 70 | # Fetch formula from git repo |
| 71 | # $1 - formula git repo url |
| 72 | # $2 - formula name (optional) |
| 73 | # $3 - branch (optional) |
| 74 | function fetchGitFormula() { |
| 75 | test -n "${FETCHED}" || declare -a FETCHED=() |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 76 | mkdir -p "$SALT_ENV_PATH" "$FORMULAS_BASE" |
| 77 | |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 78 | if [ -n "$1" ]; then |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 79 | |
| 80 | # set origin uri |
| 81 | # FIXME, TEMP fix for not yet up to date gh:salt-formulas -> s/tcpcloud/salt-formulas/ |
| 82 | origin="${1/tcpcloud/salt-formulas}" |
| 83 | # set gh repo https://salt-formulas/salt-formula-salt -> $FORMULAS_BASE/salt-formulas/salt-formula-salt |
| 84 | repo=$(echo $origin | awk -F'/' '{ print substr($0, index($0,$4)) }') |
| 85 | # set normula name |
| 86 | test -n "$2" && name=$2 || name="$(echo ${origin//*\/} | sed -e 's/-formula$//' -e 's/^salt-formula-//' -e 's/^formula-//')" |
| 87 | # set branch |
| 88 | test -n "$3" && branch=$3 || branch=${FORMULA_VERSION} |
| 89 | |
| 90 | # DEBUG |
| 91 | #echo '--- ------------------------------' |
| 92 | #echo origin, $origin |
| 93 | #echo repo, $repo |
| 94 | #echo fetched ${FETCHED[@]} |
| 95 | #echo -e name, $name |
| 96 | #echo '---' |
| 97 | #return |
| 98 | |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 99 | if ! [[ "${FETCHED[*]}" =~ $name ]]; then # dependency not yet fetched |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 100 | echo -e "[I] Fetching: $origin -> $FORMULAS_BASE/$repo" |
| 101 | if [ -e "$FORMULAS_BASE/$repo" ]; then |
| 102 | pushd "$FORMULAS_BASE/$repo" &>/dev/null |
| 103 | git pull -r; git checkout $branch; |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 104 | popd &>/dev/null |
| 105 | else |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 106 | echo -e "[I] git clone $origin $FORMULAS_BASE/$repo -b $branch" |
| 107 | if ! git ls-remote --exit-code --heads $origin $branch; then |
| 108 | # Fallback to the master branch if the branch doesn't exist for this repository |
| 109 | branch=master |
| 110 | fi |
| 111 | if ! git clone "$origin" "$FORMULAS_BASE/$repo" -b "$branch"; then |
| 112 | echo -e "[E] Fetching formula from $origin failed." |
| 113 | return ${FAIL_ON_ERRORS:-0} |
| 114 | fi |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 115 | fi |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 116 | |
Petr Michalec | 636976c | 2018-03-29 21:28:38 +0200 | [diff] [blame] | 117 | # A metadata.yml is github.com/salt-formulas specific |
| 118 | if [ ! -n "$2" -a -e "$FORMULAS_BASE/$repo/metadata.yml" ]; then |
| 119 | # try to update name as in formula metadata |
| 120 | name=$(getFormulaName "$FORMULAS_BASE/$repo/metadata.yml") |
| 121 | fi |
| 122 | |
Petr Michalec | dc93f0b | 2018-03-31 16:20:58 +0200 | [diff] [blame] | 123 | # FIXME, better formula recognition/name fixup for saltstack formulas |
Petr Michalec | 636976c | 2018-03-29 21:28:38 +0200 | [diff] [blame] | 124 | # Recognize the repo is formula |
| 125 | if [ ! -e $FORMULAS_BASE/$repo/$name ]; then |
| 126 | echo -e "[E] The repository $FORMULAS_BASE/$repo was not recognized as formula repository." |
Petr Michalec | dc93f0b | 2018-03-31 16:20:58 +0200 | [diff] [blame] | 127 | rm -rf "$FORMULAS_BASE/$repo" |
Petr Michalec | 636976c | 2018-03-29 21:28:38 +0200 | [diff] [blame] | 128 | return ${FAIL_ON_ERRORS:-0} |
| 129 | fi |
| 130 | |
Petr Michalec | fbebe99 | 2018-03-29 10:07:48 +0200 | [diff] [blame] | 131 | # Avoid checkout formulas/repos without CI |
| 132 | if ! $FORMULA_WITHOUT_CI; then |
| 133 | CI=false |
Petr Michalec | 636976c | 2018-03-29 21:28:38 +0200 | [diff] [blame] | 134 | for p in .circleci .travis.yml .kitchen.yml invoke.yml tasks.py tox.ini test tests; do |
Petr Michalec | fbebe99 | 2018-03-29 10:07:48 +0200 | [diff] [blame] | 135 | if [ -e "$FORMULAS_BASE/$repo/$p" ]; then |
| 136 | CI=true; break; |
| 137 | fi |
| 138 | done |
| 139 | if ! $CI; then |
| 140 | mv "$FORMULAS_BASE/$repo" "$FORMULAS_BASE/${repo}.deprecated-no-ci"; |
Petr Michalec | 636976c | 2018-03-29 21:28:38 +0200 | [diff] [blame] | 141 | return ${FAIL_ON_ERRORS:-0} |
Petr Michalec | fbebe99 | 2018-03-29 10:07:48 +0200 | [diff] [blame] | 142 | fi |
| 143 | fi |
| 144 | |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 145 | # SET FORMULA IN SALT ENV |
| 146 | if [ ! -e "$SALT_ENV_PATH/$name" ]; then |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 147 | |
Petr Michalec | 636976c | 2018-03-29 21:28:38 +0200 | [diff] [blame] | 148 | # link formula |
| 149 | ln -svf $FORMULAS_BASE/$repo/$name $SALT_ENV_PATH/$name |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 150 | |
Petr Michalec | 636976c | 2018-03-29 21:28:38 +0200 | [diff] [blame] | 151 | # copy custom _states, _modules, _etc ... |
| 152 | for c in $(/bin/ls $FORMULAS_BASE/$repo | grep '^_' | xargs -n1 --no-run-if-empty); do |
| 153 | test -e $SALT_ENV_PATH/$c || mkdir -p $SALT_ENV_PATH/$c |
| 154 | ln -svf $FORMULAS_BASE/$repo/$c/* $SALT_ENV_PATH/$c |
| 155 | done |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 156 | |
Petr Michalec | 636976c | 2018-03-29 21:28:38 +0200 | [diff] [blame] | 157 | # install optional dependencies (python/pip related as of now only) |
| 158 | if [ -e $FORMULAS_BASE/$repo/requirements.txt ]; then |
| 159 | pip install -r $FORMULAS_BASE/$repo/requirements.txt |
| 160 | fi |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 161 | |
Petr Michalec | 636976c | 2018-03-29 21:28:38 +0200 | [diff] [blame] | 162 | # NOTE: github.com/salt-formulas specific steps |
| 163 | # link formula service pillars |
Petr Michalec | 4a5461c | 2018-03-29 22:28:28 +0200 | [diff] [blame] | 164 | if [ -n "$RECLASS_BASE" -a -e "$FORMULAS_BASE/$repo/metadata/service" ]; then |
| 165 | test -e $RECLASS_BASE/classes/service || mkdir -p $RECLASS_BASE/classes/service |
| 166 | ln -svf $FORMULAS_BASE/$repo/metadata/service $RECLASS_BASE/classes/service/$name |
Petr Michalec | 636976c | 2018-03-29 21:28:38 +0200 | [diff] [blame] | 167 | fi |
| 168 | # install dependencies |
| 169 | FETCHED+=($name) |
| 170 | if [ -e "$FORMULAS_BASE/$repo/metadata.yml" ]; then |
| 171 | fetchDependencies "$FORMULAS_BASE/$repo/metadata.yml" |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 172 | fi |
| 173 | else |
| 174 | echo -e "[I] Formula "$name" already fetched." |
| 175 | fi |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 176 | fi |
| 177 | else |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 178 | echo -e '[I] Usage: fetchGitFormula git_repo_uri [branch] [local formula directory name]' |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 179 | fi |
| 180 | } |
| 181 | |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 182 | # DEPRECATED, kept for backward compatibility |
| 183 | # for github.com/salt-formulas (linking "service" pillar metadata from formula to reclas classes) |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 184 | function linkFormulas() { |
| 185 | # OPTIONAL: Link formulas from git/pkg |
| 186 | |
| 187 | SALT_ROOT=$1 |
| 188 | SALT_ENV=${2:-/usr/share/salt-formulas/env} |
| 189 | |
| 190 | # form git, development versions |
| 191 | find "$SALT_ENV"/_formulas -maxdepth 1 -mindepth 1 -type d -print0| xargs -0 -n1 --no-run-if-empty basename | xargs -I{} --no-run-if-empty \ |
| 192 | ln -fs "$SALT_ENV"/_formulas/{}/{} "$SALT_ROOT"/{}; |
| 193 | |
| 194 | # form pkgs |
| 195 | find "$SALT_ENV" -maxdepth 1 -mindepth 1 -path "*_formulas*" -prune -o -name "*" -type d -print0| xargs -0 -n1 --no-run-if-empty basename | xargs -I{} --no-run-if-empty \ |
| 196 | ln -fs "$SALT_ENV"/{} "$SALT_ROOT"/{}; |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 197 | } |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 198 | |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 199 | |
| 200 | function setupPyEnv() { |
| 201 | MODULES="pygithub pyyaml" |
| 202 | pip3 install --upgrade $MODULES || { |
| 203 | which pipenv || { |
| 204 | pip install --upgrade pipenv |
| 205 | } |
| 206 | pipenv --three |
| 207 | pipenv install $MODULES |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | function listRepos_github_com() { |
| 212 | #export python=$(pipenv --py || (setupPyEnv &>/dev/null; pipenv --py)) |
| 213 | if [ -e Pipfile.lock ]; then python=$(pipenv --py); else python=python3; fi |
| 214 | $python - "$1" <<-LIST_REPOS |
| 215 | import sys |
| 216 | import github |
| 217 | |
| 218 | def make_github_agent(user=None, password=None): |
| 219 | """ Create github agent to auth """ |
| 220 | if not user: |
| 221 | return github.Github() |
| 222 | else: |
| 223 | return github.Github(user, password) |
| 224 | |
| 225 | def get_org_repos(gh, org_name): |
| 226 | org = gh.get_organization(org_name) |
| 227 | for repo in org.get_repos(): |
| 228 | yield repo.name |
| 229 | |
Petr Michalec | b29b9cd | 2018-03-29 15:49:28 +0200 | [diff] [blame] | 230 | try: |
| 231 | print(*get_org_repos(make_github_agent(), str(sys.argv[1])), sep="\n") |
| 232 | except Exception as e: |
| 233 | print("[E] {}".format(e.__doc__)) |
| 234 | print("[E] {}".format(e.message)) |
| 235 | sys.exit(1) |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 236 | LIST_REPOS |
| 237 | } |
| 238 | |
| 239 | function fetchAll() { |
Petr Michalec | 48fb30d | 2018-03-29 16:49:51 +0200 | [diff] [blame] | 240 | # iterate over all defined sources |
| 241 | for source in $(echo ${FORMULA_SOURCES} | xargs -n1 --no-run-if-empty); do |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 242 | hosting=$(echo ${source//\./_} | awk -F'/' '{print $3}') |
| 243 | orgname=$(echo ${source//\./_} | awk -F'/' '{print $4}') |
Petr Michalec | 48fb30d | 2018-03-29 16:49:51 +0200 | [diff] [blame] | 244 | |
| 245 | # Get repos. To protect builds on master we likely fail on errors/none while getting repos from $source |
| 246 | set -o pipefail |
| 247 | repos="$(listRepos_$hosting "$orgname" | xargs -n1 --no-run-if-empty| sort)" |
| 248 | set +o pipefail |
| 249 | if [ ! -n "$repos" ]; then |
| 250 | echo "[E] Error caught or no repositories found at $source. Exiting."; |
| 251 | exit 1; |
| 252 | fi |
| 253 | |
| 254 | # fetch all repos that looks like formula |
| 255 | for repo in $(echo ${repos} | xargs -n1 --no-run-if-empty); do |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 256 | # TODO, avoid a hardcoded pattern to filter formula repos |
| 257 | if [[ $repo =~ ^(.*formula.*)$ ]]; then |
| 258 | fetchGitFormula "$source/$repo"; |
| 259 | fi |
| 260 | done; |
Petr Michalec | 48fb30d | 2018-03-29 16:49:51 +0200 | [diff] [blame] | 261 | |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 262 | done; |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 263 | } |
| 264 | |
| 265 | # detect if file is being sourced |
| 266 | [[ "$0" != "$BASH_SOURCE" ]] || { |
Petr Michalec | 40e909d | 2018-03-26 21:15:50 +0200 | [diff] [blame] | 267 | # if executed, fetch specific formula |
| 268 | fetchGitFormula ${@} |
Ales Komarek | 1b37311 | 2017-08-08 08:48:56 +0200 | [diff] [blame] | 269 | } |