diff --git a/stack b/stack new file mode 100644 index 0000000..3081ff0 --- /dev/null +++ b/stack @@ -0,0 +1,227 @@ +#!/usr/bin/env bash +set -uo pipefail + +CONFIG_FILE="${STACK_CONF:-$HOME/stack.conf}" + +COMPOSE_CANDIDATES=( + "compose.yml" + "compose.yaml" + "docker-compose.yml" + "docker-compose.yaml" +) + +usage() { + cat <<'EOF' +Usage: + stack start + stack add . + stack add foldername + stack add folder1 folder2 folder3 +EOF +} + +strip_quotes() { + local s="$1" + s="${s#\"}" + s="${s%\"}" + s="${s#\'}" + s="${s%\'}" + printf '%s' "$s" +} + +resolve_path() { + local input="$1" + if command -v realpath >/dev/null 2>&1; then + realpath -m "$input" + else + python3 - "$input" <<'PY' +import os, sys +print(os.path.abspath(os.path.expanduser(sys.argv[1]))) +PY + fi +} + +ensure_config_exists() { + if [[ ! -f "$CONFIG_FILE" ]]; then + mkdir -p "$(dirname "$CONFIG_FILE")" + cat > "$CONFIG_FILE" </dev/null 2>&1; then + printf 'docker compose' + elif command -v docker-compose >/dev/null 2>&1; then + printf 'docker-compose' + else + echo "error: neither 'docker compose' nor 'docker-compose' is installed." >&2 + exit 1 + fi +} + +add_folder() { + ensure_config_exists + load_config + + if [[ $# -eq 0 ]]; then + echo "usage: stack add .|foldername|/path/to/folder [more folders...]" >&2 + exit 1 + fi + + local added=0 + local arg target + for arg in "$@"; do + if [[ "$arg" == "." ]]; then + target="$PWD" + elif [[ "$arg" == /* ]]; then + target="$arg" + else + target="$PWD/$arg" + fi + + target="$(resolve_path "$target")" + + if [[ ! -d "$target" ]]; then + echo "error: folder does not exist: $target" >&2 + continue + fi + + if grep -Fxq -- "$target" "$CONFIG_FILE"; then + echo "already in stack.conf: $target" + continue + fi || true + + printf '%s\n' "$target" >> "$CONFIG_FILE" + echo "added: $target" + added=1 + done + + [[ "$added" -eq 0 ]] && exit 1 +} + +start_stacks() { + ensure_config_exists + load_config + + local cc + cc="$(compose_cmd)" + + local any=0 dir file found + for dir in "${STACK_DIRS[@]}"; do + [[ -z "$dir" ]] && continue + + if [[ ! -d "$dir" ]]; then + echo "skipping missing folder: $dir" >&2 + continue + fi + + found=0 + for file in "${COMPOSE_CANDIDATES[@]}"; do + if [[ -f "$dir/$file" ]]; then + found=1 + any=1 + echo "starting: $dir/$file" + (cd "$dir" && $cc -f "$file" up -d) + fi + done + + if [[ "$found" -eq 0 ]]; then + echo "no compose file found in: $dir" >&2 + fi + done + + if [[ "$any" -eq 0 ]]; then + echo "no compose files were started." + fi +} + +update_stacks() { + ensure_config_exists + load_config + + local cc + cc="$(compose_cmd)" + + local any=0 dir file found + for dir in "${STACK_DIRS[@]}"; do + [[ -z "$dir" ]] && continue + + if [[ ! -d "$dir" ]]; then + echo "skipping missing folder: $dir" >&2 + continue + fi + + found=0 + for file in "${COMPOSE_CANDIDATES[@]}"; do + if [[ -f "$dir/$file" ]]; then + found=1 + any=1 + echo "updating: $dir/$file" + + ( + cd "$dir" && \ + $cc -f "$file" pull && \ + $cc -f "$file" up -d + ) + fi + done + + if [[ "$found" -eq 0 ]]; then + echo "no compose file in: $dir" >&2 + fi + done + + if [[ "$any" -eq 0 ]]; then + echo "no compose files updated." + fi +} + +main() { + local cmd="${1:-}" + shift || true + + case "$cmd" in + start) + start_stacks + ;; + update) + update_stacks + ;; + add) + add_folder "$@" + ;; + ""|-h|--help|help) + usage + ;; + *) + echo "unknown command: $cmd" >&2 + usage + exit 1 + ;; + esac +} + +main "$@" \ No newline at end of file