fix(build): COPY .git breaks the Portainer build — the version stamp stops deploys #235

Closed
opened 2026-08-29 15:49:35 -05:00 by bermudalamb · 0 comments
Owner

#233 added COPY .git ./.git to the backend build stage. Portainer's build context does not contain .git, so every stack deploy now fails before anything else runs:

failed to deploy a stack: compose build operation failed: failed to solve:
failed to compute cache key: failed to calculate checksum of ref ...: "/.git": not found

This is the exact failure #233 set out to avoid. That issue says in as many words 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. But the guard was put in the wrong layer: COPY fails hard at image-build time, long before any of that code executes. Graceful degradation in the application is worth nothing if the Dockerfile has already refused to build.

The assumption came from the local build context, where there is no .dockerignore and .git is therefore present. It was verified with a local docker build, which passed, and never against Portainer — the only environment that matters for this.

Immediate fix

Remove the COPY .git ./.git line. Nothing else in #233 can fail a build: writeBuildInfo is guarded, finds no .git, warns, and writes a stamp with commit: "unknown" and a real builtAt.

That leaves the build timestamp working in deployed environments, which is worth more than it sounds. Portainer already shows which commit it cloned; what it cannot tell you is whether the running container is actually that build. The timestamp closes precisely that gap — and a stale timestamp is exactly what the QA incident on 2026-08-29 would have shown.

The commit will read unknown in QA and production until it can be sourced another way.

The commit, separately

Getting the real commit into a Portainer-built image needs a different mechanism, and it is worth deciding deliberately rather than guessing again:

  • A GIT_COMMIT build arg fed from a stack variable — works, but has to be updated by hand every deploy, which defeats the purpose.
  • A version file written by Gitea Actions on merge to main — automatic, at the cost of a bot commit on every merge.
  • Building and pushing an image from CI and having Portainer pull it rather than build — the most correct answer and the largest change to how deployment works.

None of these should be attempted without checking them against Portainer first. The lesson from this issue is that a local docker build passing proves nothing about the environment that actually deploys.

Follows #233.

#233 added `COPY .git ./.git` to the backend build stage. Portainer's build context does not contain `.git`, so every stack deploy now fails before anything else runs: ``` failed to deploy a stack: compose build operation failed: failed to solve: failed to compute cache key: failed to calculate checksum of ref ...: "/.git": not found ``` **This is the exact failure #233 set out to avoid.** That issue says in as many words 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. But the guard was put in the wrong layer: `COPY` fails hard at image-build time, long before any of that code executes. Graceful degradation in the application is worth nothing if the Dockerfile has already refused to build. The assumption came from the local build context, where there is no `.dockerignore` and `.git` is therefore present. It was verified with a local `docker build`, which passed, and never against Portainer — the only environment that matters for this. ## Immediate fix Remove the `COPY .git ./.git` line. Nothing else in #233 can fail a build: `writeBuildInfo` is guarded, finds no `.git`, warns, and writes a stamp with `commit: "unknown"` and a real `builtAt`. That leaves the **build timestamp working in deployed environments**, which is worth more than it sounds. Portainer already shows which commit it cloned; what it cannot tell you is whether the running container is actually that build. The timestamp closes precisely that gap — and a stale timestamp is exactly what the QA incident on 2026-08-29 would have shown. The commit will read `unknown` in QA and production until it can be sourced another way. ## The commit, separately Getting the real commit into a Portainer-built image needs a different mechanism, and it is worth deciding deliberately rather than guessing again: - A `GIT_COMMIT` build arg fed from a stack variable — works, but has to be updated by hand every deploy, which defeats the purpose. - A version file written by Gitea Actions on merge to `main` — automatic, at the cost of a bot commit on every merge. - Building and pushing an image from CI and having Portainer pull it rather than build — the most correct answer and the largest change to how deployment works. None of these should be attempted without checking them against Portainer first. The lesson from this issue is that a local `docker build` passing proves nothing about the environment that actually deploys. Follows #233.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bermudalamb/redefined-designs#235