Files
redefined-designs/Dockerfile
T
bermudalamb 1c69ac71f6
Linting / lint (pull_request) Successful in 2m13s
SonarQube Analysis / sonarqube (pull_request) Failing after 21m57s
fix(build): stop the version stamp from breaking every Portainer deploy (#235)
`COPY .git ./.git`, added in #233, fails with `"/.git": not found` in Portainer's build context, so every stack deploy died before anything else ran.

This is the exact outcome #233 set out to prevent. That issue states that a version stamp must never be the thing that stops a deploy, and the resolution code honours it — every unreadable-.git path returns "unknown" and warns. The guard was simply in the wrong layer: COPY fails at image-build time, long before any of that code executes. Graceful degradation in the application buys nothing once the Dockerfile has refused to build.

The assumption came from the local build context, where there is no .dockerignore and .git is therefore present. It was checked with a local `docker build`, which passed, and never against the only environment that actually deploys.

Verified properly this time, by building from `git archive HEAD` — a context containing exactly the tracked files and no history, which is what a clean checkout gives. That build now succeeds and stamps `commit: "unknown"` with a real `builtAt`. The failing case is reproduced and fixed rather than reasoned about.

`commit` will read "unknown" wherever Portainer builds. `builtAt` is still real, and is the half that matters most: Portainer already reports which commit it cloned, but cannot tell you whether the running container is that build. A build time can, and a stale one is exactly what the QA incident earlier today would have shown. Locally nothing changes — writeBuildInfo reads ../.git directly and still resolves a real commit.

Sourcing the real commit inside a Portainer build needs a different mechanism, and #235 records the three candidates rather than guessing at a fourth.

Closes #235
2026-08-29 15:51:52 -05:00

46 lines
2.0 KiB
Docker
Executable File

FROM node:20-bookworm-slim AS frontend-build
WORKDIR /app/frontend
COPY frontend/package.json ./
RUN npm install
COPY frontend/ ./
RUN npm run build
FROM node:20-bookworm-slim AS backend-build
WORKDIR /app/backend
COPY backend/package.json ./
RUN npm install
COPY backend/ ./
# There is deliberately no `COPY .git` here. It was tried in #233 and broke
# every Portainer deploy with `"/.git": not found` — Portainer's build context
# does not contain the repository history, whatever a local `docker build`
# suggests. That was the version stamp becoming the thing that stopped a
# deploy, which is the one outcome it must never be (#235).
#
# The consequence is that `commit` reads "unknown" in any environment Portainer
# builds. `builtAt` is still real, and is the half that matters most here:
# Portainer already reports which commit it cloned, but it cannot tell you
# whether the running container is actually that build. A build time can.
#
# writeBuildInfo runs against the compiled output, so it has to follow tsc, and
# it warns rather than fails when it finds no .git.
RUN npm run build && node dist/writeBuildInfo.js
FROM node:20-bookworm-slim
WORKDIR /app
COPY --from=backend-build /app/backend/package.json ./
RUN npm install --omit=dev
COPY --from=backend-build /app/backend/dist ./dist
COPY --from=backend-build /app/backend/migrate.js ./migrate.js
COPY --from=backend-build /app/backend/migrations ./migrations
COPY --from=frontend-build /app/frontend/dist ./public
ENV NODE_ENV=production
EXPOSE 3000
# Migrations run before the app serves, so deployed code can never be ahead of
# the database schema. Previously this was a separate, manual
# `docker exec ... node migrate.js up` that was easy to forget — and forgetting
# it meant every item query referenced tables that did not exist yet, which
# surfaced as an empty storefront rather than an error.
#
# `&&` matters: migrate.js exits non-zero on failure, so a bad migration stops
# the container instead of letting it serve against a half-migrated schema.
CMD ["sh", "-c", "node migrate.js up && node dist/server.js"]