release-1.6 was cut at v1.6.0 on 2026-07-22, and the finalize fixes
landed on main afterwards, so shipping v1.6.1 from this line would repeat
two defects v1.6.0 hit. Workflows run from the ref they fire on: a promote
PR based on release-1.6 runs THIS file, not main's.
Carries three upstream commits, after which this file is byte-identical
to main, so future backports touching it will not conflict:
01e1e7188 persist-credentials: false on the checkout
f49d54a68 publish the release with the merged changelog as its body
ba67fea7d drop paths-ignore from the trigger
Without the first, the checkout persists GITHUB_TOKEN as an
http.extraheader that silently wins over the app token injected by
git remote set-url. The stable tag then pushes as GITHUB_TOKEN, which
creates no workflow run, so tags.yaml never fires and its
generate-changelog and update-website-docs backstops stay silent. That is
exactly what happened to v1.6.0.
Without the second, the release publishes with the draft's body
("Promoted from vX.Y.Z-rc.N"). The two compound on a maintenance line: a
patch's changelog is committed to release-1.6 and never reaches main, and
update-releasenotes.yaml only watches main, so nothing would ever sync it.
v1.6.1 would ship with placeholder release notes permanently.
The third is latent rather than active — a promote PR carrying only
docs/changelogs/vX.Y.Z.md would be dropped by the filter, producing no
finalize run, no tag and no error. v1.6.1 will carry tag-string rewrites
so it would not have fired, but the filter has no remaining purpose now
that the promote PR always carries a changelog.
actionlint and zizmor clean; no bats suite references this workflow, and
hack/promote-gate-contract.bats does not exist on this line.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Promotion mutated the registry at dispatch time — retagging ~37 images to
vX.Y.Z (+:latest), publishing the stable cozy-installer chart — before the
promote PR's e2e ran and before any merge. An abandoned promotion then left
stable-named bytes for a version that never shipped, and the (correct) image
write-once check wedged any later re-promotion to the same version.
Move every irreversible registry side effect into pull-requests-release.yaml
finalize, which runs only after the promote PR merged (= after its e2e passed),
making promotion atomic. promote-rc.yaml now only stages the release-X.Y.Z
branch, drafts the stable release, and opens the PR; an abandoned promotion
leaves nothing but an overwritable draft + branch.
Folded into the same restructure:
- Cut the api/apps/v1alpha1/vX.Y.Z Go-module tag in finalize, at the stable
tag's commit. It was gated in tags.yaml on `release_exists==false &&
is_stable`, unsatisfiable under promotion (the draft always pre-exists), so
promoted stables never got their submodule tag — a regression for Go
consumers. Finalize is where the stable tag is born, so it cuts the matching
submodule tag write-once (stable only).
- Gate :latest by max-semver. promote-retag.sh now moves :latest only when
MOVE_LATEST=1 (default off); finalize passes it the same make_latest decision
the release gets, so a patch on an older line moves neither the release's
`latest` nor the images' :latest. Added MOVE_LATEST bats coverage.
- Stamp platformVersion into the published stable chart's default values
(uncommitted, at package time) so `helm --install --version X.Y.Z` reports the
stable version in telemetry instead of the rc version baked into the operator
image. Not committed to the branch — that would leak platformVersion=vX.Y.Z
onto main and mis-stamp the next rc.
- promote-rc: serialize all promotions (static concurrency group, not per-rc);
label the PR full-e2e so the full suite runs; scope the rc->stable sed to the
depth-2 values.yaml glob plus the kubernetes .tag files (the build's own write
set); tolerate a leftover draft so re-promotion is not wedged.
- finalize: assert the PR author is cozystack-ci[bot], and run on an ephemeral
oracle-vm runner (needs skopeo/helm).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Address review feedback from coderabbitai on
.github/workflows/pull-requests-release.yaml: the fast-forward catch block
treated every updateRef failure as a recoverable warning, so an auth, API, or
malformed-ref error would silently leave the maintenance branch stale. Warn
only on the 422 non-fast-forward rejection and fail the job otherwise.
Assisted-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Replace the force-retag operations in the release flow with write-once /
compare-before-force guards, so published tags are never moved (the cache
poisoning on api/apps/v1alpha1 via the Go module proxy, plus SBOM/scanner
drift, all stem from moved tags).
- tags.yaml: the api/apps/v1alpha1 submodule tag is now write-once (create
if absent, no-op if unchanged, fail loudly if it would move). The
release-X.Y.Z staging branch uses a compare-before-force guard (skip when
unchanged; force+log only when the staging branch genuinely moves — it is
a mutable staging ref, not an artifact). The build is now skipped when a
release (draft OR published) already exists, so a stable tag prepared by
promotion is never rebuilt here.
- pull-requests-release.yaml: the stable tag is created at the PR merge
commit with a write-once guard instead of `git tag -f`/`git push -f` —
the merge commit did not exist before the PR opened, so force is
impossible by construction. The maintenance branch is fast-forward-only
(no `updateRef force:true`); a non-FF update warns and is left for a
maintainer.
- auto-release.yaml: removed. Stable patch tags were auto-bumped nightly via
delete-recreate; stable now appears only through an explicit rc promotion.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Raise the OpenSSF Scorecard Token-Permissions and Pinned-Dependencies
checks (both currently 0/10) ahead of CNCF Incubation due diligence:
- Declare a top-level read-only default (permissions: contents: read) in
every workflow, plus an explicit per-job permissions: block that grants
only the writes that job actually needs. Workflows that previously held
write scopes at the workflow level (backport, pr-labeler, pr-size,
stale) now hold them at job level instead.
- Pin every GitHub-owned and third-party action to a full commit SHA with
a version comment. scorecard.yml was already pinned in #2721.
No triggers, job graph, or step logic change; only token scopes and
action refs. Self-hosted release jobs keep the exact write scopes they
already declared.
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Assisted-By: Claude <noreply@anthropic.com>
When the git tag was deleted and re-created between draft creation
and merge, GitHub orphaned the draft release: tag_name became
"untagged-<hash>" while `name` was preserved. The finalize step
looked up the draft by tag_name and threw `Draft release for <tag>
not found`, blocking the release.
Fall back to matching by `name` only when the candidate is the
GitHub-orphaned form (tag_name matches /^untagged-/), repair the
draft's tag_name via updateRelease, then publish. Hard-fail is
preserved when neither lookup matches.
Same failure recurred on the v1.4.1 finalize run; this commit
prevents recurrence but does not auto-recover the stuck v1.4.2
draft.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
The Publish draft release step used the workflow's default GITHUB_TOKEN,
which does not return draft releases from listReleases — so the script
failed with "Draft release for vX.Y.Z not found" and every release tag
had to be undrafted manually.
This same bug was fixed once before in 4db55ac5eb ([ci] Add Github
token to fetch draft releases) by adding github-token: GH_PAT to the
listing step. It was lost during the refactor in 66a756b6 that combined
the separate listing/flags/publish steps into a single Publish step,
and not restored when f96b2da1 migrated remaining github-token usages
from GH_PAT to the cozystack-ci App token.
Add github-token: app-token.outputs.token to the merged step so it
shares the same auth as the rest of the workflow.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
The cozystack-ci[bot] noreply email with brackets is rejected by DCO
probot as an invalid email address, causing release PRs to fail DCO
checks.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Replace the cozystack-bot personal access token (GH_PAT) with a
GitHub App (cozystack-ci) installation token across all CI/CD
workflows. This improves security by using short-lived, scoped
tokens instead of a long-lived PAT tied to a user account.
Changes:
- tags.yaml: use app token in all 3 jobs (prepare-release,
generate-changelog, update-website-docs)
- auto-release.yaml: use app token for daily patch releases
- pull-requests-release.yaml: use app token for release finalization
The cozystack-ci GitHub App (ID: 3297617) is installed org-wide
with contents:write and pull-requests:write permissions. Secrets
COZYSTACK_CI_APP_ID and COZYSTACK_CI_PRIVATE_KEY are set at org
level.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace unreliable getLatestRelease() API with semver-based max version
detection. After publishing a backport release, explicitly restore the
latest flag on the highest semver release to handle cases where GitHub
API ignores make_latest: 'false'.
Also remove dead code (unused steps) from tags.yaml workflow.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
- Skip long workflows on PRs that only change files inside `./docs` directory.
- Not applicable to other docs in this repository, such as `packages/apps/**/*.md`,
as they're part of the build.
Signed-off-by: Nick Volynkin <nick.volynkin@gmail.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Expanded support for prerelease tags to include "alpha" and "beta"
suffixes (e.g., `-alpha.1`, `-beta.2`) in addition to "rc".
- **Style**
- Improved formatting and consistency in comments and log messages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This change also fixes `finalizing release` workflow
https://github.com/cozystack/cozystack/pull/890#issuecomment-2830525103
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Chores**
- Updated release tag validation to require a dot between "rc" and the
number (e.g., `v0.31.5-rc.1` instead of `v0.31.5-rc1`).
- Adjusted error messages to reflect the new release tag format.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Chores**
- Improved reliability of GitHub Actions workflows by ensuring only one
job per pull request or branch runs at a time. If a new workflow run is
triggered, any previous in-progress runs for the same group will be
automatically canceled, preventing overlapping executions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->