v1.10.0 carries upstream CVE fixes that the older releases predate, so stop building them. Their series move to patches/archive/<tag>/ rather than being deleted — still available for a deliberate rebuild, just not shipped — and ci/support-matrix.yaml now lists v1.10.0 alone. Dropping the matrix entries was not enough on its own. Two gaps made "not built" untrue: * apply-patches.sh only consulted the matrix `if command -v yq`, and the stock golang CI container has no yq — so in CI the registry was never actually read. The tag fell through to patches/<tag> every time. * An empty or missing patch directory was a warning and exit 0, i.e. "building vanilla upstream". Archiving the directories would therefore have made a dispatch for v1.8.0 publish an un-de-branded, unpatched, CVE-vulnerable build under this project's release name — the exact outcome we are trying to prevent. So the matrix is now an enforced allowlist: a tag it does not list exits 1, as does a patch directory with no patches in it. It is parsed with yq when present and with a small awk parse otherwise, so the allowlist holds in the CI container too. If the parse ever broke, an unmatched tag is refused rather than built, so the failure mode stays closed. PATCH_DIR remains the one bypass, and is now the documented way to rebuild an archived series on purpose. That path also needed a fix: the patch list is globbed before we cd into the source tree, so a relative PATCH_DIR — the form the new error message suggests — globbed fine and then failed in git am. PATCH_DIR is now resolved to an absolute path, with a missing directory reported as such. Also re-pointed the runnable examples in README.md, ci/run.sh and the workflow's dispatch hint from v1.5.0 to v1.10.0; following them verbatim would now hit the refusal. Verified by exercising every branch: v1.10.0 applies; v1.8.0 is refused with exit 1; PATCH_DIR=patches/archive/v1.8.0 rebuilds the archived series; an empty directory and a nonexistent one are both refused. The awk parser was checked against defaulted `patches:` fields, several tags sharing one directory, and comment lines containing decoy `- tag:` text. All seven archived patch files are recorded as pure renames. Note: existing published releases for the retired versions stay downloadable until deleted by hand in Gitea. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6.3 KiB
Multi-version build pipeline
How s3-gateway turns an upstream versitygw release into a de-branded build, and how to keep it tracking new upstream versions. This mirrors the scoutfs-build / scoutfs-notify model, collapsed into one repo because the patch set is small.
CI builds one version per run — triggered by a tag push or a manual dispatch, never on a schedule.
The flow
Each build is four steps, run for a single upstream tag:
-
ci/clone-source.sh— shallow-clones upstream versitygw at a tag intosrc/. Tag selection priority:VGW_TAG(the dispatch input, or a manual run),- the pushed tag (
GITHUB_REF=refs/tags/...), - the latest remote
vX.Y.Ztag. The checkout forcescore.autocrlf=falseso the tree is byte-for- byte upstream (LF) and the LF patches apply on any host.
-
ci/apply-patches.sh— resolves the patch directory for the tag fromci/support-matrix.yaml(patches:field, default = tag name) and appliespatches/<dir>/*.patchwithgit am --whitespace=nowarn --3way, in filename order. The--3wayfallback is what lets a series rebase across minor upstream drift. A tag the matrix does not list is refused (exit 1), as is a patch directory that turns out to be empty — either would otherwise publish an un-de-branded or superseded build. -
ci/build.sh— runsgoreleaser release --clean --skip=validate,publishinsrc/, producing binaries + deb/rpm insrc/dist/. Because the patch commits sit on top of the tag, HEAD is no longer exactly the tag, so we setGORELEASER_CURRENT_TAGto pin the stamped version and--skip=validateto bypass the dirty/tag checks. Upstream's.goreleaser.yamlis used unmodified — artifact names stayversitygw. -
ci/publish-release.sh— creates (or reuses) a Gitea release for the tag in this repo and uploadssrc/dist/*as assets. Idempotent: re-running for the same tag replaces existing assets.
Patch registry: ci/support-matrix.yaml
versitygw:
- tag: v1.10.0
patches: v1.10.0
tag— upstream git tag.patches— directory underpatches/to apply (defaults totag). Point several tags at one directory if the same series applies to a range of upstream releases.
apply-patches.sh consults this to find the patch directory for the tag
being built; with the default patches/<tag>/ naming it doubles as a
human-readable record of which versions are maintained.
It is also an allowlist, which is the mechanism that keeps
superseded versions from being rebuilt: a tag absent from the matrix
makes apply-patches.sh exit 1. Only PATCH_DIR bypasses it, and only
explicitly — that is how an archived series is rebuilt on purpose.
The matrix is read with yq when it is installed and with a small
awk parse otherwise, so the allowlist still applies in the stock
golang CI container, which has no yq.
Adding a new upstream version
When upstream releases vX.Y.Z:
-
Rebase the patch series onto the new tag in a throwaway clone:
git clone https://github.com/versity/versitygw vgw && cd vgw git -c core.autocrlf=false checkout -b debrand vX.Y.Z git config user.email you@example.com && git config user.name you git am --3way /path/to/s3-gateway/patches/<previous-tag>/*.patch # If a hunk conflicts: fix files, `git add -A`, `git am --continue`. mkdir -p /path/to/s3-gateway/patches/vX.Y.Z git format-patch --binary vX.Y.Z..HEAD \ -o /path/to/s3-gateway/patches/vX.Y.Z--binaryis required because the series deletes PNG logo assets. -
Register it in
ci/support-matrix.yaml:versitygw: - tag: vX.Y.Z patches: vX.Y.ZLeave the previous entry in place only if you still want builds of it. If the new release supersedes it — a security fix, say — retire the old one at the same time (see Retiring a version below).
-
Commit and push
ci/support-matrix.yaml+patches/vX.Y.Z/. -
Build it — either push a
vX.Y.Ztag to this repo, or run the build workflow manually and entervX.Y.Z. CI builds that one version and publishes it.
Sanity-check a series before committing
VGW_REPO_URL="/path/to/local/versitygw" VGW_TAG=vX.Y.Z bash ci/run.sh
A clean git am plus a populated src/dist/ means the series is good.
Retiring a version
Drop its ci/support-matrix.yaml entry — that alone stops it being
built, since the matrix is the allowlist — and git mv its directory
into patches/archive/<tag>/ rather than deleting it, so the series
stays available for a deliberate rebuild:
git mv patches/v1.8.0 patches/archive/v1.8.0
# then remove the v1.8.0 entry from ci/support-matrix.yaml
Update the table in patches/archive/README.md
and the supported-versions table in the top-level README to match.
Releases already published in this repo are not affected: retiring the patches stops future builds but leaves existing release assets downloadable until you delete them by hand in Gitea.
To rebuild an archived version deliberately, pass PATCH_DIR instead
of re-adding it to the matrix:
PATCH_DIR=patches/archive/v1.8.0 VGW_TAG=v1.8.0 bash ci/apply-patches.sh
CI configuration
- Triggers: a
v*tag push (builds that tag) or manual dispatch with a tag input (one-shot). No cron. - Runner: any Linux runner —
runs-on: ubuntu-latest. The Go toolchain comes from thegolang:latestcontainer, so the runner only needs Docker; nothing version-specific to install. - Container:
golang:latest— pure-Go build, GoReleaser's bundled nfpm makes deb/rpm without rpmbuild/dpkg. - Secret
TOKEN_GITEA: Gitea PAT with write:repository. Used both to clone this repo (the workflow does a manual token-rewritten clone, since a stock container has no Node foractions/checkout) and to publish releases. - Variable
VGW_REPO_URL(optional): override the upstream URL.
Release tagging
Releases are created in this repo with the upstream tag name
(v1.10.0). If you need to ship a rebuild of the same upstream version
(e.g. a patch fix), set RELEASE_TAG in ci/publish-release.sh's
environment to something like v1.10.0-acg2 to avoid clobbering.