You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(promote-release): promote the caller's own release and baseline on the Latest pointer
Two changes needed for a caller that publishes stable cuts with the GitHub
"None" label (release.prerelease: auto + make_latest: false), where nothing
ever flips the release afterwards:
- New promote-self input: also promotes the CALLER repo's release (unset
pre-release, set Latest), gated by the same backport check as :latest. Off
by default, and only an exact "true" enables it, so a caller whose releases
already go out promoted is unaffected.
- The unscoped backport gate now compares against the release flagged
isLatest, i.e. the last one actually promoted, instead of the newest
isPrerelease == false. Under prerelease: auto an un-promoted stable cut is
already non-prerelease, so the old baseline would refuse to advance :latest
behind any newer un-promoted cut and strand it. Re-promoting the release
that is already Latest now resolves as promotable too, so a partially
failed promotion can simply be re-run. The line-scoped :{major}.{minor}
gate keeps its shape comparison: GitHub has no per-line Latest pointer.
Also document that this belongs on workflow_dispatch rather than
release: types: [released] - with prerelease: auto that event fires at build
time, and it resolves its workflow file from the release's own tag ref, so it
never fires for tags on maintenance branches that predate the file.
feat(release-notification): add a promote reminder and link both releases
- needs_promotion + promote_workflow render a GitHub label field naming the
promote workflow, since the release build is the only thing that tells the
Release Captain a step is still outstanding. is_prerelease was declared but
unused; it now feeds the same field. Fallback is the neutral "Release", so
callers passing neither flag get no guessed label.
- paired_repo links the release and changelog for BOTH repos. One vCluster cut
publishes two releases, and the single link pointed at the private pro repo,
the less useful of the two.
All three are opt-in with defaults that reproduce the previous output exactly,
so vCluster Platform banners are unchanged.
Refs DEVOPS-1270
| docker-username | string | true || Username paired with github-token for the <br>GHCR login (GHCR checks the token, but docker/login-action requires a username value). |
59
-
| dry-run | string | false |`"false"`| Fail-closed: a real promotion runs only <br>on an exact "false" (the default, so the release:released trigger still promotes for real). Any <br>other value ("true", a typo, etc.) is a dry-run <br>that only prints the planned retags/promotion. |
70
+
| dry-run | string | false |`"false"`|Fail-closed: a real promotion runs only <br>on an exact "false" (the default, so a plain dispatch still promotes for real). Any <br>other value ("true", a typo, etc.) is a dry-run <br>that only prints the planned retags/promotion.|
60
71
| github-token | string | true || Token with GHCR write:packages, and contents:write <br>on oss-repo and homebrew-tap-repo if set. |
61
72
| homebrew-formula-paths | string | false |`"[]"`| JSON array of formula file paths <br>within homebrew-tap-repo to update, e.g. ["Formula/vcluster.rb"]. <br>Required if homebrew-tap-repo is set. |
62
73
| homebrew-tap-repo | string | false || owner/repo of a Homebrew tap to <br>promote (e.g. loft-sh/homebrew-tap). Requires oss-repo to be <br>set, since checksums come from its <br>release. Leave empty to skip. |
63
74
| images | string | true || JSON array of image entries to <br>retag, each `{"image": "ghcr.io/loft-sh/x", "suffix": ""}` (suffix optional, default <br>""). For each entry, copies `<image>:<version><suffix>` <br>to `<image>:latest<suffix>`, `<image>:<major><suffix>`, and `<image>:<major>.<minor><suffix>`. The <br>suffix is also how per-arch moving <br>tags are promoted: an entry with <br>suffix `-amd64` retags `<image>:<version>-amd64` (a bare single-platform manifest) to <br>`<image>:latest-amd64` etc. crane preserves its digest, <br>so its cosign signature stays valid. |
| promote-self | string | false |`"false"`| Set to "true" to also promote <br>the CALLER repo's own <version> release <br>(unset pre-release, set Latest). Required when the caller publishes <br>stable cuts with the GitHub "None" <br>label, since nothing else ever flips <br>them. Latest is gated by the <br>same backport check as :latest. |
65
77
| version | string | true || The promoted release tag, e.g. v0.37.1. |
66
78
67
79
<!-- AUTO-DOC-INPUT:END -->
@@ -70,12 +82,15 @@ as an all-or-nothing skip — a formula has no line-scoped equivalent to
70
82
71
83
```yaml
72
84
on:
73
-
release:
74
-
types: [released]
85
+
workflow_dispatch:
86
+
inputs:
87
+
version:
88
+
description: "Stable version to promote, e.g. v0.37.1"
Copy file name to clipboardExpand all lines: .github/actions/promote-release/action.yml
+28-15Lines changed: 28 additions & 15 deletions
Original file line number
Diff line number
Diff line change
@@ -8,27 +8,35 @@ description: |
8
8
`docker buildx imagetools create` because imagetools re-wraps a bare
9
9
single-platform manifest in a new index (changing its digest, orphaning the
10
10
signature); crane preserves the digest for both single-platform manifests
11
-
(per-arch tags) and multi-arch indexes. Optionally also promotes the paired
12
-
public release in a companion repo (unsets prerelease, sets latest).
11
+
(per-arch tags) and multi-arch indexes. Optionally also promotes the caller's
12
+
own release (`promote-self`) and the paired public release in a companion
13
+
repo (unsets prerelease, sets latest).
13
14
14
-
Wire this from `on: release: types: [released]` on the repo that owns the
15
-
moving tags. That event only fires when a human -- not GITHUB_TOKEN/a bot --
16
-
flips a release from pre-release to a full release (verified live for
17
-
DEVOPS-1083); a bot-authored release publish never triggers it, so there is
18
-
no risk of the build itself re-entering this action.
15
+
Wire this from `on: workflow_dispatch` on the repo that owns the moving tags,
16
+
with the version to promote as an input. A `release: types: [released]`
17
+
trigger is NOT suitable for a caller that publishes stable cuts as the GitHub
18
+
"None" label (`release.prerelease: auto`): `released` fires at build time for
19
+
such a cut, which would self-promote it unvetted. It is also resolved from the
20
+
release's own tag ref, so it never fires for tags on maintenance branches that
21
+
predate the workflow file, whereas `workflow_dispatch` always runs the default
22
+
branch's copy and therefore covers every release line.
19
23
20
24
Only acts on a stable `vX.Y.Z` version (no prerelease suffix); any other
21
25
shape is a no-op, since moving tags and "latest" promotion aren't meaningful
22
26
for -rc/-alpha/-next cuts.
23
27
24
28
Backport-safe: before advancing `:latest`/`:{major}` (or `--latest` on
25
-
oss-repo), checks whether `version` is actually the newest stable release
26
-
on the caller's own repo (`GITHUB_REPOSITORY`, set automatically by
27
-
Actions) / on `oss-repo` respectively. Promoting an older line's patch
28
-
after a newer stable is already `:latest` only advances `:{major}.{minor}`
29
-
(scoped to that line), never `:latest`/`:{major}` backwards. A failure to
30
-
even list releases fails the run closed rather than risking a silent
31
-
downgrade.
29
+
oss-repo / the caller's own release), checks whether `version` is newer than
30
+
the release currently flagged Latest on the caller's own repo
31
+
(`GITHUB_REPOSITORY`, set automatically by Actions) / on `oss-repo`
32
+
respectively. The baseline is that Latest pointer, not "newest
33
+
non-prerelease": under `release.prerelease: auto` an un-promoted stable cut is
34
+
already non-prerelease, so only the Latest flag records what `:latest`
35
+
actually tracks. Promoting an older line's patch after a newer stable is
36
+
already `:latest` only advances `:{major}.{minor}` (scoped to that line),
37
+
never `:latest`/`:{major}` backwards. A failure to even list releases fails
38
+
the run closed rather than risking a silent downgrade. Re-promoting the
39
+
current Latest is allowed, so a partially failed promotion can be re-run.
32
40
33
41
Optionally also promotes a Homebrew tap: a metadata patch, not a rebuild.
34
42
A formula's per-platform sha256 values are exactly what's already in
@@ -54,6 +62,10 @@ inputs:
54
62
`<image>:latest-amd64` etc. crane preserves its digest, so its cosign
55
63
signature stays valid.
56
64
required: true
65
+
promote-self:
66
+
description: 'Set to "true" to also promote the CALLER repo''s own <version> release (unset pre-release, set Latest). Required when the caller publishes stable cuts with the GitHub "None" label, since nothing else ever flips them. Latest is gated by the same backport check as :latest.'
67
+
required: false
68
+
default: 'false'
57
69
oss-repo:
58
70
description: 'owner/repo whose matching <version> release should also be promoted (prerelease unset, latest set). Leave empty to skip.'
59
71
required: false
@@ -73,7 +85,7 @@ inputs:
73
85
required: false
74
86
default: '[]'
75
87
dry-run:
76
-
description: 'Fail-closed: a real promotion runs only on an exact "false" (the default, so the release:released trigger still promotes for real). Any other value ("true", a typo, etc.) is a dry-run that only prints the planned retags/promotion.'
88
+
description: 'Fail-closed: a real promotion runs only on an exact "false" (the default, so a plain dispatch still promotes for real). Any other value ("true", a typo, etc.) is a dry-run that only prints the planned retags/promotion.'
0 commit comments