Skip to content

Instantly share code, notes, and snippets.

@AndreSteenveld
Last active March 21, 2024 05:49
Show Gist options
  • Save AndreSteenveld/c8e6409b4dd79f1b7abd479c3c0065be to your computer and use it in GitHub Desktop.
Save AndreSteenveld/c8e6409b4dd79f1b7abd479c3c0065be to your computer and use it in GitHub Desktop.
Helper script to gather docker-compose files
#! /usr/bin/env bash
cf__help(){
printf '%s\n' \
'compose-files - Helper script to gather docker-compose files ' \
' ' \
'Usage: ' \
' compose-files [options --] [presets] ' \
' ' \
'Options: ' \
' -?, -h, --help Show this help message ' \
' -v, --verbose List found docker-compose files to stderr ' \
' -l, --list List found docker-compose files to stdout (this omits the ' \
' `--file` prefix) ' \
' -p, --path <p> Root paths from which to look for docker-compose files, ' \
' multiple roots can be provided. These are searched in order. ' \
' If no paths are provided this defaults to `$PWD`. ' \
' ' \
'Presets: ' \
' Presets can be the one of two things; An infix for a docker compose file or a ' \
' path to a file. When preset is supplied the script checks if it is a file ' \
' first. For example `Makefile` would be a poor infix, most likley the script ' \
' will pass your `Makefile`-file to docker compose which will then throw an ' \
' error. If the supplied preset is not a file it will look for all files in all ' \
' supplied paths that look like `docker-compose.<infix>.{yml,yaml,json}. ' \
' ' \
' If options are supplied everything after `--` is treated as a preset. If no ' \
' options are provided (or none can be parsed) all argumnets will be treated as ' \
' presets. `--he1p` is bad taste, but otherwise a perfectly valid infix. ' \
' ' \
'Example: ' \
' compose-files ' \
' compose-files first-preset second-preset ./docker-compose.other.yaml ' \
' compose-files --list --path . --path ../first --path ../second -- preset ' \
' docker compose --project-name example $(compose-files development) up ' \
' '
exit 0
}
cf__gather(){
#
# So we're passing basically two arrays to this function which we'll need to parse out. The strategy to do this has
# been taken from/inspired by the following SO posts:
#
# https://stackoverflow.com/a/70855715/95019 -- Passing arrays as seperate values
# https://stackoverflow.com/a/71060036/95019 -- Passing arrays _by reference_, requires bash v4
#
# We're going with the _old_ way of doing this, the reasoning here is that is has to work out of the box on OSX
# which comes with bash v3 by default.
#
local arguments=("$@")
local number_of_paths=${arguments[0]}
local number_of_presets=${arguments[$((number_of_paths + 1))]}
local paths=()
local presets=()
#
# So this array parsing could be done in a single loop, but that seems more confusing than just using
# all the offsets. I'm sure there is a way to make this work with slices but can't really be botherd.
#
for (( i = 1 ; i <= number_of_paths ; i++ )) ; do
paths+=("${arguments[$i]}")
done
for (( i = ( number_of_paths + 1 + 1) ; i <= ( number_of_paths + number_of_presets + 1); i++ )) ; do
presets+=("${arguments[$i]}")
done
local compose_files=();
#
# Compose the list of compose files, first we're going to look for all regular compose files and load these
# regardless. Then go through the presets and add these to the list as well.
#
# As it turns out `globstar` is a feature introduced in bash v4, making it unusable for OSX. Luckikly we can
# fall back on good old `find`.
# shopt -s globstar nullglob extglob
for path in "${paths[@]}"; do
#for compose_file in "${path}"/**/docker-compose.{yml,yaml,json}; do
for compose_file in $(find "${path}" -name 'docker-compose.yml' -or -name 'docker-compose.yaml' -or -name 'docker-compose.json') ; do
compose_files+=( "$(realpath "${compose_file}")" )
done
done
for path in "${paths[@]}"; do
for preset in "${presets[@]}"; do
if [[ -f "${preset}" ]]; then
compose_files+=( "$(realpath "${preset}")" )
continue
fi
#for compose_file in "${path}"/**/docker-compose."${preset}".{yml,yaml,json}; do
for compose_file in $(find "${path}" -name "docker-compose.${preset}.yml" -or -name "docker-compose.${preset}.yaml" -or -name "docker-compose.${preset}.json") ; do
compose_files+=( "$(realpath "${compose_file}")" )
done
done
done
printf '%s\n' "${compose_files[@]}"
}
cf__main(){
local verbose=0;
local list=0
local paths=();
local presets=();
while (( $# )) ; do
case $1 in
"-h" | "-?" | --help ) cf__help ;;
"-v" | --verbose ) verbose=1 ; shift ; continue ;;
"-l" | --list ) list=1 ; shift ; continue ;;
"-p" | --path ) paths+=( "$2" ) ; shift 2 ; continue ;;
-- ) shift ;;
esac
presets=( "$@" )
break
done
if [[ "${#paths[@]}" == 0 ]]; then
paths+=( "$PWD" )
fi
cf__gather "${#paths[@]}" "${paths[@]}" "${#presets[@]}" "${presets[@]}" | (
if [[ "$verbose" == 0 ]] && [[ "$list" == 0 ]]; then
sed -e 's/^/ --file /' | tr -d '\n'
elif [[ "$verbose" == 1 ]] && [[ "$list" == 0 ]]; then
tee >(sed 's/^/[compose-files] /' >&2) | sed -e 's/^/ --file /' | tr -d '\n'
elif [[ "$verbose" == 1 ]] && [[ "$list" == 1 ]]; then
tee >(sed 's/^/[compose-files] /' >&2)
elif [[ "$verbose" == 0 ]] && [[ "$list" == 1 ]]; then
cat
fi
)
}
(return 0 2> /dev/null) || cf__main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment