There is currently no way to tell which build a deployed environment is running. A redeploy either took effect or it did not, and nothing on the screen says which.
This is not hypothetical. On 2026-08-29, immediately after #232 merged, docker exec redefined-designs-qa-syn npm run backfill:images failed with tsx: not found. The container was still running the pre-merge image. The only thing that revealed it was npm echoing the old script line — had the change been anywhere other than a package.json script, the container would have looked healthy while running the wrong code.
What it shows
A muted line in the admin header: the short commit and when the image was built.
a5076cc · built 29 Aug 20:14
The commit answers "is this the code I expect". The build time answers "did my redeploy actually rebuild" — a distinct question, and the one that would have caught the case above.
Getting it into the image
A build stage copies .git and reads the SHA with a few lines of node, writing dist/buildInfo.json. No git binary is needed and no apt layer: node:20-bookworm-slim has no git, and installing one for this would be a poor trade. There is no .dockerignore, so .git is already in the build context, and Portainer clones the repository before building, so it is present there too.
Both .git/HEAD shapes are handled — a detached HEAD holds the SHA directly, a ref: line means reading refs/heads/<name> and falling back to packed-refs. Anything missing yields unknown rather than a build failure. A version stamp must never be the thing that stops a deploy.
Where it is served
A new GET /api/admin/version, on its own router behind requireAdminGate, reading the file once at startup.
Deliberately not on /api/config. That endpoint is public — the storefront fetches it — and publishing a commit SHA there tells anyone exactly which code is running, which is free help to someone matching known vulnerabilities against the repository. The request was for the admin side, and the admin side is also the correct side.
Testing
The .git parsing is the only part with branches, so it is pure and unit tested: detached HEAD, ref: form, packed-refs fallback, and everything missing yielding unknown. An integration test covers the endpoint being gated and returning the expected shape. The real proof is building the image and confirming the reported SHA matches git rev-parse --short HEAD.
Out of scope
Telling you the deployment is stale — comparing what is running against what is on main — is a bigger feature needing a source of truth about what should be deployed. This makes the running version visible; noticing it is out of date is still a person's job.
There is currently no way to tell which build a deployed environment is running. A redeploy either took effect or it did not, and nothing on the screen says which.
This is not hypothetical. On 2026-08-29, immediately after #232 merged, `docker exec redefined-designs-qa-syn npm run backfill:images` failed with `tsx: not found`. The container was still running the pre-merge image. The only thing that revealed it was npm echoing the old script line — had the change been anywhere other than a `package.json` script, the container would have looked healthy while running the wrong code.
## What it shows
A muted line in the admin header: the short commit and when the image was built.
```
a5076cc · built 29 Aug 20:14
```
The commit answers "is this the code I expect". The build time answers "did my redeploy actually rebuild" — a distinct question, and the one that would have caught the case above.
## Getting it into the image
A build stage copies `.git` and reads the SHA with a few lines of node, writing `dist/buildInfo.json`. No `git` binary is needed and no apt layer: `node:20-bookworm-slim` has no git, and installing one for this would be a poor trade. There is no `.dockerignore`, so `.git` is already in the build context, and Portainer clones the repository before building, so it is present there too.
Both `.git/HEAD` shapes are handled — a detached HEAD holds the SHA directly, a `ref:` line means reading `refs/heads/<name>` and falling back to `packed-refs`. Anything missing yields `unknown` rather than a build failure. **A version stamp must never be the thing that stops a deploy.**
## Where it is served
A new `GET /api/admin/version`, on its own router behind `requireAdminGate`, reading the file once at startup.
Deliberately **not** on `/api/config`. That endpoint is public — the storefront fetches it — and publishing a commit SHA there tells anyone exactly which code is running, which is free help to someone matching known vulnerabilities against the repository. The request was for the admin side, and the admin side is also the correct side.
## Testing
The `.git` parsing is the only part with branches, so it is pure and unit tested: detached HEAD, `ref:` form, packed-refs fallback, and everything missing yielding `unknown`. An integration test covers the endpoint being gated and returning the expected shape. The real proof is building the image and confirming the reported SHA matches `git rev-parse --short HEAD`.
## Out of scope
Telling you the deployment is *stale* — comparing what is running against what is on `main` — is a bigger feature needing a source of truth about what should be deployed. This makes the running version visible; noticing it is out of date is still a person's job.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
There is currently no way to tell which build a deployed environment is running. A redeploy either took effect or it did not, and nothing on the screen says which.
This is not hypothetical. On 2026-08-29, immediately after #232 merged,
docker exec redefined-designs-qa-syn npm run backfill:imagesfailed withtsx: not found. The container was still running the pre-merge image. The only thing that revealed it was npm echoing the old script line — had the change been anywhere other than apackage.jsonscript, the container would have looked healthy while running the wrong code.What it shows
A muted line in the admin header: the short commit and when the image was built.
The commit answers "is this the code I expect". The build time answers "did my redeploy actually rebuild" — a distinct question, and the one that would have caught the case above.
Getting it into the image
A build stage copies
.gitand reads the SHA with a few lines of node, writingdist/buildInfo.json. Nogitbinary is needed and no apt layer:node:20-bookworm-slimhas no git, and installing one for this would be a poor trade. There is no.dockerignore, so.gitis already in the build context, and Portainer clones the repository before building, so it is present there too.Both
.git/HEADshapes are handled — a detached HEAD holds the SHA directly, aref:line means readingrefs/heads/<name>and falling back topacked-refs. Anything missing yieldsunknownrather than a build failure. A version stamp must never be the thing that stops a deploy.Where it is served
A new
GET /api/admin/version, on its own router behindrequireAdminGate, reading the file once at startup.Deliberately not on
/api/config. That endpoint is public — the storefront fetches it — and publishing a commit SHA there tells anyone exactly which code is running, which is free help to someone matching known vulnerabilities against the repository. The request was for the admin side, and the admin side is also the correct side.Testing
The
.gitparsing is the only part with branches, so it is pure and unit tested: detached HEAD,ref:form, packed-refs fallback, and everything missing yieldingunknown. An integration test covers the endpoint being gated and returning the expected shape. The real proof is building the image and confirming the reported SHA matchesgit rev-parse --short HEAD.Out of scope
Telling you the deployment is stale — comparing what is running against what is on
main— is a bigger feature needing a source of truth about what should be deployed. This makes the running version visible; noticing it is out of date is still a person's job.