ci(release): publish releases as the sites/zddc-releases OCI image #7
Loading…
Reference in a new issue
No description provided.
Delete branch "ctrl-agent/zddc:ci/releases-oci-image"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Replaces the rsync release deploy (
deploy-origin.sh, whose header said an OCI image would replace it) with the lab's standard content-image model. Releases are now published assites/zddc-releases:<X.Y.Z>+:latest, with the tree underreleases/in the image (image path/releases/...). The origin mounts the image at/var/www/zddc-releasesand serves/releases/*withroot /var/www/zddc-releases, so no path is rewritten. Each release is a full image, so a stale file a cut didn't include is simply absent; that replaces rsync's--delete-aftercleansing.Flow (
release-on-merge.yml)clone → decide → preflight (every push) → release: fetch+verify →
publish-image.sh→ verify live or an explicit "SKIPPED, not refreshed" warning step → record │ no release:ensure-published.sh→ (verify live if it built and refreshed).publish-image.sh(replacesdeploy-origin.shin the same position). It moves the verified bundle to<stage>/releases, runsbuildah from scratch+copy <stage> /, deletes the staging tree, commits,buildah rms the working container, logs in (password on stdin) and pushes:<ver>+:latest. When all threeDEPLOY_*are set it rings"$DEPLOY_COMMAND zddc-releases"and printsrefreshed=true. Otherwise it printsrefreshed=falsewith a::warning::that the release is not live, and the workflow's live-check step is skipped while a separate step says so. It never passes silently.History guard (fail closed), before any build. The bundle's
archive_v*.htmlcount (maxdepth 1,.sigexcluded) must be ≥ the count in the LIVE listinghttps://zddc.varasys.io/releases/fetched withAccept: application/json(Caddy browse JSON). If the listing can't be read, isn't JSON, isn't an array, or counts zero, the publish fails and is never treated as "no history". The messages are the old guard's (history guard: bundle=N versions, live=M versions,…could not count live versions…,live tree reports ZERO versions…,bundle has less history than live (N < M)…).ensure-published.shruns on every non-release merge. It sendsGET https://$REGISTRY/v2/sites/zddc-releases/manifests/<manifest version>with the push credentials (passed to curl on stdin with-K -, never in argv) and an Accept header listing the OCI and docker manifest and index types:zddc-releasesbundle. That bundle'sbundle.sha256must equal the manifest'sbundle-sha256(stricter than the brief, see below). It then runs the samerelease-fetch.shchecks a release gets (hash, every signature againstrelease-keys/current.pem, canonicals point at the version, the index names it), deletes the tarball, and callspublish-image.sh.This is the bootstrap (this PR's own merge publishes the first image, from v0.0.57) and it self-heals a lost image.
release-preflight.shruns on every push. It requiresREGISTRY,REGISTRY_USER,REGISTRY_TOKENandPACKAGE_TOKEN.DEPLOY_ORIGIN,DEPLOY_COMMANDandSTATIC_DEPLOY_KEYmust be set all together or not at all; a partial set fails with nothing published.publish-image.shre-checks all-or-none itself.release-fetch.shgains an optional<package> <package-version>source (both or neither); with neither, it behaves exactly as before. The inline live check moved unchanged intoverify-live.sh.Vars/secrets (for the hub)
REGISTRYREGISTRY_USERpush-zddc-releasesDEPLOY_ORIGINDEPLOY_COMMANDrefresh-site)REGISTRY_TOKENpush-zddc-releases's passwordPACKAGE_TOKENSTATIC_DEPLOY_KEYrefresh-site zddc-releases, at cutoverMust be true before merging
REGISTRY,REGISTRY_USERandREGISTRY_TOKENmust be set before the merge. The preflight now runs on every push, so without them this merge and every later push go red. That is intended: fail closed.vars.DEPLOY_ORIGINandsecrets.STATIC_DEPLOY_KEY, very likely org-level, which this repo inherits. WithDEPLOY_COMMANDunset that is 2 of 3, and the preflight fails the merge. WithDEPLOY_COMMANDorg-visible too, it is 3 of 3 and the merge rings the origin with the old shared key before cutover. Before merging, make sure this repo sees either none of the three or all three. Repo-level values override org ones, but an org value cannot be "unset" per repo.REGISTRY_USERmust be the repo-level scoped account. An inherited org-level push account such asci-pubwould pass the preflight and then fail the push with a 403.Between this merge and the cutover
Releases publish their image and are recorded (accumulator, 7 tags) but are not live: the origin keeps serving the old
/var/www/zddc/releases/tree, and each run warns "published but NOT verified live". The history guard keeps comparing against that live listing, which is still correct. AGENTS.md says this.Disk (runner-pub, ~23 GB free)
The tree is ~5.35 GB unpacked and ~2.35 GB packed.
release-fetch.shunpacks it, and the staging tree right afterbuildah copy. Peak ≈ tree + vfs container rootfs (≈10.7 GB) during copy and commit.release-record.sh's accumulator upload, so the peak is ≈ 13.1 GB.buildah pushspools a compressed blob, add ≈2.4 GB, so the worst case is ≈15.5 GB.These are estimates from the vfs model, not a measured run.
Tests
sh .forgejo/scripts/test-release-on-merge.shgives 42 passed, 0 failed (was 21). The 21 new known-answer cases are:refresh=false, all three giverefresh=true,REGISTRY_TOKENmissing,PACKAGE_TOKENmissing, and two of threeDEPLOY_*refused./releaseswith the canonical symlink intact,:verand:latestpushed, password on stdin, refresh skipped out loud, and staging removed. Fewer than live is refused with nothing pushed. Unreadable, non-JSON, non-array and zero listings are refused.ssh deploy@origin 'refresh-site zddc-releases',refreshed=true), and a half-configured set is refused.release-fetch.sh.buildah, ssh and the registry's
/v2/answers are logging stand-ins on PATH. Every other request goes to the real curl againstfile://fixtures.Mutation tests. Each planted bug turned the suite red:
[]archive*.html-ushellcheck -S warningis clean on every new or changed script (and-S info, except SC1091 for the dynamic.sources). The SC2034 warnings in the untouchedrelease-record.shwere already there.Not tested here
No real buildah, registry or origin was available on the authoring side. The test stand-ins assume
buildah copy <dir> /copies the directory's contents and preserves symlinks: the canonicalarchive.htmland friends are symlinks. Please confirm on the first image, for example/releases/archive.htmlresolves after cutover.verify-live.shchecks it.Hub notes
REGISTRY_TOKEN(a repo secret),REGISTRY_USER(push-zddc-releases),IMAGE_REPOandSITE_NAME.REGISTRY,DEPLOY_ORIGINandDEPLOY_COMMANDare inherited org vars.STATIC_DEPLOY_KEYis still the inherited (gated) org key until cutover. All of the refresh inputs are therefore present, so the refresh will be attempted on merge.refresh-siterejectszddc-releases, which it does not serve yet. The image push happens before the refresh and is unaffected. The hub cuts over right after the merges and re-runs the workflow.🤖 Generated with Claude Code