blob: f103fcde7f3a2ae928c2bf4b0b61346e90eebc7e [file] [log] [blame]
Ales Komarek1b373112017-08-08 08:48:56 +02001#!/bin/bash
2
3# Usage:
4# ./formula-fetch.sh <Formula URL> <Name> <Branch>
5#
Petr Michalec40e909d2018-03-26 21:15:50 +02006# 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 Komarek1b373112017-08-08 08:48:56 +02009# --
Petr Michalec40e909d2018-03-26 21:15:50 +020010# ./formula-fetch.sh
Ales Komarek1b373112017-08-08 08:48:56 +020011# xargs -n1 ./formula-fetch.sh < dependencies.txt
12
13
Petr Michalec40e909d2018-03-26 21:15:50 +020014## DEFAULTS
15#
16# default sources
17FORMULA_SOURCES="${SALT_FORMULA_SOURCES:-https://github.com/salt-formulas https://github.com/saltstack-formulas}"
18FORMULA_VERSION="${SALT_FORMULA_VERSION:-master}"
Petr Michalecfbebe992018-03-29 10:07:48 +020019# where to fetch formulas
Petr Michalec7b8c20b2018-03-29 16:31:16 +020020FORMULAS_BASE=${SALT_FORMULAS_BASE:-/srv/salt/formula}
Petr Michalecfbebe992018-03-29 10:07:48 +020021# For better stability, skip formula repos without recognized CI
22FORMULA_WITHOUT_CI=${SALT_FORMULA_WITHOUT_CI:-false}
Petr Michalec40e909d2018-03-26 21:15:50 +020023# salt env/root, where formulas are found
24SALT_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 Michalec40e909d2018-03-26 21:15:50 +020027# reclass related
28RECLASS_BASE=${RECLASS_BASE:-/srv/salt/reclass}
Petr Michalecfbebe992018-03-29 10:07:48 +020029# env
Petr Michalec40e909d2018-03-26 21:15:50 +020030LC_ALL=en_US.UTF-8
31LANG=en_US.UTF-8
32
33
Ales Komarek1b373112017-08-08 08:48:56 +020034# 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
39function fetchDependencies() {
40 METADATA="$1";
Petr Michalec40e909d2018-03-26 21:15:50 +020041 grep -E "^dependencies:" "$METADATA" &>/dev/null || return 0
42 (python3 - "$METADATA" | while read dep; do fetchGitFormula $dep; done) <<-DEPS
Ales Komarek1b373112017-08-08 08:48:56 +020043 import sys,yaml
Petr Michalec40e909d2018-03-26 21:15:50 +020044 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 Michalecb29b9cd2018-03-29 15:49:28 +020048 except Exception as e:
49 print("[W] {}".format(e.__doc__))
50 print("[W] {}".format(e.message))
Petr Michalec40e909d2018-03-26 21:15:50 +020051 pass
Ales Komarek1b373112017-08-08 08:48:56 +020052 DEPS
53}
54
Petr Michalec40e909d2018-03-26 21:15:50 +020055
56# Read formula name from meetadata.yml
57# $1 - path to <formula>/metadata.yml
58function getFormulaName() {
59 python3 - "$1" <<-READ_NAME
60 try:
61 import sys,yaml;print(yaml.load(open(sys.argv[1], "r"))["name"]);
Petr Michalecb29b9cd2018-03-29 15:49:28 +020062 except Exception as e:
63 print("[W] {}".format(e.__doc__))
64 print("[W] {}".format(e.message))
Petr Michalec40e909d2018-03-26 21:15:50 +020065 pass
66 READ_NAME
67}
68
69
Ales Komarek1b373112017-08-08 08:48:56 +020070# Fetch formula from git repo
71# $1 - formula git repo url
72# $2 - formula name (optional)
73# $3 - branch (optional)
74function fetchGitFormula() {
75 test -n "${FETCHED}" || declare -a FETCHED=()
Petr Michalec40e909d2018-03-26 21:15:50 +020076 mkdir -p "$SALT_ENV_PATH" "$FORMULAS_BASE"
77
Ales Komarek1b373112017-08-08 08:48:56 +020078 if [ -n "$1" ]; then
Petr Michalec40e909d2018-03-26 21:15:50 +020079
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 Komarek1b373112017-08-08 08:48:56 +020099 if ! [[ "${FETCHED[*]}" =~ $name ]]; then # dependency not yet fetched
Petr Michalec40e909d2018-03-26 21:15:50 +0200100 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 Komarek1b373112017-08-08 08:48:56 +0200104 popd &>/dev/null
105 else
Petr Michalec40e909d2018-03-26 21:15:50 +0200106 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 Komarek1b373112017-08-08 08:48:56 +0200115 fi
Petr Michalec40e909d2018-03-26 21:15:50 +0200116
Petr Michalec636976c2018-03-29 21:28:38 +0200117 # 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 Michalecdc93f0b2018-03-31 16:20:58 +0200123 # FIXME, better formula recognition/name fixup for saltstack formulas
Petr Michalec636976c2018-03-29 21:28:38 +0200124 # 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 Michalecdc93f0b2018-03-31 16:20:58 +0200127 rm -rf "$FORMULAS_BASE/$repo"
Petr Michalec636976c2018-03-29 21:28:38 +0200128 return ${FAIL_ON_ERRORS:-0}
129 fi
130
Petr Michalecfbebe992018-03-29 10:07:48 +0200131 # Avoid checkout formulas/repos without CI
132 if ! $FORMULA_WITHOUT_CI; then
133 CI=false
Petr Michalec636976c2018-03-29 21:28:38 +0200134 for p in .circleci .travis.yml .kitchen.yml invoke.yml tasks.py tox.ini test tests; do
Petr Michalecfbebe992018-03-29 10:07:48 +0200135 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 Michalec636976c2018-03-29 21:28:38 +0200141 return ${FAIL_ON_ERRORS:-0}
Petr Michalecfbebe992018-03-29 10:07:48 +0200142 fi
143 fi
144
Petr Michalec40e909d2018-03-26 21:15:50 +0200145 # SET FORMULA IN SALT ENV
146 if [ ! -e "$SALT_ENV_PATH/$name" ]; then
Petr Michalec40e909d2018-03-26 21:15:50 +0200147
Petr Michalec636976c2018-03-29 21:28:38 +0200148 # link formula
149 ln -svf $FORMULAS_BASE/$repo/$name $SALT_ENV_PATH/$name
Petr Michalec40e909d2018-03-26 21:15:50 +0200150
Petr Michalec636976c2018-03-29 21:28:38 +0200151 # 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 Michalec40e909d2018-03-26 21:15:50 +0200156
Petr Michalec636976c2018-03-29 21:28:38 +0200157 # 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 Michalec40e909d2018-03-26 21:15:50 +0200161
Petr Michalec636976c2018-03-29 21:28:38 +0200162 # NOTE: github.com/salt-formulas specific steps
163 # link formula service pillars
Petr Michalec4a5461c2018-03-29 22:28:28 +0200164 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 Michalec636976c2018-03-29 21:28:38 +0200167 fi
168 # install dependencies
169 FETCHED+=($name)
170 if [ -e "$FORMULAS_BASE/$repo/metadata.yml" ]; then
171 fetchDependencies "$FORMULAS_BASE/$repo/metadata.yml"
Petr Michalec40e909d2018-03-26 21:15:50 +0200172 fi
173 else
174 echo -e "[I] Formula "$name" already fetched."
175 fi
Ales Komarek1b373112017-08-08 08:48:56 +0200176 fi
177 else
Petr Michalec40e909d2018-03-26 21:15:50 +0200178 echo -e '[I] Usage: fetchGitFormula git_repo_uri [branch] [local formula directory name]'
Ales Komarek1b373112017-08-08 08:48:56 +0200179 fi
180}
181
Petr Michalec40e909d2018-03-26 21:15:50 +0200182# DEPRECATED, kept for backward compatibility
183# for github.com/salt-formulas (linking "service" pillar metadata from formula to reclas classes)
Ales Komarek1b373112017-08-08 08:48:56 +0200184function 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 Michalec40e909d2018-03-26 21:15:50 +0200197}
Ales Komarek1b373112017-08-08 08:48:56 +0200198
Petr Michalec40e909d2018-03-26 21:15:50 +0200199
200function 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
211function 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 Michalecb29b9cd2018-03-29 15:49:28 +0200230 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 Michalec40e909d2018-03-26 21:15:50 +0200236 LIST_REPOS
237}
238
239function fetchAll() {
Petr Michalec48fb30d2018-03-29 16:49:51 +0200240 # iterate over all defined sources
241 for source in $(echo ${FORMULA_SOURCES} | xargs -n1 --no-run-if-empty); do
Petr Michalec40e909d2018-03-26 21:15:50 +0200242 hosting=$(echo ${source//\./_} | awk -F'/' '{print $3}')
243 orgname=$(echo ${source//\./_} | awk -F'/' '{print $4}')
Petr Michalec48fb30d2018-03-29 16:49:51 +0200244
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 Michalec40e909d2018-03-26 21:15:50 +0200256 # TODO, avoid a hardcoded pattern to filter formula repos
257 if [[ $repo =~ ^(.*formula.*)$ ]]; then
258 fetchGitFormula "$source/$repo";
259 fi
260 done;
Petr Michalec48fb30d2018-03-29 16:49:51 +0200261
Petr Michalec40e909d2018-03-26 21:15:50 +0200262 done;
Ales Komarek1b373112017-08-08 08:48:56 +0200263}
264
265# detect if file is being sourced
266[[ "$0" != "$BASH_SOURCE" ]] || {
Petr Michalec40e909d2018-03-26 21:15:50 +0200267 # if executed, fetch specific formula
268 fetchGitFormula ${@}
Ales Komarek1b373112017-08-08 08:48:56 +0200269}