fix: close deployment decoupling review

This commit is contained in:
2026-08-05 07:52:32 +02:00
parent 5d037e97c4
commit 09834d5cd4
45 changed files with 1082 additions and 791 deletions
+44 -2
View File
@@ -11,8 +11,12 @@ render_profile() {
local env_file=$2
local compose_file=$3
local rendered="$tmp/$profile.json"
local -a files=(-f compose.yaml -f "$compose_file")
if [[ "$profile" == server ]]; then
files+=(-f deploy/compose.session-server.yaml.example)
fi
docker compose --env-file "$env_file" -f compose.yaml -f "$compose_file" \
docker compose --env-file "$env_file" "${files[@]}" \
config --format json >"$rendered"
node - "$rendered" "$profile" <<'NODE'
@@ -38,6 +42,28 @@ const piAuthMounts = (config.services.core.volumes || []).filter(
if (piAuthMounts.length !== 1 || piAuthMounts[0].type !== "bind" || !piAuthMounts[0].read_only) {
throw new Error("Pi auth must be one read-only file bind");
}
if (config.services.core.environment?.THT_SECRETS_FILE !== "/run/secrets/thothii.secrets") {
throw new Error("core must read the canonical runtime secret bundle from /run/secrets");
}
const runtimeSecrets = config.services.core.secrets || [];
const bundleSecrets = runtimeSecrets.filter(
(secret) => secret.source === "thothii_secrets" && secret.target === "thothii.secrets",
);
if (bundleSecrets.length !== 1) {
throw new Error("core must receive exactly one canonical runtime secret bundle");
}
if (profile === "local" && runtimeSecrets.length !== 1) {
throw new Error("local core must receive only the canonical runtime secret bundle");
}
if (profile === "server") {
const targets = new Set(runtimeSecrets.map((secret) => secret.target));
for (const target of ["session_runtime_password", "session_ca.pem"]) {
if (!targets.has(target)) throw new Error("server core lacks " + target);
}
}
if ((config.services.frontend.secrets || []).length !== 0) {
throw new Error("frontend must not receive runtime secrets");
}
const ports = Object.fromEntries(
Object.entries(config.services).map(([name, service]) => [name, service.ports || []]),
@@ -60,9 +86,12 @@ assert_remote_required() {
local env_file=$1
local compose_file=$2
local without_remote="$tmp/without-remote.env"
local -a files=(-f compose.yaml -f "$compose_file")
[[ "$compose_file" != deploy/compose.server.yaml ]] \
|| files+=(-f deploy/compose.session-server.yaml.example)
grep -v '^THT_WORKSPACE_GIT_REMOTE=' "$env_file" >"$without_remote"
if env -u THT_WORKSPACE_GIT_REMOTE docker compose --env-file "$without_remote" -f compose.yaml -f "$compose_file" \
if env -u THT_WORKSPACE_GIT_REMOTE docker compose --env-file "$without_remote" "${files[@]}" \
config --format json >"$tmp/missing-remote.out" 2>"$tmp/missing-remote.err"; then
echo "Compose must require THT_WORKSPACE_GIT_REMOTE" >&2
exit 1
@@ -72,6 +101,7 @@ assert_remote_required() {
THT_WORKSPACE_GIT_REMOTE=https://git.example.invalid/platform/thoth-workspaces.git \
PI_AUTH_FILE=/dev/null \
THT_SECRETS_FILE=/dev/null \
docker compose -f compose.yaml config --format json >"$tmp/base.json"
node - "$tmp/base.json" <<'NODE'
const fs = require("fs");
@@ -98,6 +128,18 @@ const piAuthMounts = (config.services.core.volumes || []).filter(
if (piAuthMounts.length !== 1 || piAuthMounts[0].type !== "bind" || !piAuthMounts[0].read_only) {
throw new Error("Pi auth must be one read-only file bind");
}
if (config.services.core.environment?.THT_SECRETS_FILE !== "/run/secrets/thothii.secrets") {
throw new Error("core must read the canonical runtime secret bundle from /run/secrets");
}
const runtimeSecrets = config.services.core.secrets || [];
if (runtimeSecrets.length !== 1
|| runtimeSecrets[0].source !== "thothii_secrets"
|| runtimeSecrets[0].target !== "thothii.secrets") {
throw new Error("core must receive exactly the canonical runtime secret bundle");
}
if ((config.services.frontend.secrets || []).length !== 0) {
throw new Error("frontend must not receive runtime secrets");
}
NODE
render_profile local deploy/env/local.env.example deploy/compose.local.yaml