refactor: consolidate release pipeline with draft-first pattern#124
Open
refactor: consolidate release pipeline with draft-first pattern#124
Conversation
## What
Absorbed release-image.yaml and release-discussion.yaml into release.yaml
as optional jobs, controlled by inputs and secrets. Updated test-release.yaml
to call the single consolidated workflow.
## Why
The three release workflows were always chained together sequentially and
shared data flow. Consolidating reduces caller complexity from three
workflow calls to one while keeping each concern as a separate job.
## Notes
- Breaking change for consumers currently calling release-image.yaml or
release-discussion.yaml directly — they need to migrate to the extended
release.yaml inputs/secrets
- release_discussion job always spins up a runner when a tag is created,
then skips at step level if secrets aren't set (can't use secrets in
job-level if conditions)
- image-registry defaults to ghcr.io, image-registry-username defaults
to github.actor, image-platforms defaults to linux/amd64,linux/arm64
- Top-level permissions changed from contents:read to {} per GHA standards
Signed-off-by: jmeridth <jmeridth@gmail.com>
## What Changed release workflow to always create a draft release first, added an optional GoReleaser job that builds binaries and uploads artifacts to the draft release, and added a publish_release job that publishes after all preceding jobs succeed or are skipped. Added repo visibility checks before attestation in both GoReleaser and image jobs. ## Why Repositories with immutable releases enabled cannot modify a release after publishing. The draft-first pattern ensures all artifacts are uploaded before the release becomes visible, and the publish gate prevents partial releases if any build job fails. ## Notes - `publish_release` uses `always()` with per-job result checks so it runs even when optional jobs are skipped, but still blocks on any failure - GoReleaser job creates and pushes the git tag itself since release-drafter with `publish: false` does not create tags - Both GoReleaser and image attestation steps now check repo visibility via the GitHub API — this adds an API call per job but prevents cryptic failures on private repos Signed-off-by: jmeridth <jmeridth@gmail.com>
## What Moved tag creation into create_release job, removed update_major_tag job and its input, added GoReleaser config validation via yq to enforce release.disable: true, fixed -f to -F for boolean in publish_release, made go-version-file configurable, made upload globs resilient with nullglob, consolidated discussion validation into a single input check, and pinned release_image checkout to the tagged commit. ## Why The draft-first pattern changed when git tags are created, but downstream jobs hadn't been updated to account for that. Multiple review agents independently identified race conditions, a silent publish failure, and fragile assumptions that needed fixing before this is mergeable. ## Notes - Removing the `update-major-tag` input is a breaking change for callers that explicitly set it — major tag is now always pushed in create_release - The yq install adds a step to the GoReleaser job; mikefarah/yq action is SHA-pinned but is a new third-party dependency - GoReleaser config validation only checks `release.disable` not `changelog.disable` — the latter is recommended but not enforced since it won't cause conflicts Signed-off-by: jmeridth <jmeridth@gmail.com>
…workflows ## What Restored release-image.yaml and release-discussion.yaml as thin stubs that accept the original inputs/secrets but immediately fail with deprecation errors and migration instructions. Restored docs with CAUTION banners and before/after migration examples. ## Why Existing callers of the removed workflows would get a confusing "workflow not found" error. The stubs provide a clear error message with a link to migration docs, making the transition path obvious. ## Notes - Stubs preserve the original input/secret signatures so callers don't hit schema validation errors before seeing the deprecation message - Each stub spins up a runner just to emit the error — unavoidable since reusable workflows must have at least one job Signed-off-by: jmeridth <jmeridth@gmail.com>
zkoppert
reviewed
Apr 6, 2026
## What Added -f flag to full tag creation and push in the create_release job, and gated the release_discussion job on inputs.publish. ## Why Without -f, workflow reruns fail with "tag already exists" when the full tag was created before a downstream job failed. The short tag already used -f, so this was an inconsistency. The discussion job ran unconditionally, which would announce draft releases publicly before they were published. ## Notes - Force-pushing tags rewrites git history for that tag — acceptable here since the tag points to the same commit on rerun - Callers using `publish: false` will no longer get discussion announcements, which is a behavior change for any existing consumers relying on that Signed-off-by: jmeridth <jmeridth@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
Proposed Changes
What
Consolidated the three separate release workflows (
release.yaml,release-image.yaml,release-discussion.yaml) into a singlerelease.yamlreusable workflow with a draft-first release pattern. Added optional GoReleaser support for building and uploading Go binaries. Added apublish_releasejob that publishes the draft only after all artifact, attestation, and discussion jobs succeed.The deprecated
release-image.yamlandrelease-discussion.yamlworkflows are preserved as stubs that fail with clear migration instructions.Why
The three release workflows were always chained together and shared data flow. Consolidating reduces caller complexity from three workflow calls to one. The draft-first pattern (draft → tags → artifacts → attestation → publish) supports repositories with immutable releases enabled, ensuring all artifacts and attestations are in place before the release becomes visible.
Notes
release-image.yamlorrelease-discussion.yamlwill get a deprecation error with migration instructionsupdate-major-taginput removed — major tag is now always pushed increate_releasepublishinput defaults totrue(backwards compatible) but release-drafter always creates a draft first;publish_releasejob handles the final publishpublish_releaseusesalways()with per-job result checks so it runs even when optional jobs are skipped, but blocks on any failureyqto ensurerelease.disable: trueis set, preventing conflicts with the release-drafter draftrelease_discussionjob always spins up a runner when a tag is created, then skips at step level if secrets aren't set (can't use secrets in job-levelif:conditions)-F draft=false(capital F) is used inpublish_releaseto send a boolean, not a stringTesting
actionlintpasses on all modified workflow filesReadiness Checklist
Author/Contributor