fix(build): stop the version stamp from breaking every Portainer deploy (#235) #236

Merged
bermudalamb merged 1 commits from fix/235-remove-git-copy into main 2026-08-29 17:07:38 -05:00
Owner

Merge this to unblock deploys. One line removed from the Dockerfile.

COPY .git ./.git, which I added in #233, fails in Portainer's build context:

failed to solve: failed to compute cache key:
failed to calculate checksum of ref ...: "/.git": not found

What went wrong

This is precisely the outcome #233 set out to prevent. That issue says 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 rather than throwing.

The guard was in the wrong layer. COPY fails at image-build time, long before any application code runs. Graceful degradation buys nothing once the Dockerfile has already refused to build.

The assumption came from the local build context, where there is no .dockerignore so .git is present. I verified it with a local docker build, which passed, and never against the only environment that actually deploys.

Verified properly this time

Rather than reasoning about it, I reproduced the failing case: built from git archive HEAD, a context containing exactly the tracked files and no history — what a clean checkout gives Portainer.

$ cd <archive of HEAD, no .git>
$ docker build -t redefined-nogit-check .
... DONE

$ docker run --rm redefined-nogit-check cat dist/buildInfo.json
{ "commit": "unknown", "builtAt": "2026-08-29T20:50:24Z" }

The build succeeds and stamps a real build time. Locally nothing changes — writeBuildInfo reads ../.git directly, outside Docker, and still resolves a real commit:

$ node dist/writeBuildInfo.js
[build-info] a6121db built 2026-08-29T20:51:28Z
$ git rev-parse --short HEAD
a6121db

296 unit tests pass; no application code changed.

What you get after this

commit reads unknown wherever Portainer builds. builtAt is real.

That is a partial delivery of what you asked for, and worth being straight about — but the timestamp is the more useful half here. Portainer already tells you which commit it cloned; what it cannot tell you is whether the running container is actually that build. A build time can, and a stale one is exactly what this morning's QA container would have shown.

Getting the real commit

Needs a different mechanism. #235 records the three candidates — a build arg from a stack variable, a version file written by Gitea Actions, or building in CI and having Portainer pull rather than build — with their costs. I would rather decide that deliberately than guess at a fourth, having just guessed wrong at the third.

The lesson is recorded there too: a passing local docker build proves nothing about the environment that deploys.

Closes #235

**Merge this to unblock deploys.** One line removed from the Dockerfile. `COPY .git ./.git`, which I added in #233, fails in Portainer's build context: ``` failed to solve: failed to compute cache key: failed to calculate checksum of ref ...: "/.git": not found ``` ## What went wrong This is precisely the outcome #233 set out to prevent. That issue says 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 rather than throwing. **The guard was in the wrong layer.** `COPY` fails at image-build time, long before any application code runs. Graceful degradation buys nothing once the Dockerfile has already refused to build. The assumption came from the local build context, where there is no `.dockerignore` so `.git` is present. I verified it with a local `docker build`, which passed, and never against the only environment that actually deploys. ## Verified properly this time Rather than reasoning about it, I reproduced the failing case: built from `git archive HEAD`, a context containing exactly the tracked files and no history — what a clean checkout gives Portainer. ``` $ cd <archive of HEAD, no .git> $ docker build -t redefined-nogit-check . ... DONE $ docker run --rm redefined-nogit-check cat dist/buildInfo.json { "commit": "unknown", "builtAt": "2026-08-29T20:50:24Z" } ``` The build succeeds and stamps a real build time. Locally nothing changes — `writeBuildInfo` reads `../.git` directly, outside Docker, and still resolves a real commit: ``` $ node dist/writeBuildInfo.js [build-info] a6121db built 2026-08-29T20:51:28Z $ git rev-parse --short HEAD a6121db ``` 296 unit tests pass; no application code changed. ## What you get after this `commit` reads **`unknown`** wherever Portainer builds. `builtAt` is real. That is a partial delivery of what you asked for, and worth being straight about — but the timestamp is the more useful half here. Portainer already tells you which commit it cloned; what it cannot tell you is whether the running container is actually that build. A build time can, and a stale one is exactly what this morning's QA container would have shown. ## Getting the real commit Needs a different mechanism. #235 records the three candidates — a build arg from a stack variable, a version file written by Gitea Actions, or building in CI and having Portainer pull rather than build — with their costs. I would rather decide that deliberately than guess at a fourth, having just guessed wrong at the third. The lesson is recorded there too: a passing local `docker build` proves nothing about the environment that deploys. Closes #235
bermudalamb added 1 commit 2026-08-29 15:52:22 -05:00
fix(build): stop the version stamp from breaking every Portainer deploy (#235)
SonarQube Analysis / sonarqube (pull_request) Failing after 21m57s
Linting / lint (pull_request) Successful in 2m13s
1c69ac71f6
`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
bermudalamb merged commit 71f8a21e45 into main 2026-08-29 17:07:38 -05:00
bermudalamb deleted branch fix/235-remove-git-copy 2026-08-29 17:07:46 -05:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bermudalamb/redefined-designs#236