#!/usr/bin/env bash # Validate operator-managed Compose inputs before delegating to Docker Compose. set -euo pipefail usage() { echo "usage: $0 --env-file -f ... " >&2 exit 2 } trim() { local value="$1" value="${value#"${value%%[![:space:]]*}"}" value="${value%"${value##*[![:space:]]}"}" printf '%s' "$value" } is_safe_absolute_path() { local value="$1" segment local -a segments [[ "$value" == /* && "$value" != *//* ]] || return 1 IFS=/ read -r -a segments <<<"$value" for segment in "${segments[@]}"; do [[ "$segment" != . && "$segment" != .. ]] || return 1 done } read_env_value() { local source="$1" wanted="$2" line trimmed name value result="" while IFS= read -r line || [[ -n "$line" ]]; do trimmed="$(trim "$line")" [[ -n "$trimmed" && "$trimmed" != \#* && "$trimmed" == *=* ]] || continue name="$(trim "${trimmed%%=*}")" [[ "$name" == "$wanted" ]] || continue value="$(trim "${trimmed#*=}")" value="$(trim "${value%%#*}")" value="${value#\"}"; value="${value%\"}" value="${value#\'}"; value="${value%\'}" result="$value" done <"$source" printf '%s' "$result" } source_names() { local source="$1" line trimmed name { while IFS= read -r line || [[ -n "$line" ]]; do trimmed="$(trim "$line")" [[ -n "$trimmed" && "$trimmed" != \#* && "$trimmed" == *=* ]] || continue name="$(trim "${trimmed%%=*}")" [[ "$name" =~ ^[A-Za-z_][A-Za-z0-9_]*_SOURCE$ ]] && printf '%s\n' "$name" done <"$source" while IFS= read -r line; do name="${line%%=*}" [[ "$name" =~ ^[A-Za-z_][A-Za-z0-9_]*_SOURCE$ ]] && printf '%s\n' "$name" done < <(env) } | sort -u } record_git_transport() { local compose_file="$1" transport="" [[ -f "$compose_file" ]] || return 0 transport="$(awk ' /^[[:space:]]*x-thoth-git-transport:[[:space:]]*/ { value = $0 sub(/^[[:space:]]*x-thoth-git-transport:[[:space:]]*/, "", value) sub(/[[:space:]]*#.*/, "", value) print value exit } ' "$compose_file")" case "$transport" in ssh) ssh_override=1 ;; https) https_override=1 ;; "") case "$(basename "$compose_file")" in *git-ssh*.yaml|*git-ssh*.yml) ssh_override=1 ;; *git-https*.yaml|*git-https*.yml) https_override=1 ;; esac ;; *) echo "invalid x-thoth-git-transport in $compose_file" >&2; exit 2 ;; esac } env_file="" ssh_override=0 https_override=0 arguments=("$@") for ((index = 0; index < ${#arguments[@]}; index += 1)); do argument="${arguments[index]}" case "$argument" in --env-file) ((index + 1 < ${#arguments[@]})) || usage ((index += 1)) env_file="${arguments[index]}" ;; --env-file=*) env_file="${argument#--env-file=}" ;; -f|--file) ((index + 1 < ${#arguments[@]})) || usage ((index += 1)) compose_file="${arguments[index]}" record_git_transport "$compose_file" ;; --file=*) compose_file="${argument#--file=}" record_git_transport "$compose_file" ;; esac done [[ -n "$env_file" && -f "$env_file" ]] || { echo "Compose preflight requires an existing --env-file" >&2; exit 2; } if ((ssh_override && https_override)); then echo "SSH and HTTPS Git overrides are mutually exclusive" >&2 exit 2 fi while IFS= read -r name; do value="$(read_env_value "$env_file" "$name")" if [[ -v "$name" ]]; then value="${!name}" fi if [[ -n "$value" ]] && ! is_safe_absolute_path "$value"; then echo "unsafe source path for $name in $env_file" >&2 exit 2 fi done < <(source_names "$env_file") exec docker compose "${arguments[@]}"