0012 — Portal version stamp from the built commit
Status: Accepted · Date: 2026-09-25
Context
Every app's version is resolved from git at build time by
scripts/version.mjs. No file in the tree carries a version number. The portal
used to read the nearest portal-v* tag, the way the tracking app reads v*.
The production portal (https://portal.enode.ai) always showed the version
dev. The live bundle had appVersion:"dev" baked in. Vercel builds the portal
from a shallow clone without tags, so the tag lookup failed and the script fell
back to dev without logging anything. With the full history, the same release
commit resolves to 1.0.1 (67).
Two further problems made the tag scheme a poor fit for the portal:
- A release tag has to exist before the deploy is built. The deploy is built on
the merge into
release, but a tag is usually set after that merge. - No
portal-v*tag had been set sinceportal-v1.0.1(2026-08-28), so even a working lookup would have shown a stale version.
Decision
The portal is stamped with the commit it was built from, not with a release number:
| Value | Source | Example |
|---|---|---|
NEXT_PUBLIC_APP_VERSION | committer date of HEAD, as YYYY.MM.DD | 2026.09.22 |
NEXT_PUBLIC_APP_BUILD | first seven characters of the HEAD hash | e42a1a2 |
useAppVersion joins them into 2026.09.22 (e42a1a2).
The portal is a website. Every user always runs the latest deploy, so a release number would answer no question. A support report only needs to identify the code that was running, and the hash does that exactly. This is the common practice for continuously deployed web apps.
The stamp needs only the checked-out commit: no tag, no history, no manual step. It therefore works in Vercel's shallow clone as it is.
What stays the same
- The tracking app keeps its
v*tags. Play and the App Store require a real release version and a monotonic build number. - The backend reads no portal version. The
app-versionheader is native-only because it is not in the API's CORS allow-list, so the format is free to choose. - The existing
portal-v*tags stay in place but are no longer read.
Consequences
- The portal shows no marketing version such as
1.0.2any more. - A local
next devof the portal shows the checked-outHEAD, even with uncommitted changes on top. - When no git checkout is available, the version is
dev, andscripts/version.mjsprints a[version]warning to the build log.
See Deployment → "Versioning".