3 # Ansible Boilerplate Collection: Setup & Maintenance Script.
6 # Detect commands to use:
7 PYTHON="${PYTHON:-$(command -v python3 2>/dev/null || echo "python3")}"
8 PIP="${PIP:-$(command -v pip 2>/dev/null || echo "pip")}"
9 ANSIBLE_GALAXY="${ANSIBLE_GALAXY:-$(command -v ansible-galaxy 2>/dev/null || echo "ansible-galaxy")}"
11 BASE_D="ansible_galaxy/ansible_collections/alexbarton/boilerplate"
14 # Show usage information on stderr.
20 echo " help Show this help text and exit."
21 echo " init Initialize project and boilerplate code."
22 echo " upgrade Upgrade boilerplate code and dependencies. [Alias: update, up]"
23 echo " --force Force overwriting an existing role or collection."
29 # Initialize a new project.
31 # Create some default files and call Upgrade() afterwards. This function does not
32 # overwrite any already existing file.
39 if [ -e Makefile.boilerplate ]; then
40 echo "This is the upstream project! Don't call \"init\" on it!" >&2
43 echo "Initialize project:"
49 test -e "${file}" || touch "${file}"
51 mkdir -p .vscode playbooks roles
52 test -e "hosts.ini" || Init_HostsIni
53 test -e "Makefile" || Init_Makefile
54 test -e "requirements.yml" || Init_RequirementsYml
60 # Create a Makefile template file.
63 echo "Creating \"Makefile\" ..."
69 SOURCE_ROOT ?= \$(CURDIR)
71 # Make sure that the Ansible Boilerplate Collection project is set up (its
72 # files are "available"), and initialize it if not!
73 _DUMMY := \$(shell test -e ansible_galaxy/ansible_collections/alexbarton/boilerplate/Makefile.boilerplate || bin/ansible-boilerplate upgrade >&2)
77 # Include the Ansible Boilerplate Collection Makefile fragment:
78 include ansible_galaxy/ansible_collections/alexbarton/boilerplate/Makefile.boilerplate
90 maintainer-clean: distclean
92 .PHONY: default all check install clean distclean maintainer-clean
97 # Create a hosts.ini template file.
100 echo "Creating \"hosts.ini\" ..."
110 # Create a requirements.yml template file.
112 Init_RequirementsYml() {
113 echo "Creating \"requirements.yml\" ..."
114 cat >requirements.yml <<EOF
116 # Ansible dependencies
119 - ${BOILERPLATE_COLLECTION_SRC:-alexbarton.boilerplate}
129 # - Initialize a Python virtual environment (when a ".venv" directory exists
130 # or the ansible-galaxy command is not found).
131 # - Install Ansible when ansible-galaxy command is not found.
132 # - Install "ansible-boilerplate" collection when not found.
133 # - Update local "ansible-boilerplate" setup: copy script, create links, ...
134 # - Upgrade template files.
135 # - Install/upgrade Python dependencies (from requirements.txt file).
136 # - Install/upgrade Ansible Galaxy dependencies (from requirements.yml file).
138 # --force: Passed to "ansible-galaxy install" command.
139 # --init: Upgrade() is called by the Init() function.
143 while [ $# -gt 0 ]; do
157 [ -z "${is_init}" ] && echo "Upgrade project:"
159 # Check Python virtual environment
160 if [ -d .venv ] || ! command -v "${ANSIBLE_GALAXY}" >/dev/null; then
161 # Either an existing ".venv" folder was found or the
162 # ansible-galaxy(1) command was not found on the system, so
163 # let's use a Python virtual environment!
164 echo "Using a Python virtual environment."
165 PIP="./.venv/bin/pip"
166 ANSIBLE_GALAXY="./.venv/bin/ansible-galaxy"
167 if ! [ -x .venv/bin/pip ]; then
168 echo "Initializing Python virtual environment ..."
169 "${PYTHON}" -m venv .venv
170 "${PIP}" install -U pip setuptools
173 for var in PYTHON PIP ANSIBLE_GALAXY; do
174 eval 'echo " - ${var} is \"$'"${var}"'\"."'
177 if [ -r requirements.txt ]; then
178 echo "Installing Python dependencies ..."
179 "${PIP}" install -U -r requirements.txt
182 # Make sure that the "ansible-galaxy" command is available now:
183 if ! [ -x "${ANSIBLE_GALAXY}" ]; then
184 echo "Oops, \"${ANSIBLE_GALAXY}\" not found!" >&2
185 echo "You either need Ansible installed locally or list it as a dependency in" >&2
186 echo "the \"requirements.txt\" file of this project!" >&2
190 if [ -r requirements.yml ]; then
191 echo "Upgrading Ansible Galaxy dependencies ..."
192 # shellcheck disable=SC2248
193 "${ANSIBLE_GALAXY}" collection install -U -r requirements.yml ${do_force}
194 # shellcheck disable=SC2248
195 "${ANSIBLE_GALAXY}" role install -r requirements.yml ${do_force}
198 # Are we running in a dependent project? If so, perform specific upgrade tasks!
199 # shellcheck disable=SC2086
200 [ -e Makefile.boilerplate ] || Upgrade_Dependent ${is_init}
204 # Upgrade steps for dependent projects only.
206 # --init: Upgrade() is called by the Init() function.
208 Upgrade_Dependent() {
209 # Verify that the Boilerplate Collection is available now.
210 # NOTE: This dependency must be properly listed in the requirements.yml
211 # file inside of the (dependent) project!
212 "${ANSIBLE_GALAXY}" collection verify --offline alexbarton.boilerplate
214 echo "Copying \"boilerplate\" script into bin/ directory ..."
216 cp -av "${BASE_D}/bin/ansible-boilerplate" "bin/ansible-boilerplate"
218 echo "Creating symbolic links to files inside of the Boilerplate Collection ..."
224 # Create (new) symbolic links, when the target already is a symbolic link or
225 # does not yet exists. Don't overwrite existing regular files etc.!
226 test -L "${file}" && ln -fsv "../${BASE_D}/${file}" "${file}"
227 test -e "${file}" || ln -fsv "../${BASE_D}/${file}" "${file}"
230 echo "Upgrading template files from the Boilerplate Collection ..."
235 .vscode/settings.json \
240 # shellcheck disable=SC2086
241 Upgrade_Template "${file}" ${is_init}
246 # Upgrade a template file.
248 # --init: Initialize a new project, therefore create the template file if it
249 # does not yet exist.
252 # Does the target directory exist? Skip this template file if not!
253 [ -d "$(dirname "$1")" ] || return 0
255 # Return when the target file does not exist and not in "init mode":
256 [ ! -e "$1" ] && [ "$2" != "--init" ] && return 0
258 # Remove the target when it is a symbolic link.
259 [ -L "$1" ] && rm -v "$1"
261 # Do not override the target when it exists already!
263 # Target already exists. Is it different?
264 if ! cmp "$1" "${BASE_D}/$1"; then
265 # Files are not the same! Install new version in parallel:
266 install -b -m 0644 -p -v "${BASE_D}/$1" "$1.new"
269 # Target does not yet exist:
270 install -b -m 0644 -p -v "${BASE_D}/$1" "$1"
275 [ $# -gt 0 ] && shift
281 "upgrade"|"update"|"up")