I ended up writing some shell wrapper to iterate over the galaxy requirements and storing the content locally first. Once done, any subsequent run will use the cached content. This saves time/resources, and also avoids dependencies on galaxy.ansible.com (which may not always be online). Note that this does not do dependency resolution, but that is OK in our case as we have listed everything statically in the ansible_galaxy.yml file.
Boilerplate:
#!/usr/bin/env bash
# Check for ansible_galaxy.yml file
GALAXY_REQS="ansible_galaxy.yml"
usage() {
local script_name
script_name=$(basename "$0")
cat <<EOF
Usage: ${script_name} [-h|--help]
Cache and install Ansible Galaxy collections defined in ${GALAXY_REQS}.
Options:
-h, --help Display this help message and exit
EOF
}
for arg in "$@"; do
case "$arg" in
-h|--help)
usage
exit 0
;;
esac
done
# Check for required tools
for cmd in yq jq ansible-galaxy python3; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "Error: Required command '${cmd}' not found in PATH." >&2
exit 1
fi
done
if [ ! -r "${GALAXY_REQS}" ]; then
echo "No readable ansible_galaxy.yml file found, but we do need one.
Exiting"
exit 1
fi
# Set commands
PYTHON="python3"
GALAXY="ansible-galaxy"
# Galaxy part
function install_galaxy_requirements() {
CACHE_DIR="${HOME}/.cache/galaxy"
if [ ! -d "$CACHE_DIR" ]; then
mkdir -p "$CACHE_DIR"
fi
# Start with empty data structure, to build a local requirements file
TEMP_REQS="$(mktemp).yml"
echo -e "---\ncollections: []" > "${TEMP_REQS}"
# Iterate over requirements
yq -rc '.collections[]' "${GALAXY_REQS}" | while read -r item; do
NAME=$(jq -r '.name' <<< "$item")
SOURCE=$(jq -r '.source // "https://galaxy.ansible.com"' <<< "$item")
VERSION=$(jq -r '.version' <<< "$item")
CACHE_FILE="${CACHE_DIR}/${NAME/./-}-${VERSION}.tar.gz"
if [ ! -f "${CACHE_FILE}" ]; then
# Download collection
"$GALAXY" collection download \
--server "${SOURCE}" \
--download-path "${CACHE_DIR}" \
"${NAME}:${VERSION}"
else
echo "Collection $NAME is available in cache, not downloading it"
fi
# Append item to collection list in the cached requirements file.
# For the "name" we use the absolute path to the tarball
yq --yaml-output --in-place \
--arg name "$(realpath "${CACHE_DIR}")/${NAME/./-}-${VERSION}.tar.gz" \
--arg version "${VERSION}" \
'.collections += [{name: $name, version: $version}]' \
"${TEMP_REQS}"
done
# Now install from the cached requirements file
ANSIBLE_COLLECTIONS_PATH=$("${PYTHON}" -c "import site; print(site.getsitepackages()[0])") \
"$GALAXY" collection install -r "${TEMP_REQS}"
# Remove temp requirements file
rm -f "${TEMP_REQS}"
}
# Install the collections
install_galaxy_requirements