Repository navigation
v1.0.0
[1.0.0] - 2026-08-14
First tagged version of the site. The site itself was already live on
kbrdn.dev — what this version adds is everything that was
missing around it: a way to say which version is deployed, a way to go back to
a previous one, and a release that a tag actually performs rather than merely
records.
Versioning starts at 1.0.0 rather than 0.x: the product is in production,
and a 0. would misdescribe it.
Added
-
A release flow, where there was none
(#18). Deployment worked, but
nothing tied it to a version: images were tagged:mainand:dev, both
mutable,package.jsoncarried noversionfield at all, and nobody could
answer "which version is live on pre-prod right now".Tags now drive deployment.
vX.Y.Z-rc.Nbuilds an immutable
ghcr.io/kbrdn1/kbrdn.dev:vX.Y.Z-rc.N, ships it to preprod and opens a
GitHub pre-release;vX.Y.Zdoes the same for prod and publishes the
release. Pushing todevstill redeploys preprod for day-to-day iteration —
that path is untouched, anddeploy.ymlonly ever wakes on branches while
release.ymlonly wakes on tags.The routing lives in
scripts/resolve-release-tag.shrather than inline YAML,
because getting it wrong ships an unvalidated candidate to production;
scripts/resolve-release-tag.test.shcovers it, malformed tags included. -
GET /api/healthreturning{ status, version, sha, env }, all three
baked in at build time. The containerHEALTHCHECKnow points at it instead
of/: the old probe went green as soon as the home page rendered
server-side, which stays true even when the API layer behind/api/github/*
and/api/contactis dead. It uses a real GET, notwget --spider— that
sends a HEAD, and h3 does not route HEAD to a.gethandler. -
Guardrails that hold the protocol up, rather than a document asking
politely: a stable tag must be an ancestor ofmainand a candidate an
ancestor ofdev;package.jsonmust match the tag; the notes file must
exist. All four run before anything is built or deployed. The GitHub
release is only published once/api/healthhas confirmed, from the outside,
that the expected version and environment are serving traffic. -
scripts/github-deploy.shis versioned and hardened. The piece that
reconciles the workflow — which configures over environment variables — with
deploy.sh, which parses flags, turned out to be aForceCommandwrapper
living only on the VPS: the centre of the deployment pipeline was invisible
from the repository. It now only exports an allowlist (ENV,IMAGE,
GITHUB_TOKEN,RESEND_API_KEY,NUXT_STUDIO_TOKEN) and refuses anything
else,PATHincluded —deploy.shresolvesdocker,nginxand
systemctlthrough it, as root. -
A promo banner at the top of the README
(#24), dark and light, built
around the site's own monogram.
Changed
-
Rollback actually rolls back
(#18).scripts/deploy.sh
used todocker rm -fthe outgoing colour at the end of every deploy, while
--rollbackrequired exactly that container — at steady state a single
colour runs, so the option could only ever fail, and silently so until the
day it was needed. The outgoing colour is now stopped and kept, and the
immutable per-version images make
--image ghcr.io/kbrdn1/kbrdn.dev:vX.Y.Za real return to a known version
rather than a colour flip. -
Deployments to one environment are serialised. The protocol itself used
to put two in flight: merging intomainwakesdeploy.yml, pushing the tag
right after wakesrelease.yml, and their workflow-level concurrency groups
differ. Twodeploy.sh --env prodwould then target the same container names
and the same nginx upstream. Both jobs now share avps-deploy-<env>key. -
Containers carry a
kbrdn.versionlabel, sodocker inspectanswers the same
question as/api/healthfrom inside the VPS. -
Re-running a released tag no longer rebuilds its image: registry tags are
mutable and the build is not reproducible, so republishing could quietly make
:vX.Y.Zstop meaning what it meant — the very property version rollback
rests on.
Docs
docs/RELEASE.md— the protocol, the repo's knobs, the
real deployment chain (ForceCommand→github-deploy.sh→deploy.sh), and
the VPS synchronisation procedure.- Two traps found the hard way and written down: overwriting
/srv/deploy.sh
while it runs breaks the deploy in progress (bash reads a script by offset),
and every PR intomainmust close an issue — the release PR included, so
each version needs its own.