#!/usr/bin/env bash # Active installation manuals must drive the canonical two-service base+profile stack. set -euo pipefail root="$(cd "$(dirname "$0")/.." && pwd -P)" tmp="$(mktemp -d "${TMPDIR%/}/thoth-canonical-install.XXXXXX")" trap 'rm -rf "$tmp"' EXIT HUP INT TERM for retired_example in \ "$root/docs/install/examples/local-compose.workspace-registry.yaml" \ "$root/docs/install/examples/server-compose.workspace-registry.yaml" \ "$root/docs/install/examples/git-ssh.workspace-registry.yaml" \ "$root/docs/install/examples/git-https.workspace-registry.yaml"; do if [[ -e "$retired_example" ]]; then echo "superseded one-service install example remains active: ${retired_example#"$root/"}" >&2 exit 1 fi done printf '%s\n' '{}' >"$tmp/pi-auth.json" printf '%s\n' 'THT_MODEL_API_KEY=fixture-model-api-key' >"$tmp/thothii.secrets" chmod 0600 "$tmp/pi-auth.json" "$tmp/thothii.secrets" mkdir -p "$tmp/data" "$tmp/pi-state" "$tmp/workspace-registry" "$root/scripts/prepare-server-pi-state.sh" "$tmp/pi-state" "$(id -u)" "$(id -g)" >/dev/null printf '%s\n' 'fixture-session-password' >"$tmp/session-runtime-password" printf '%s\n' 'fixture-session-migrator-password' >"$tmp/session-migrator-password" printf '%s\n' 'fixture-session-ca' >"$tmp/session-ca.pem" cp "$root/deploy/workspaces/server-sessions.yaml.example" "$tmp/server-sessions.yaml" chmod 0600 "$tmp/session-runtime-password" "$tmp/session-migrator-password" "$tmp/session-ca.pem" for profile in local server; do env_file="$tmp/$profile.env" { printf '%s\n' \ 'THT_WORKSPACE_GIT_REMOTE=https://git.example.invalid/platform/thoth-workspaces.git' \ "PI_AUTH_FILE=$tmp/pi-auth.json" \ "THT_SECRETS_FILE=$tmp/thothii.secrets" if [[ "$profile" == server ]]; then printf '%s\n' \ "THT_DATA_ROOT=$tmp/data" \ "THT_PI_STATE_ROOT=$tmp/pi-state" \ "THT_WORKSPACE_REGISTRY_ROOT=$tmp/workspace-registry" \ "THT_SERVER_WORKSPACE_CONFIG=$tmp/server-sessions.yaml" \ 'THT_SESSION_DB_HOST=sessions.example.invalid' \ 'THT_SESSION_DB_NAME=thoth_sessions' \ 'THT_SESSION_RUNTIME_USER=thoth_sessions_app' \ 'THT_SESSION_MIGRATOR_USER=thoth_sessions_migrate' \ "THT_SESSION_RUNTIME_PASSWORD_SOURCE=$tmp/session-runtime-password" \ "THT_SESSION_MIGRATOR_PASSWORD_SOURCE=$tmp/session-migrator-password" \ "THT_SESSION_CA_SOURCE=$tmp/session-ca.pem" fi } >"$env_file" compose_files=(-f "$root/compose.yaml" -f "$root/deploy/compose.$profile.yaml") if [[ "$profile" == server ]]; then compose_files+=(-f "$root/deploy/compose.session-server.yaml.example") fi docker compose --env-file "$env_file" "${compose_files[@]}" \ config --format json >"$tmp/$profile.json" node - "$tmp/$profile.json" "$profile" <<'NODE' const fs = require("fs"); const [path, profile] = process.argv.slice(2); const config = JSON.parse(fs.readFileSync(path, "utf8")); if (Object.keys(config.services).sort().join(",") !== "core,frontend") { throw new Error(profile + ": install stack must be exactly core,frontend"); } if (!config.services.core.secrets?.some((secret) => secret.target === "thothii.secrets")) { throw new Error(profile + ": install stack lacks the runtime secret bundle"); } if (!config.services.core.volumes?.some( (mount) => mount.target === "/home/thoth/.pi/agent/auth.json" && mount.read_only, )) { throw new Error(profile + ": install stack lacks the read-only Pi auth file"); } if ((config.services.frontend.secrets || []).length !== 0) { throw new Error(profile + ": frontend received runtime secrets"); } if (profile === "server" && config.services.core.environment?.THT_SESSION_STORAGE !== "postgres") { throw new Error("server: public startup must include the PostgreSQL session override"); } if (JSON.stringify(config).includes("fixture-model-api-key")) { throw new Error(profile + ": rendered Compose leaked a secret value"); } NODE done for profile in local server; do manual="$root/docs/install/$profile-workspace-registry.md" grep -Fq -- '--env-file "$THT_OPERATOR_ENV"' "$manual" \ && grep -Fq -- "-f \"\$THT_SOURCE_ROOT/compose.yaml\" -f \"\$THT_SOURCE_ROOT/deploy/compose.$profile.yaml\"" "$manual" || { echo "$profile manual lacks the canonical base+profile command" >&2 exit 1 } if rg -q 'local-compose\.workspace-registry|server-compose\.workspace-registry' "$manual"; then echo "$profile manual still references a superseded standalone Compose example" >&2 exit 1 fi done echo "canonical install Compose contract passed."