Skip to content

refactor: consolidate release pipeline with draft-first pattern#124

Open
jmeridth wants to merge 5 commits intomainfrom
jm_consolidate
Open

refactor: consolidate release pipeline with draft-first pattern#124
jmeridth wants to merge 5 commits intomainfrom
jm_consolidate

Conversation

@jmeridth
Copy link
Copy Markdown
Collaborator

@jmeridth jmeridth commented Apr 6, 2026

Pull Request

Proposed Changes

What

Consolidated the three separate release workflows (release.yaml, release-image.yaml, release-discussion.yaml) into a single release.yaml reusable workflow with a draft-first release pattern. Added optional GoReleaser support for building and uploading Go binaries. Added a publish_release job that publishes the draft only after all artifact, attestation, and discussion jobs succeed.

The deprecated release-image.yaml and release-discussion.yaml workflows 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

  • Breaking change: callers of release-image.yaml or release-discussion.yaml will get a deprecation error with migration instructions
  • Breaking change: update-major-tag input removed — major tag is now always pushed in create_release
  • publish input defaults to true (backwards compatible) but release-drafter always creates a draft first; publish_release job handles the final publish
  • publish_release uses always() with per-job result checks so it runs even when optional jobs are skipped, but blocks on any failure
  • GoReleaser config is validated via yq to ensure release.disable: true is set, preventing conflicts with the release-drafter draft
  • Both GoReleaser and image attestation steps check repo visibility via the GitHub API — private repos get a warning instead of a cryptic failure
  • 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)
  • -F draft=false (capital F) is used in publish_release to send a boolean, not a string

Testing

  • actionlint passes on all modified workflow files
  • Workflow logic validated against the tasktrakkr release workflow which uses the same draft-first pattern in production
  • Full end-to-end testing requires a release trigger (PR merge with release label or workflow_dispatch)

Readiness Checklist

Author/Contributor

  • If documentation is needed for this change, has that been included in this pull request

jmeridth added 2 commits April 5, 2026 21:27
## 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>
@jmeridth jmeridth marked this pull request as ready for review April 6, 2026 03:29
@jmeridth jmeridth requested a review from zkoppert as a code owner April 6, 2026 03:29
…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>
## 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>
@jmeridth jmeridth requested a review from zkoppert April 6, 2026 21:57
Copy link
Copy Markdown
Contributor

@zkoppert zkoppert left a comment

Choose a reason for hiding this comment

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

:shipit:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants