Commit c40c1db
authored
feat(promote-release): retag docker moving tags + promote paired release on human promotion (#194)
* feat(promote-release): retag docker moving tags + promote paired release on human release-promotion
DEVOPS-1083. Wires on: release: types: [released] (verified live: fires
only on a human editing prerelease->false, never on a GITHUB_TOKEN/bot
release publish) to a digest-preserving docker buildx imagetools retag of
:latest/:X/:X.Y (and suffix variants), then optionally un-prereleases +
latests a paired release in another repo. Image list and oss-repo are
caller-supplied inputs so this isn't vcluster-pro-specific.
* fix(promote-release): use docker-username input, not github.actor, for GHCR login
Matches the existing vcluster-pro release.yaml pattern (DOCKER_USERNAME
secret paired with the GH_ACCESS_TOKEN PAT), instead of assuming the
release-promoting human's own GitHub login has GHCR write access.
* docs(promote-release): regenerate README input table with auto-doc
The hand-written table drifted from auto-doc's exact formatting
(alphabetical ordering, quoted default values), which check-docs caught.
* test(promote-release): add missing CI workflow, use fake fixtures, fix bats negation
- Add .github/workflows/test-promote-release.yaml - every other tested
action has one; this one was missing, so the bats suite never actually
ran in CI (only actionlint/zizmor/check-docs did).
- Replace real-looking test fixtures (v0.37.1, ghcr.io/loft-sh/vcluster-pro,
loft-sh/vcluster) with obviously-fake ones (v9.9.9, example-org/...) so
nobody mistakes test data for a real artifact.
- Fix two bare '! grep' assertions (shellcheck SC2314): a bash negation
outside 'run' doesn't reliably fail a bats test. Use 'run !' instead.
* fix(promote-release): guard :latest/:major against backport downgrades
Addresses PR review on #194:
- blocking: promoting an older line's backport/patch after a newer
stable is already :latest silently moved :latest/:{major} backwards.
Added is_latest_stable(), checked against the caller's own repo (docker
tags) and oss-repo (the paired release's --latest) independently.
:{major}.{minor} is unaffected (line-scoped, always safe). A failure to
list releases fails closed, matching the existing Homebrew-tap
downgrade guard's precedent rather than risking a silent downgrade.
- consider: pre-flight now checks the source manifest actually exists
(docker buildx imagetools inspect) before any retag starts, so a
suffix variant that wasn't built for this version can't leave earlier
entries partially retagged.
- consider: gh release edit failure now warns instead of hard-failing -
the docker retags already succeeded by that point, so a transient API
error on the advisory oss-repo step shouldn't read as a total failure.
- nit: README usage snippet now pins with the promote-release/v1 tag,
matching every sibling action README instead of a <sha> placeholder.
6 new bats cases (backport on each repo independently, missing-manifest
pre-flight, release-list fail-closed, edit-failure soft-warn); 19 total,
all green. zizmor/shellcheck clean.
* test(promote-release): cover same-major, older-minor backport ordering
Confirms the backport guard also catches promoting e.g. v0.35.6 after
v0.36.0 already exists, not just a major-version jump - sort -V's minor
component comparison is a distinct thing to get right from the major
one already covered.
* test(promote-release): explicit coverage for every unstable version shape
The existing stable-version regex (anchored, no room for a suffix) already
rejects rc/alpha/beta/next/next.internal before any docker or gh call - a
human can uncheck pre-release on ANY release regardless of its tag shape,
so release:released can fire for these too. Only one generic -rc.1 case
was actually exercised; add explicit cases for every named category.
* feat(promote-release): promote Homebrew taps as a metadata patch, not a rebuild
New optional inputs: homebrew-tap-repo, homebrew-formula-paths.
A formula's per-platform sha256 values are exactly what's already in
oss-repo's <version> release checksums.txt (already published, already
cosign-signed) - verified by hand against the real loft-sh/homebrew-tap
vcluster.rb and its matching v0.36.0 OSS release checksums.txt, which
match byte-for-byte. So promotion patches the existing formula file via
the GitHub Contents API (no git clone/push) instead of re-templating it:
swap the version and every url's tag segment, then rewrite each
artifact's sha256 on the line immediately after its url, sourced by
filename match against checksums.txt. Everything else in the formula
(deps, install blocks, test block) is preserved byte-for-byte.
Same backport guard as the docker tags, but all-or-nothing: a formula
has no line-scoped equivalent to :{major}.{minor}, so a backport/patch
promotion skips Homebrew entirely rather than partially. Missing
checksums, download failures, and contents API failures all warn and
skip rather than fail the run - the docker retags (and oss-repo
promotion, if configured) have already succeeded by this point.
11 new bats tests (32 total): happy path incl. static-content
preservation, backport skip, validation errors, download/GET/PUT
failure handling, dry-run, multiple formulas, missing-checksum
partial-patch. zizmor/shellcheck clean.
* fix(promote-release): harden advisory paths and close test-coverage gaps
Address PR #194 review:
- guard homebrew grep|head with || true so a no-match warn-skips under
pipefail instead of hard-failing the run
- guard jq .sha with // empty so a missing field warn-skips instead of
PUTting the literal "null" and 422ing
- compute oss-repo latest-stable once and reuse it for the advisory
homebrew gate, so it isn't re-listed and can't hit a second
hard-failing list call after everything else succeeded
- point the tap-update failure warning at re-running the idempotent
action instead of an incomplete manual gh api command
- document contents:write on homebrew-tap-repo in the token scope
- cover newest-among-non-empty-prior-stables, homebrew-skip
all-or-nothing invariant, and second-formula PUT body
References DEVOPS-1083
* docs(promote-release): regenerate README inputs table for token scope
* fix(promote-release): harden tag guards, dry-run, and homebrew patching per review
Addresses the review feedback on #194:
- docker moving tags: gate :{major}.{minor} on a line-scoped is_latest_stable
so a same-line out-of-order promotion can't regress the line tag, and widen
the release-list window to 1000 so a line's siblings can't scroll out and
fail the guard open.
- dry-run: fail closed like vcluster-release - only an exact "false" promotes
for real; any other value warn-skips, so an intended preview can't fire a
real retag or release flip.
- oss-repo --latest gate: soft-fail a post-retag `gh release list` blip
(warn + skip --latest) instead of hard-exiting after the irreversible
retags, and keep it distinct from a confirmed backport via a four-state
OSS_IS_LATEST so the homebrew skip reports accurately and stays retryable.
- homebrew patching: rewrite tags/artifacts with literal awk string ops (no
regex/sed interpolation of formula-controlled values), anchor the version
rewrite to the top-level line, validate the download tag shape, and guard
the base64 decode so the advisory path never hard-fails.
- docs: correct the token/permissions guidance in both READMEs and the
action.yml dry-run description.
- tests: cover every fix above; add per-repo list-failure and bad-base64
mock knobs.
* docs(promote-release): regenerate inputs table for fail-closed dry-run description
* docs: run make generate-docs before pushing commits1 parent ed40924 commit c40c1db
10 files changed
Lines changed: 1782 additions & 2 deletions
File tree
- .github
- actions/promote-release
- src
- test
- workflows
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
0 commit comments