Cutting a release
This page describes how to publish a new GitOps Promoter release. The process is mostly automated — you trigger it, review the draft, and publish.
Prerequisites
- Maintainer access to the repository (merge to
main). - Repository secrets
QUAY_USERNAMEandQUAY_TOKENconfigured for the Quay.io container registry.
Steps
1. Dispatch the version-bump workflow
Go to the Bump Docs Manifests workflow, click Run workflow, and enter the new version number (e.g. 1.2.3, without the v prefix).
This updates manifest version references in the docs and opens a pull request titled docs: bump manifest versions to v1.2.3.
2. Merge the version-bump PR
Review and merge the PR to main. The commit message must contain docs: bump manifest versions to v<version> — the automated PR already sets this.
3. Automated tag creation and release dispatch
Merging triggers create-release-tag.yaml, which:
- Extracts the version from the commit message.
- Creates and pushes the git tag (e.g.
v1.2.3). - Dispatches the
release.yamlworkflow on the tag ref, so the Sigstore OIDC signing identity isrefs/tags/v1.2.3.
4. GoReleaser builds the release
The release workflow runs GoReleaser, which:
- Builds multi-arch binaries (linux/darwin, amd64/arm64).
- Builds and pushes multi-arch Docker images to
quay.io/argoprojlabs/gitops-promoter:<tag>. - Signs container images and checksums with cosign (keyless / Sigstore).
- Creates a draft GitHub Release with binaries, checksums, install manifest, and the Argo CD extension archive.
5. Review and publish the draft release
Go to Releases on GitHub. The new release appears as a draft. Review the generated changelog, then click Publish release.
Note
RC / pre-release versions (e.g. v1.2.3-rc.1) are not supported. The release pipeline only handles stable vX.Y.Z versions.
Warning
If immutable releases are enabled on the repository, the release and its tag become permanently locked once published. Make sure everything looks right before publishing.
Why workflow_dispatch instead of a tag trigger?
A simpler design would be: push the tag in the first workflow, and have the release workflow trigger on push: tags: ['v*']. The reason we don't do this is that GitHub suppresses most events created by GITHUB_TOKEN to prevent recursive workflow loops. A tag pushed by one workflow's GITHUB_TOKEN will not trigger a push: tags event in another workflow. The only events exempt from this restriction are workflow_dispatch and repository_dispatch.
We use workflow_dispatch (via gh workflow run release.yaml --ref <tag>) because it lets us specify the tag as the workflow ref. This means the release workflow runs in the context of refs/tags/vX.Y.Z, and the GitHub OIDC token — used by cosign for keyless Sigstore signing — carries that tag as the certificate identity. repository_dispatch cannot do this because it always runs on the default branch (refs/heads/main), which would produce the wrong signing identity.
Retrying a failed release
If the release workflow fails partway through, you can re-trigger it without re-creating the tag:
gh workflow run release.yaml --ref v1.2.3
The draft release is mutable until published, so GoReleaser can overwrite partial artifacts.
latest images
The release-latest.yaml workflow runs on every push to main (independently of the release process) and pushes quay.io/argoprojlabs/gitops-promoter:latest. These images are signed with a certificate identity of refs/heads/main.
Verifying a release
See the verification instructions in the footer of each GitHub Release. In short:
cosign verify \
--certificate-identity 'https://github.com/argoproj-labs/gitops-promoter/.github/workflows/release.yaml@refs/tags/v1.2.3' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
quay.io/argoprojlabs/gitops-promoter:v1.2.3