diff --git a/Dockerfile b/Dockerfile index 6db512d4b..413b9421d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,6 +16,8 @@ RUN --mount=type=cache,id=pulse-npm-cache,target=/root/.npm \ # Copy frontend source COPY frontend-modern/ ./ COPY scripts/exclusive-lock.mjs /app/scripts/exclusive-lock.mjs +COPY docs/ /app/docs/ +COPY SECURITY.md TERMS.md /app/ # Build frontend RUN --mount=type=cache,id=pulse-npm-cache,target=/root/.npm \ diff --git a/docs/release-control/v6/internal/subsystems/deployment-installability.md b/docs/release-control/v6/internal/subsystems/deployment-installability.md index 3655e88d5..f26496028 100644 --- a/docs/release-control/v6/internal/subsystems/deployment-installability.md +++ b/docs/release-control/v6/internal/subsystems/deployment-installability.md @@ -128,6 +128,12 @@ demo-deploy workflow. Shipped binaries, installable container images, and governed deployment-build workflows must all carry the same build metadata contract rather than depending on whichever local ldflags string happened to be updated last. +That same Docker release-build boundary also owns the embedded frontend's +shipped-doc inputs. When the frontend embed build syncs public docs from the +repo root, `Dockerfile` must stage the canonical shipped docs set into the +container build context before `npm run build` runs, rather than relying on a +workstation-local checkout layout or leaving hosted runtime image builds unable +to resolve `/app/docs/*.md`, `SECURITY.md`, or `TERMS.md`. The same governed promotion path must now stay explicit too: `scripts/release_control/resolve_release_promotion.py` is the canonical owner for stable-versus-prerelease metadata validation shared by `.github/workflows/release-dry-run.yml` diff --git a/scripts/installtests/build_release_assets_test.go b/scripts/installtests/build_release_assets_test.go index de2273cba..d2b1e79f8 100644 --- a/scripts/installtests/build_release_assets_test.go +++ b/scripts/installtests/build_release_assets_test.go @@ -105,6 +105,24 @@ func TestDockerAndDemoBuildsUseCanonicalReleaseLdflags(t *testing.T) { } } +func TestDockerfileStagesShippedDocsForEmbeddedFrontendBuild(t *testing.T) { + dockerfileBytes, err := os.ReadFile(repoFile("Dockerfile")) + if err != nil { + t.Fatalf("read Dockerfile: %v", err) + } + + dockerfile := string(dockerfileBytes) + required := []string{ + `COPY docs/ /app/docs/`, + `COPY SECURITY.md TERMS.md /app/`, + } + for _, needle := range required { + if !strings.Contains(dockerfile, needle) { + t.Fatalf("Dockerfile missing shipped-doc build input: %s", needle) + } + } +} + func repoFile(parts ...string) string { root := filepath.Join("..", "..") segments := append([]string{root}, parts...)