fix(deploy): close container final review

This commit is contained in:
2026-07-12 00:44:39 +02:00
parent 0ca6346f75
commit a3a266fd81
30 changed files with 526 additions and 37 deletions
+14
View File
@@ -36,3 +36,17 @@ The small input file pins the harness's PEP 517 build backend as well; it is not
host environment. The image installs the resulting lock with pip's `--require-hashes`, then
installs the local `tht` project with `--no-deps --no-build-isolation`. This prevents both project
metadata and an isolated build environment from resolving unpinned packages.
## Base images
Every `FROM` uses an exact tag plus a multi-platform manifest-list digest. To update one:
1. Choose an exact patch tag that publishes both `linux/amd64` and `linux/arm64`.
2. Inspect it with `docker buildx imagetools inspect <tag>`.
3. Replace both the human-readable tag and `@sha256:...` digest.
4. Run `./scripts/verify-container-images.sh` and the Compose smoke.
5. Review the generated inventory under `.artifacts/container-images/`.
The digest freezes image layers, but `apt-get update` and `apk add` still consume mutable package
repositories during a no-cache rebuild. Full OS-package immutability would require Debian/Alpine
snapshot repositories and is not claimed by this deployment.
+18
View File
@@ -1,6 +1,24 @@
#!/bin/sh
set -eu
load_secret() {
value_name=$1
file_name=$2
secret_file=$(printenv "$file_name" 2>/dev/null || true)
if [ -n "$secret_file" ]; then
if [ ! -r "$secret_file" ]; then
echo "$file_name is not readable: $secret_file" >&2
exit 2
fi
secret_value=$(cat "$secret_file")
export "$value_name=$secret_value"
fi
}
load_secret THT_DWH_API_KEY THT_DWH_API_KEY_FILE
load_secret THT_VEC_API_KEY THT_VEC_API_KEY_FILE
load_secret THT_VEC_WRITE_API_KEY THT_VEC_WRITE_API_KEY_FILE
case "${1:-server}" in
server)
shift || true
+4 -4
View File
@@ -1,11 +1,11 @@
# syntax=docker/dockerfile:1
FROM node:22.19.0-bookworm-slim AS node-runtime
FROM node:22.19.0-bookworm-slim@sha256:4a4884e8a44826194dff92ba316264f392056cbe243dcc9fd3551e71cea02b90 AS node-runtime
WORKDIR /opt/pi-runtime
COPY docker/pi-runtime/package.json docker/pi-runtime/package-lock.json ./
RUN npm ci --omit=dev --ignore-scripts --no-audit --no-fund \
&& ./node_modules/.bin/pi --version
FROM node:22.19.0-bookworm-slim AS backend-build
FROM node:22.19.0-bookworm-slim@sha256:4a4884e8a44826194dff92ba316264f392056cbe243dcc9fd3551e71cea02b90 AS backend-build
WORKDIR /src/backend
COPY backend/package.json backend/package-lock.json ./
RUN npm ci --no-audit --no-fund
@@ -13,12 +13,12 @@ COPY backend/ ./
RUN npm run build \
&& npm prune --omit=dev
FROM node:22.19.0-bookworm-slim AS gate-deps
FROM node:22.19.0-bookworm-slim@sha256:4a4884e8a44826194dff92ba316264f392056cbe243dcc9fd3551e71cea02b90 AS gate-deps
WORKDIR /src/harness
COPY harness/package.json harness/package-lock.json ./
RUN npm ci --no-audit --no-fund
FROM python:3.12-slim-bookworm AS runtime
FROM python:3.12.11-slim-bookworm@sha256:519591d6871b7bc437060736b9f7456b8731f1499a57e22e6c285135ae657bf7 AS runtime
RUN apt-get update \
&& apt-get install --yes --no-install-recommends ca-certificates curl \
+14 -1
View File
@@ -1,7 +1,20 @@
#!/bin/sh
set -eu
backend_base_url=${BACKEND_BASE_URL:-/api}
backend_base_url=${BACKEND_BASE_URL-/api}
case "$backend_base_url" in
""|/|/api|/api/) ;;
http://*|https://*)
if printf '%s' "$backend_base_url" | grep -Eq '[[:space:]]|^https?://[^/]*@'; then
echo "Invalid BACKEND_BASE_URL: credentials and whitespace are not allowed" >&2
exit 2
fi
;;
*)
echo "Invalid BACKEND_BASE_URL: use empty/root, /api, or an absolute http(s) URL" >&2
exit 2
;;
esac
runtime_config=$(jq -cn --arg backend_base_url "$backend_base_url" \
'{backendBaseUrl: $backend_base_url}')
printf 'window.__THOTHII_CONFIG__ = %s;\n' "$runtime_config" \
+2 -2
View File
@@ -1,12 +1,12 @@
# syntax=docker/dockerfile:1
FROM node:22.19.0-bookworm-slim AS build
FROM node:22.19.0-bookworm-slim@sha256:4a4884e8a44826194dff92ba316264f392056cbe243dcc9fd3551e71cea02b90 AS build
WORKDIR /src/frontend
COPY frontend/package.json frontend/package-lock.json ./
RUN npm ci --no-audit --no-fund
COPY frontend/ ./
RUN npm run build
FROM nginxinc/nginx-unprivileged:1.27-alpine
FROM nginxinc/nginx-unprivileged:1.27.5-alpine@sha256:65e3e85dbaed8ba248841d9d58a899b6197106c23cb0ff1a132b7bfe0547e4c0
USER root
RUN apk add --no-cache jq
COPY --from=build /src/frontend/dist /usr/share/nginx/html
+10
View File
@@ -8,6 +8,13 @@ server {
try_files $uri =404;
}
location = /health {
proxy_pass http://core:8787/health;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_cache off;
}
location /api/ {
proxy_pass http://core:8787/;
proxy_http_version 1.1;
@@ -15,6 +22,9 @@ server {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Trusted only when AUTH_MODE=upstream and this frontend port is reachable solely
# from the authenticated host proxy documented in deploy/.
proxy_set_header X-Authenticated-User $http_x_authenticated_user;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 1h;
+1 -1
View File
@@ -7,7 +7,7 @@ assignment=$(sed \
/usr/share/nginx/html/config.js)
printf '%s\n' "$assignment" \
| jq -e --arg expected "${BACKEND_BASE_URL:-/api}" \
| jq -e --arg expected "${BACKEND_BASE_URL-/api}" \
'type == "object" and keys == ["backendBaseUrl"] and .backendBaseUrl == $expected' \
>/dev/null