COMMIT_SHA defaulted to 'unknown', so a caller that forgot to pass
--build-arg COMMIT_SHA would silently ship an image whose footer,
health checks, and error reports all report 'unknown'. Add a guard in
the builder stage that refuses to proceed when COMMIT_SHA is unset or
still the 'unknown' default.
showcase/scripts/ now ships a package-lock.json (committed here for the
first time), so the Docker builder can use 'npm ci' for deterministic
installs instead of 'npm install'. Update the comment to match actual
state and copy the lockfile into the scripts stage.
The runner stage was copying the entire builder node_modules tree,
shipping tailwindcss, postcss, tsx, typescript, and every @types/*
package into the production image. Add 'npm prune --omit=dev' after
the node_modules copy (and also copy package-lock.json so prune runs
deterministically) to strip dev-only packages from the runtime image.
- Re-declare COMMIT_SHA and BRANCH ARGs in the runner stage so ENV
interpolation resolves (ARGs do not cross stages in multi-stage builds).
- Declare NEXT_PUBLIC_BASE_URL as a build-time ARG/ENV so CI can pass it
via --build-arg for the production `next build` (next.config.ts requires it).
- Copy lockfile for shell-docs and switch to `npm ci` for reproducible installs.
scripts/ has no committed lockfile yet, so keep `npm install` there.
- Add .dockerignore so local .next/, node_modules/, .git, logs, and .env*
are not slurped into the build context.
- Invoke next via node_modules/.bin/next (both build and CMD) instead of
npx to avoid runtime network-fallback risk.
- Document the required build context at the top of the Dockerfile.
- Note the libc6-compat discussion: the slim base is Debian (glibc), so
no compat shim is required; note how to enable it if we ever move to
node:20-alpine.