build: harden image build inputs
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
**/.env.*
|
**/.env.*
|
||||||
!.env.example
|
!.env.example
|
||||||
!deploy/env/*.env.example
|
!deploy/env/*.env.example
|
||||||
|
deploy/thothii.env
|
||||||
deploy/secrets/
|
deploy/secrets/
|
||||||
harness/workspaces/psd.yaml
|
harness/workspaces/psd.yaml
|
||||||
**/*.log
|
**/*.log
|
||||||
|
|||||||
@@ -4,8 +4,11 @@
|
|||||||
ARG PI_VERSION=0.80.3
|
ARG PI_VERSION=0.80.3
|
||||||
ARG IMAGE_VERSION=local
|
ARG IMAGE_VERSION=local
|
||||||
|
|
||||||
|
# ---- Pinned Node source for the runtime binary and npm ----
|
||||||
|
FROM node:22-bookworm@sha256:7725a5c2c83eed1d36258c66efae14b1ceccd021db9ed1d9559d3335ed3d68ed AS node-runtime
|
||||||
|
|
||||||
# ---- Stage 0: locked Pi runtime ----
|
# ---- Stage 0: locked Pi runtime ----
|
||||||
FROM node:22-bookworm AS pi-runtime-build
|
FROM node:22-bookworm@sha256:7725a5c2c83eed1d36258c66efae14b1ceccd021db9ed1d9559d3335ed3d68ed AS pi-runtime-build
|
||||||
ARG PI_VERSION
|
ARG PI_VERSION
|
||||||
WORKDIR /opt/pi-runtime
|
WORKDIR /opt/pi-runtime
|
||||||
COPY docker/pi-runtime/package.json docker/pi-runtime/package-lock.json ./
|
COPY docker/pi-runtime/package.json docker/pi-runtime/package-lock.json ./
|
||||||
@@ -13,7 +16,7 @@ RUN npm ci --omit=dev \
|
|||||||
&& test "$(./node_modules/.bin/pi --version)" = "$PI_VERSION"
|
&& test "$(./node_modules/.bin/pi --version)" = "$PI_VERSION"
|
||||||
|
|
||||||
# ---- Stage 1: backend TypeScript -> dist ----
|
# ---- Stage 1: backend TypeScript -> dist ----
|
||||||
FROM node:22-bookworm AS backend-build
|
FROM node:22-bookworm@sha256:7725a5c2c83eed1d36258c66efae14b1ceccd021db9ed1d9559d3335ed3d68ed AS backend-build
|
||||||
WORKDIR /src/backend
|
WORKDIR /src/backend
|
||||||
COPY backend/package*.json ./
|
COPY backend/package*.json ./
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
@@ -21,7 +24,7 @@ COPY backend/ ./
|
|||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# ---- Stage 2: runtime (Python 3.12 nativo + Node 22 copiato, stesso glibc bookworm) ----
|
# ---- Stage 2: runtime (Python 3.12 nativo + Node 22 copiato, stesso glibc bookworm) ----
|
||||||
FROM python:3.12-slim-bookworm AS runtime
|
FROM python:3.12-slim-bookworm@sha256:d50fb7611f86d04a3b0471b46d7557818d88983fc3136726336b2a4c657aa30b AS runtime
|
||||||
ARG PI_VERSION
|
ARG PI_VERSION
|
||||||
ARG IMAGE_VERSION
|
ARG IMAGE_VERSION
|
||||||
LABEL org.opencontainers.image.title="thothii-core" \
|
LABEL org.opencontainers.image.title="thothii-core" \
|
||||||
@@ -36,8 +39,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
&& ln -s /usr/bin/fdfind /usr/local/bin/fd
|
&& ln -s /usr/bin/fdfind /usr/local/bin/fd
|
||||||
|
|
||||||
# Node 22 + npm copiati dall'immagine ufficiale (stesso Debian bookworm → binario compatibile)
|
# Node 22 + npm copiati dall'immagine ufficiale (stesso Debian bookworm → binario compatibile)
|
||||||
COPY --from=node:22-bookworm /usr/local/bin/node /usr/local/bin/node
|
COPY --from=node-runtime /usr/local/bin/node /usr/local/bin/node
|
||||||
COPY --from=node:22-bookworm /usr/local/lib/node_modules /usr/local/lib/node_modules
|
COPY --from=node-runtime /usr/local/lib/node_modules /usr/local/lib/node_modules
|
||||||
RUN ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
|
RUN ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
|
||||||
&& ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx
|
&& ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# syntax=docker/dockerfile:1.7
|
# syntax=docker/dockerfile:1.7
|
||||||
# thothii-frontend: build Vite (React) + nginx-unprivileged (porta 8080).
|
# thothii-frontend: build Vite (React) + nginx-unprivileged (porta 8080).
|
||||||
ARG IMAGE_VERSION=local
|
ARG IMAGE_VERSION=local
|
||||||
FROM node:22-bookworm AS build
|
FROM node:22-bookworm@sha256:7725a5c2c83eed1d36258c66efae14b1ceccd021db9ed1d9559d3335ed3d68ed AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY frontend/package*.json ./
|
COPY frontend/package*.json ./
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
@@ -9,7 +9,7 @@ COPY frontend/ ./
|
|||||||
ENV VITE_BASE=/ VITE_BACKEND_URL=/api
|
ENV VITE_BASE=/ VITE_BACKEND_URL=/api
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM nginxinc/nginx-unprivileged:1.27-alpine AS runtime
|
FROM nginxinc/nginx-unprivileged:1.27-alpine@sha256:65e3e85dbaed8ba248841d9d58a899b6197106c23cb0ff1a132b7bfe0547e4c0 AS runtime
|
||||||
ARG IMAGE_VERSION
|
ARG IMAGE_VERSION
|
||||||
LABEL org.opencontainers.image.title="thothii-frontend" \
|
LABEL org.opencontainers.image.title="thothii-frontend" \
|
||||||
org.opencontainers.image.version="${IMAGE_VERSION}" \
|
org.opencontainers.image.version="${IMAGE_VERSION}" \
|
||||||
@@ -18,6 +18,7 @@ COPY --from=build /src/dist /usr/share/nginx/html
|
|||||||
COPY docker/nginx.conf.template /etc/nginx/templates/default.conf.template
|
COPY docker/nginx.conf.template /etc/nginx/templates/default.conf.template
|
||||||
COPY --chmod=755 docker/frontend-entrypoint.sh /usr/local/bin/frontend-entrypoint
|
COPY --chmod=755 docker/frontend-entrypoint.sh /usr/local/bin/frontend-entrypoint
|
||||||
COPY --chmod=755 docker/validate-frontend-api-upstream.sh /usr/local/bin/validate-frontend-api-upstream
|
COPY --chmod=755 docker/validate-frontend-api-upstream.sh /usr/local/bin/validate-frontend-api-upstream
|
||||||
|
COPY --chmod=755 docker/smoke/frontend-smoke.sh /usr/local/bin/frontend-config-smoke
|
||||||
ENTRYPOINT ["/usr/local/bin/frontend-entrypoint"]
|
ENTRYPOINT ["/usr/local/bin/frontend-entrypoint"]
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
assignment=$(sed \
|
test "$(cat /usr/share/nginx/html/config.js)" = 'window.__THOTHII_CONFIG__ = {};'
|
||||||
-e 's/^window\.__THOTHII_CONFIG__ = //' \
|
|
||||||
-e 's/;$//' \
|
|
||||||
/usr/share/nginx/html/config.js)
|
|
||||||
|
|
||||||
printf '%s\n' "$assignment" \
|
|
||||||
| jq -e --arg expected "${BACKEND_BASE_URL-/api}" \
|
|
||||||
'type == "object" and keys == ["backendBaseUrl"] and .backendBaseUrl == $expected' \
|
|
||||||
>/dev/null
|
|
||||||
|
|
||||||
printf '%s\n' "frontend runtime config smoke: ok"
|
printf '%s\n' "frontend runtime config smoke: ok"
|
||||||
|
|||||||
@@ -18,6 +18,36 @@ export THT_WORKSPACE_GIT_REMOTE="https://git.example.invalid/thothii/workspaces.
|
|||||||
export THOTH_CORE_HTTP_PORT=0
|
export THOTH_CORE_HTTP_PORT=0
|
||||||
export THOTH_HTTP_PORT=0
|
export THOTH_HTTP_PORT=0
|
||||||
|
|
||||||
|
context_check="$tmp/build-context"
|
||||||
|
mkdir -p "$context_check/deploy"
|
||||||
|
cp .dockerignore "$context_check/.dockerignore"
|
||||||
|
printf '%s\n' 'task-5-context-sentinel' >"$context_check/deploy/thothii.env"
|
||||||
|
cat >"$context_check/Dockerfile" <<'EOF'
|
||||||
|
FROM scratch
|
||||||
|
COPY deploy/thothii.env /sentinel
|
||||||
|
EOF
|
||||||
|
if docker build --quiet -f "$context_check/Dockerfile" "$context_check" >"$tmp/context-check.out" 2>&1; then
|
||||||
|
echo "deploy/thothii.env entered the Docker build context" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! awk '
|
||||||
|
$1 == "FROM" && $2 !~ /^[^@]+@sha256:[0-9a-f]{64}$/ {
|
||||||
|
print FILENAME ":" FNR ": unpinned base image: " $0 > "/dev/stderr"
|
||||||
|
bad = 1
|
||||||
|
}
|
||||||
|
END { exit bad }
|
||||||
|
' docker/core.Dockerfile docker/frontend.Dockerfile; then
|
||||||
|
echo "every production FROM reference must use tag@sha256" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r base_image; do
|
||||||
|
manifest=$(docker buildx imagetools inspect "$base_image")
|
||||||
|
printf '%s\n' "$manifest" | grep -Eq 'Platform:[[:space:]]+linux/amd64'
|
||||||
|
printf '%s\n' "$manifest" | grep -Eq 'Platform:[[:space:]]+linux/arm64'
|
||||||
|
done < <(awk '$1 == "FROM" { print $2 }' docker/core.Dockerfile docker/frontend.Dockerfile | sort -u)
|
||||||
|
|
||||||
docker compose --project-name "$project" -f compose.yaml -f deploy/compose.local.yaml build --pull
|
docker compose --project-name "$project" -f compose.yaml -f deploy/compose.local.yaml build --pull
|
||||||
|
|
||||||
core_label=$(docker image inspect --format '{{ index .Config.Labels "io.thothii.pi.version" }}' thothii-core:local)
|
core_label=$(docker image inspect --format '{{ index .Config.Labels "io.thothii.pi.version" }}' thothii-core:local)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ set -eu
|
|||||||
|
|
||||||
image=${1:?usage: test-vector-migration-image.sh IMAGE [PLATFORM]}
|
image=${1:?usage: test-vector-migration-image.sh IMAGE [PLATFORM]}
|
||||||
platform=${2:-${PLATFORM:-linux/arm64}}
|
platform=${2:-${PLATFORM:-linux/arm64}}
|
||||||
|
repo_root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
||||||
slug=$$
|
slug=$$
|
||||||
network="thoth-vector-migration-$slug"
|
network="thoth-vector-migration-$slug"
|
||||||
database="thoth-vector-db-$slug"
|
database="thoth-vector-db-$slug"
|
||||||
@@ -36,7 +37,12 @@ status=$(docker run --rm --platform "$platform" --network "$network" \
|
|||||||
--entrypoint /opt/venv/bin/tht -e THT_VECTOR_ADMIN_URL="$database_url" \
|
--entrypoint /opt/venv/bin/tht -e THT_VECTOR_ADMIN_URL="$database_url" \
|
||||||
"$image" vector migrate --status --json)
|
"$image" vector migrate --status --json)
|
||||||
|
|
||||||
expected='{"applied": ["001", "002", "003"], "drifted": [], "pending": []}'
|
expected_versions=$(find "$repo_root/harness/tht/migrations/vector" -type f -name '[0-9][0-9][0-9]_*.sql' \
|
||||||
|
| sed 's|.*/||; s|_.*||' \
|
||||||
|
| LC_ALL=C sort \
|
||||||
|
| awk 'BEGIN { separator = ""; printf "[" } { printf "%s\"%s\"", separator, $0; separator = ", " } END { print "]" }')
|
||||||
|
test "$expected_versions" != '[]'
|
||||||
|
expected="{\"applied\": $expected_versions, \"drifted\": [], \"pending\": []}"
|
||||||
test "$applied" = "$expected"
|
test "$applied" = "$expected"
|
||||||
test "$status" = "$expected"
|
test "$status" = "$expected"
|
||||||
echo "core image vector migration discovery/status smoke passed"
|
echo "core image vector migration discovery/status smoke passed"
|
||||||
|
|||||||
@@ -25,17 +25,8 @@ test "$(docker image inspect --format '{{ index .Config.Labels "org.opencontaine
|
|||||||
docker run --rm --platform "$platform" --entrypoint /app/docker/smoke/core-smoke.sh \
|
docker run --rm --platform "$platform" --entrypoint /app/docker/smoke/core-smoke.sh \
|
||||||
"$core_image"
|
"$core_image"
|
||||||
./scripts/test-vector-migration-image.sh "$core_image" "$platform"
|
./scripts/test-vector-migration-image.sh "$core_image" "$platform"
|
||||||
docker run --rm --platform "$platform" -e BACKEND_BASE_URL=/api \
|
docker run --rm --platform "$platform" "$frontend_image" frontend-config-smoke
|
||||||
"$frontend_image" frontend-config-smoke
|
./docker/smoke/frontend-policy-smoke.sh
|
||||||
docker run --rm --platform "$platform" -e BACKEND_BASE_URL= \
|
|
||||||
"$frontend_image" frontend-config-smoke
|
|
||||||
docker run --rm --platform "$platform" --entrypoint frontend-policy-smoke "$frontend_image"
|
|
||||||
|
|
||||||
if docker run --rm --platform "$platform" -e BACKEND_BASE_URL=/backend \
|
|
||||||
"$frontend_image" frontend-config-smoke >/dev/null 2>&1; then
|
|
||||||
echo "frontend accepted an unsupported BACKEND_BASE_URL" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if docker run --rm --platform "$platform" -e THOTH_PUBLIC_EXPOSURE=true -e AUTH_MODE=none \
|
if docker run --rm --platform "$platform" -e THOTH_PUBLIC_EXPOSURE=true -e AUTH_MODE=none \
|
||||||
"$core_image" server >/dev/null 2>&1; then
|
"$core_image" server >/dev/null 2>&1; then
|
||||||
echo "core accepted public exposure without upstream authentication" >&2
|
echo "core accepted public exposure without upstream authentication" >&2
|
||||||
|
|||||||
Reference in New Issue
Block a user