Skip to content

Replace release workflows with two-phase release process#788

Open
Boomatang wants to merge 2 commits into
mainfrom
RFC0020_release_process
Open

Replace release workflows with two-phase release process#788
Boomatang wants to merge 2 commits into
mainfrom
RFC0020_release_process

Conversation

@Boomatang

@Boomatang Boomatang commented Jun 25, 2026

Copy link
Copy Markdown
Member

Summary

Replaces the existing ad-hoc release workflows with a structured two-phase release process as defined in the Two-Phase Release Workflow RFC. This introduces a machine-readable release.yaml as the version source of truth, a pre-release workflow that prepares and opens a PR for review, and a consolidated release workflow that handles tagging, image builds, artifact packaging, and GitHub Release creation in a single run.

Changes

New two-phase workflow

The release process is now split into two explicit phases with a human review gate between them:

  • Pre-release (pre-release.yaml): A workflow_dispatch workflow that creates a release branch (if needed), updates release.yaml with the target version, runs make prepare-release, and opens a pull request against the release branch for review.
  • Release (release.yaml): A workflow_dispatch workflow that reads the version from release.yaml, runs smoke tests (verify-manifests, verify-bundle, verify-helm-build, unit tests), creates a git tag, builds and pushes all container images (operator, bundle, catalog, CoreDNS), packages and signs the Helm chart, builds CLI binaries for all platforms, and creates the GitHub Release with all artifacts attached.

Version gate

A new version-gate.yaml CI check validates release.yaml on pull requests to release branches. It verifies that the version is not 0.0.0 on non-main branches and that any declared dependencies have corresponding published releases.

Shared scripts

Version parsing and validation logic is extracted into reusable shell scripts under .github/scripts/:

  • parse-version.sh extracts and validates semver components from release.yaml
  • validate-release-yaml.sh validates version constraints and dependency releases

Consolidated artifact builds

The release workflow now builds all release artifacts in a single coordinated run, including CoreDNS images and CLI binaries which were previously handled separately. CLI binaries are built directly with go build instead of relying on a third-party GitHub Action.

Removed workflows

Three workflows that were triggered by tag pushes or release events are removed, as their functionality is subsumed by the new consolidated release workflow:

  • build-images-for-tag-release.yaml
  • release-helm-chart.yaml
  • upload-cli.yml

Updated documentation

docs/RELEASE.md is rewritten to document the new two-phase process, including step-by-step instructions for minor and patch releases, a table of release artifacts, documentation of release.yaml format, and required GitHub secrets.

Testing

Due to hardcoded routes in the existing workflows, a full end-to-end test in the Kuadrant org is not possible without merging. However, the workflows have been tested in a fork:

The failures in the release workflow run are caused by hardcoded values (registry org, repository owner checks) and are expected to pass when run in the Kuadrant org.

Summary by CodeRabbit

  • New Features
    • Added a guided pre-release and release flow for versioned cuts, including automated branch creation, validation, tagging, and release publishing.
    • Introduced a pull-request “version gate” that checks release configuration changes for correctness.
  • Bug Fixes
    • Added stricter release YAML validation (version format, branch/version alignment, dependency release existence) and safeguards to prevent duplicate/invalid releases.
  • Documentation
    • Updated release instructions to reflect the new two-phase workflow and revised expected artefacts and verification steps.

This is based on RFC 0020.

Signed-off-by: Jim Fitzpatrick <jfitzpat@redhat.com>
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 069c8d36-1fb3-408e-8366-38fd99d9b268

📥 Commits

Reviewing files that changed from the base of the PR and between a9e45a5 and 0e17717.

📒 Files selected for processing (2)
  • .github/workflows/pre-release.yaml
  • .github/workflows/release.yaml
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/pre-release.yaml
  • .github/workflows/release.yaml

📝 Walkthrough

Walkthrough

The PR adds release-version parsing and validation scripts, introduces pre-release and release GitHub Actions workflows, updates release.yaml, replaces older release-specific workflows, and rewrites the release guide around the new two-phase flow.

Changes

Release automation

Layer / File(s) Summary
Version source and parser
release.yaml, .github/scripts/parse-version.sh
release.yaml adds dns-operator.version, and parse-version.sh reads it, validates the version string, derives major/minor/patch, and writes release outputs.
Release YAML gate
.github/workflows/version-gate.yaml, .github/scripts/validate-release-yaml.sh
Pull requests to release-** run the validator, which rejects invalid version states and checks dependency releases with gh release view.
Pre-release flow
.github/workflows/pre-release.yaml
The manual pre-release workflow validates the input version, creates the release branch if needed, updates release.yaml, runs make prepare-release, pushes pre-release-v{VERSION}, and opens a PR into the release branch.
Release readiness and tag
.github/workflows/release.yaml
The release workflow trigger, version parsing, branch/tag checks, test jobs, and annotated v{VERSION} tag creation are grouped into the first release checkpoint.
Container and chart builds
.github/workflows/release.yaml
The release workflow builds the operator, bundle, catalog, and CoreDNS images, then packages and signs the Helm chart artefacts.
CLI release publication
.github/workflows/release.yaml
The release workflow cross-builds the CLI archives, uploads them, downloads the release artefacts, and creates the GitHub Release with gh release create.
Release guide update
docs/RELEASE.md
The release guide is rewritten around the two-phase flow, the version gate, release artefacts, required secrets, and trimmed verification examples.

Sequence Diagram(s)

Version gate flow:

sequenceDiagram
  participant pullRequest
  participant versionGate
  participant validateRelease
  participant yq
  participant gh
  pullRequest->>versionGate: trigger on `release-**` branch changes to `release.yaml`
  versionGate->>validateRelease: run with `github.base_ref` and org name
  validateRelease->>yq: read `release.yaml` version and dependencies
  validateRelease->>gh: check dependency release tags
  validateRelease-->>versionGate: pass or fail with `::error::`
Loading

Pre-release flow:

sequenceDiagram
  participant dispatch
  participant preRelease
  participant setup
  participant prepare
  participant openPr
  participant yq
  participant makePrepare
  participant gh
  dispatch->>preRelease: version and optional source branch
  preRelease->>setup: validate version and compute `release-{MAJOR}.{MINOR}`
  setup->>prepare: create release branch if missing
  prepare->>yq: set `.dns-operator.version` in `release.yaml`
  prepare->>makePrepare: generate release files
  prepare->>openPr: push `pre-release-v{VERSION}`
  openPr->>gh: create PR into the release branch
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Poem

🐰 I hopped through branches, neat and new,
release.yaml lit the way I knew.
With tags and artefacts tucked in line,
The release moon glowed — all was fine.
🥕 Hop, hop, hooray for scripts that shine!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: replacing existing release workflows with a two-phase release process.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch RFC0020_release_process

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/scripts/validate-release-yaml.sh:
- Around line 13-18: The release YAML validation in validate-release-yaml.sh
only rejects 0.0.0 on non-main branches and does not enforce the release-branch
contract. Update the script to reuse .github/scripts/parse-version.sh for the
VERSION value, read its release-branch output, and compare it against BRANCH so
the gate fails when release.yaml does not match the target release branch. Keep
the existing BRANCH and VERSION checks, but add the metadata mismatch validation
in the same validation flow.

In @.github/workflows/pre-release.yaml:
- Around line 90-94: The Install yq step currently downloads an unpinned binary
from releases/latest, which should be hardened. Update the workflow to fetch a
specific yq release version instead of latest, and add a checksum verification
step for the downloaded binary before making it executable. Use the existing
Install yq job step as the place to pin the version and validate integrity with
the upstream SHA256 for that exact release.
- Around line 31-35: The pre-release workflow is using floating action tags in
privileged jobs, which should be pinned to full commit SHAs. Update every
`uses:` entry in the workflow, including the `actions/checkout` steps and the
`actions/setup-go` step, to reference their current stable commit SHA instead of
`@v4`/`@v5`. Keep the existing step structure and inputs intact while changing
only the action references.
- Around line 20-22: The workflow currently grants both permissions at the
workflow level, so every job inherits unnecessary access. Move the permissions
for the pre-release workflow into the specific jobs instead: give `contents:
write` only to the `setup` and `prepare-release` jobs, and give `pull-requests:
write` only to the `open-pr` job. Keep the permissions scoped to the relevant
job definitions in the workflow rather than the top-level `permissions` block.

In @.github/workflows/release.yaml:
- Around line 99-120: The tag job currently creates and pushes the official
v${VERSION} tag before the release artefacts are fully published, so move tag
creation out of the early “Create and push tag” step and into the end of the
workflow or the final publication job. Keep the existing tag-exists check and
git tag/git push logic, but only run it after the image, chart, binary, and
GitHub Release steps have all succeeded so the release tag cannot be published
ahead of an incomplete release.
- Around line 33-35: The release workflow is re-checking out a moving branch
tip, which can cause different jobs to build and publish from different
revisions. Update the release flow so the read-version job captures the exact
commit once using the checked-out HEAD, exposes it as an output, and then have
the later checkout steps in the release jobs use that fixed SHA instead of
inputs.release-branch. Also replace the build metadata references that currently
use github.sha so the tag, smoke tests, and published artefacts all point to the
same frozen commit.
- Line 33: The release workflow still uses mutable action tags, so update every
`uses:` entry in the workflow to a full commit SHA instead of versions like
actions/checkout@v4. Apply this to each action reference in the release
pipeline, keeping the same action names but replacing the tag with the pinned
SHA so the workflow remains stable and reviewable.
- Around line 15-16: The workflow currently grants write access too broadly and
leaves checkout credentials persisted in jobs that do not need push access.
Change the top-level permissions in release.yaml to contents: read, then add a
job-level permissions override with contents: write only for the tag and
create-release jobs. Also update each actions/checkout@v4 step in read-version,
smoke-tests, and all build-* jobs to set persist-credentials: false so no write
token is stored in .git/config.
- Around line 37-41: The release workflow’s yq installation step is pulling an
unpinned binary from the latest release, so replace the wget-based install in
the “Install yq” step with the repository’s existing make yq target. Update the
workflow job to use the same pinned, verified yq build path already defined in
the repo, and remove the direct download/chmod commands so the release process
stays reproducible.

In @.github/workflows/version-gate.yaml:
- Line 17: The workflow currently uses the floating tag reference for
actions/checkout in the version-gate job, so hard-pin that action to a specific
full commit SHA instead of v4. Update the checkout step in version-gate to
reference the exact commit for actions/checkout, keeping the step name and usage
unchanged so the release-gate workflow remains stable and supply-chain
controlled.
- Around line 19-23: The Install yq step in the version-gate workflow is pulling
from releases/latest without integrity verification. Update that step to
download a specific yq release version instead of latest, and add a checksum
verification step against the official SHA256 for that same version before
making it executable. Use the existing “Install yq” step in version-gate.yaml as
the place to pin the version and validate the download.

In `@docs/RELEASE.md`:
- Around line 12-13: The version-gate description in release docs is too broad
and should explicitly mention the path restriction. Update the wording around
the release.yaml source of truth and the version gate so it says the CI check
only runs on pull requests to release branches when release.yaml changes,
matching the actual behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cac64da5-ac56-44b6-8ab1-a3a80804a15a

📥 Commits

Reviewing files that changed from the base of the PR and between f0ef781 and a9e45a5.

📒 Files selected for processing (10)
  • .github/scripts/parse-version.sh
  • .github/scripts/validate-release-yaml.sh
  • .github/workflows/build-images-for-tag-release.yaml
  • .github/workflows/pre-release.yaml
  • .github/workflows/release-helm-chart.yaml
  • .github/workflows/release.yaml
  • .github/workflows/upload-cli.yml
  • .github/workflows/version-gate.yaml
  • docs/RELEASE.md
  • release.yaml
💤 Files with no reviewable changes (3)
  • .github/workflows/build-images-for-tag-release.yaml
  • .github/workflows/upload-cli.yml
  • .github/workflows/release-helm-chart.yaml

Comment thread .github/scripts/validate-release-yaml.sh
Comment thread .github/workflows/pre-release.yaml Outdated
Comment thread .github/workflows/pre-release.yaml
Comment thread .github/workflows/pre-release.yaml
Comment thread .github/workflows/release.yaml Outdated
Comment thread .github/workflows/release.yaml
Comment on lines +99 to +120
tag:
needs: [read-version, smoke-tests]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.release-branch }}
fetch-depth: 0

- name: Create and push tag
env:
VERSION: ${{ needs.read-version.outputs.version }}
run: |
TAG="v${VERSION}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "::error::Tag $TAG already exists"
exit 1
fi
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "Release $TAG"
git push origin "$TAG"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Create the Git tag only after the release artefacts are ready.

Line 120 pushes the official v${VERSION} tag before any image, chart, binary, or GitHub Release step has succeeded. If a later job fails, you are left with a published release tag but an incomplete release, and a fresh rerun will then trip the existing-tag checks.

Because the build jobs do not check out the tag anyway, move tag creation to the end of the workflow or fold it into the final publication step.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 103-106: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 103-103: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yaml around lines 99 - 120, The tag job currently
creates and pushes the official v${VERSION} tag before the release artefacts are
fully published, so move tag creation out of the early “Create and push tag”
step and into the end of the workflow or the final publication job. Keep the
existing tag-exists check and git tag/git push logic, but only run it after the
image, chart, binary, and GitHub Release steps have all succeeded so the release
tag cannot be published ahead of an incomplete release.

validate-release-yaml:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major

🧩 Analysis chain

🌐 Web query:

latest stable commit SHA for actions/checkout@v4

💡 Result:

The latest stable release for actions/checkout@v4 is version v4.3.1 [1][2]. The commit SHA associated with the v4.3.1 tag is 34e114876b [2][3][4]. While there are newer major versions available (such as v6 and v7), v4.3.1 remains the latest release within the v4 release line [1][5][6]. It is recommended to use the tag or the full commit SHA when pinning actions for security and stability [7].

Citations:


🏁 Script executed:

#!/bin/bash
# Verify the snippet in the file matches the concern and check if a SHA-based usage is already present elsewhere
cat -n .github/workflows/version-gate.yaml | grep -A1 -B1 "checkout@"

Repository: Kuadrant/dns-operator

Length of output: 226


🌐 Web query:

actions/checkout v4.3.1 commit SHA

💡 Result:

The commit SHA associated with the v4.3.1 release of the actions/checkout GitHub Action is 34e114876b5c3288d6bf963e6e7368d50f55963b [1][2][3]. It is officially referenced in the repository as 34e1148 [1][2].

Citations:


Hard-pin the action reference to a specific commit SHA.

actions/checkout@v4 currently resolves to a floating tag. Pinning it to the full commit SHA (e.g. 34e114876b5c3288d6bf963e6e7368d50f55963b) prevents upstream tag retags from altering the release-gate behaviour without a repository diff, maintaining strict supply-chain control.

Recommended change
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@34e114876b5c3288d6bf963e6e7368d50f55963b
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b5c3288d6bf963e6e7368d50f55963b
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 17-17: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 17-17: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/version-gate.yaml at line 17, The workflow currently uses
the floating tag reference for actions/checkout in the version-gate job, so
hard-pin that action to a specific full commit SHA instead of v4. Update the
checkout step in version-gate to reference the exact commit for
actions/checkout, keeping the step name and usage unchanged so the release-gate
workflow remains stable and supply-chain controlled.

Source: Linters/SAST tools

Comment on lines +19 to +23
- name: Install yq
run: |
sudo wget -qO /usr/local/bin/yq \
https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
sudo chmod +x /usr/local/bin/yq

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major

Pin yq to a specific version and verify checksum

Fetching yq from releases/latest introduces non-determinism and bypasses integrity checks. The installation should target a specific version (e.g. v4.53.3) and validate the SHA256 checksum against the official release assets.

Show diff
      - name: Install yq
        run: |
-         sudo wget -qO /usr/local/bin/yq \
-           https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
+         YQ_VERSION="v4.53.3"
+         wget -qO yq_linux_amd64 \
+           https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64
+         wget -qO checksums \
+           https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/checksums
+         grep "yq_linux_amd64" checksums | sha256sum -c -
          sudo chmod +x /usr/local/bin/yq
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/version-gate.yaml around lines 19 - 23, The Install yq
step in the version-gate workflow is pulling from releases/latest without
integrity verification. Update that step to download a specific yq release
version instead of latest, and add a checksum verification step against the
official SHA256 for that same version before making it executable. Use the
existing “Install yq” step in version-gate.yaml as the place to pin the version
and validate the download.

Comment thread docs/RELEASE.md
Comment on lines +12 to +13
A `release.yaml` file at the repository root is the machine-readable source of truth for the component version.
A **version gate** CI check validates this file on pull requests to release branches.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Tighten the version-gate scope wording.

This reads as though every PR to a release branch is checked, but the workflow only fires when release.yaml changes. Please call out the path restriction so the docs match the actual gate.

Suggested wording
- A **version gate** CI check validates this file on pull requests to release branches.
+ A **version gate** CI check validates `release.yaml` on pull requests to `release-*` branches when that file changes.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
A `release.yaml` file at the repository root is the machine-readable source of truth for the component version.
A **version gate** CI check validates this file on pull requests to release branches.
A `release.yaml` file at the repository root is the machine-readable source of truth for the component version.
A **version gate** CI check validates `release.yaml` on pull requests to `release-*` branches when that file changes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/RELEASE.md` around lines 12 - 13, The version-gate description in
release docs is too broad and should explicitly mention the path restriction.
Update the wording around the release.yaml source of truth and the version gate
so it says the CI check only runs on pull requests to release branches when
release.yaml changes, matching the actual behavior.

Signed-off-by: Jim Fitzpatrick <jfitzpat@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant