#!/usr/bin/env bash # Local regressions only: kubectl is mocked and Docker is used for config parsing. set -euo pipefail repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" scratch="$(mktemp -d)" trap 'rm -rf "$scratch"' EXIT mkdir -p "$scratch/repo/app" "$scratch/repo/postgres" "$scratch/repo/netbird" "$scratch/repo/renovate" git -C "$scratch/repo" init -q for file in app/compose.yaml postgres/shared-compose.yaml netbird/client.compose.yaml renovate/renovate-compose.yaml; do touch "$scratch/repo/$file" done git -C "$scratch/repo" add . # shellcheck source=../workflows/compose-lint.sh source "$repo/.gitea/workflows/compose-lint.sh" actual="$(cd "$scratch/repo" && compose_files)" expected=$'app/compose.yaml\nnetbird/client.compose.yaml\npostgres/shared-compose.yaml\nrenovate/renovate-compose.yaml' [ "$actual" = "$expected" ] || { echo 'Compose discovery missed a file' >&2; exit 1; } cat >"$scratch/compose.yaml" <<'YAML' services: example: image: busybox:1.37.0 environment: REQUIRED: ${HOMELAB_TEST_REQUIRED:?required for this regression} YAML unset HOMELAB_TEST_REQUIRED if validate_compose_file "$scratch/compose.yaml" >"$scratch/config.log" 2>&1; then echo 'Full Compose validation accepted a missing variable' >&2 exit 1 fi grep -q 'required for this regression' "$scratch/config.log" HOMELAB_TEST_REQUIRED=present validate_compose_file "$scratch/compose.yaml" cat >"$scratch/resources.json" <<'JSON' {"kind":"List","items":[ {"kind":"Deployment","metadata":{"namespace":"app"},"spec":{"template":{"spec":{ "containers":[{"envFrom":[{"secretRef":{"name":"credentials"}},{"secretRef":{"name":"optional","optional":true}}],"env":[{"valueFrom":{"secretKeyRef":{"name":"credentials","key":"password"}}}]}], "initContainers":[{"envFrom":[{"secretRef":{"name":"init"}}]}], "imagePullSecrets":[{"name":"registry"}], "volumes":[{"secret":{"secretName":"mounted"}},{"projected":{"sources":[{"secret":{"name":"projected"}},{"secret":{"name":"optional-projected","optional":true}}]}}] }}}}, {"kind":"CronJob","metadata":{},"spec":{"jobTemplate":{"spec":{"template":{"spec":{"containers":[{"envFrom":[{"secretRef":{"name":"cron"}}]}]}}}}}}, {"kind":"IngressRoute","metadata":{"namespace":"app"},"spec":{"tls":{"secretName":"controller-issued-tls"}}} ]} JSON actual="$(jq -r -f "$repo/.gitea/workflows/secret-references.jq" "$scratch/resources.json" | sort)" expected=$'app credentials\napp init\napp mounted\napp projected\napp registry\ndefault cron' [ "$actual" = "$expected" ] || { echo "Unexpected Secret references: $actual" >&2; exit 1; } REPO="$repo" # shellcheck source=../workflows/deploy-lib.sh source "$repo/.gitea/workflows/deploy-lib.sh" K8S_MANIFESTS=("$scratch/resources.json") KUSTOMIZE_APPS=() # No live cluster access. Reject credentials in app even if they exist elsewhere. kubectl() { case "$1" in create) cat "$scratch/resources.json" ;; get) if [ "$3" = credentials ] && [ "$5" = app ]; then return 1 fi return 0 ;; *) echo "Unexpected kubectl invocation: $*" >&2; return 1 ;; esac } if check_referenced_secrets >"$scratch/secrets.log"; then echo 'Namespace-scoped Secret check accepted a missing Secret' >&2 exit 1 fi grep -q 'MISSING OR UNREADABLE: app/credentials' "$scratch/secrets.log" # API/rendering errors must not produce an empty reference list and pass. kubectl() { return 1; } if check_referenced_secrets >"$scratch/secrets.log"; then echo 'Secret check accepted a failed manifest render' >&2 exit 1 fi printf '%s\n' 'Deploy validation regressions passed.'