Skip to content

feat(release): split release workflow into least-privilege variants - #180

Draft
jmeridth wants to merge 1 commit into
mainfrom
split-release-workflows-149
Draft

feat(release): split release workflow into least-privilege variants#180
jmeridth wants to merge 1 commit into
mainfrom
split-release-workflows-149

Conversation

@jmeridth

@jmeridth jmeridth commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Closes #149

What/Why

Callers of release.yaml are forced to grant all six permission scopes because GitHub validates nested job permissions at parse time, before if conditions are evaluated. This adds least-privilege release variants composed from shared building blocks, so callers only grant what they actually use:

Workflow Required permissions
release-minimal.yaml contents: write, pull-requests: read
release-goreleaser.yaml contents: write, pull-requests: read
release-goreleaser-attest.yaml + id-token: write, attestations: write
release-container.yaml + packages: write
release-container-attest.yaml + packages: write, id-token: write, attestations: write
release-discussion.yaml (revived, chainable) contents: read, discussions: write

Each variant unconditionally nests new release-draft.yaml / release-publish.yaml building blocks (single source of truth, also callable directly for custom compositions), preserving the draft -> build -> publish ordering that supports immutable releases. release.yaml is unchanged for backwards compatibility, and release-image.yaml stays a deprecated stub. Variants expose a new published output so chained jobs (like the discussion announcement) only fire after a successful publish.

Proof it works

  • actionlint passes on all new and modified workflows.
  • test-release.yaml now dogfoods release-container-attest.yaml plus the chained release-discussion.yaml, so this repository's own release exercises the new path (draft block, container build, attestation, publish block, discussion) on merge to main.

Risk + AI role

medium -- switches this repository's own release path via test-release.yaml. Build/publish logic was extracted verbatim from release.yaml to minimize behavior drift. AI-generated with human-directed design decisions.

Review focus

  • Publish gating in each variant: skipped or failed build jobs must block publish, while attest_sboms may legitimately skip (private repo or no SBOMs).
  • The test-release.yaml switch: the next merge to main releases via release-container-attest.yaml instead of release.yaml.

Readiness Checklist

Author/Contributor

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

Relates to #149

## What/Why

Callers of release.yaml were forced to grant all six permission scopes
because GitHub validates nested job permissions at parse time, before
if conditions. Adds least-privilege variants (minimal, goreleaser,
goreleaser-attest, container, container-attest) composed from shared
release-draft/release-publish building blocks, and revives
release-discussion.yaml as a chainable workflow. release.yaml is
unchanged for back-compat.

## Proof it works

actionlint passes on all workflows. test-release.yaml now dogfoods
release-container-attest.yaml plus the chained discussion workflow,
so this repo's own release exercises the new path on merge.

## Risk + AI role

medium -- touches this repo's own release path via test-release.yaml.
Workflow logic extracted verbatim from release.yaml. AI-generated
(Claude Fable 5), human-directed design decisions.

## Review focus

- Publish gating conditions in each variant (skipped/failed build jobs
  must block publish; attest_sboms may legitimately skip)
- test-release.yaml switch: next merge to main releases via the new
  variant instead of release.yaml

Signed-off-by: jmeridth <jmeridth@gmail.com>
@jmeridth jmeridth self-assigned this Aug 7, 2026
@github-actions
github-actions Bot marked this pull request as ready for review August 7, 2026 03:31
@github-actions
github-actions Bot requested a review from zkoppert as a code owner August 7, 2026 03:31
@jmeridth
jmeridth marked this pull request as draft August 7, 2026 03:45
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.

release.yaml: callers forced to grant unused permissions for conditionally-skipped jobs

1 participant