Skip to content

Only allow a release to be dispatched from main - #17

Merged
ipjohnson merged 1 commit into
mainfrom
fix/guard-release-dispatch
Aug 7, 2026
Merged

Only allow a release to be dispatched from main#17
ipjohnson merged 1 commit into
mainfrom
fix/guard-release-dispatch

Conversation

@ipjohnson

Copy link
Copy Markdown
Owner

release.yaml publishes to nuget.org and can be triggered two ways:

  • a v* tag — inherently reviewed, since tags are cut from merged code
  • workflow_dispatchruns against whatever ref is selected in the UI, with no ref check

The dispatch path meant an unreviewed branch could be published to nuget.org, where a version can be unlisted but never removed. The branch selector on a manual dispatch also remembers the last branch used, so this is an easy misfire rather than a theoretical one.

The check sits inside the existing version-resolution step, so a blocked dispatch fails with an explanation rather than skipping silently:

if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ] && [ "${GITHUB_REF}" != "refs/heads/main" ]; then
  echo "::error::Releases may only be dispatched from main. This run is on '${GITHUB_REF_NAME}'."
  exit 1
fi
Trigger Ref Result
push refs/tags/v1.0.0 allowed
push refs/tags/v1.0.0-rc9201 allowed
workflow_dispatch main allowed
workflow_dispatch any other branch blocked

An explicit ref comparison is used rather than github.ref_protected, so the guard does not quietly weaken if branch protection is ever reconfigured.

Follow-up to the publish-path audit: the GitHub Packages job in build-package.yaml was already correctly guarded by github.ref == 'refs/heads/main' && github.event_name == 'push', confirmed against every recent run. This was the one remaining unguarded path.

🤖 Generated with Claude Code

https://claude.ai/code/session_012QPG3s4DPZZ6E4qJ6q8kSs

release.yaml can be triggered by a v* tag or by workflow_dispatch. The tag path
is inherently reviewed, but a manual dispatch runs against whatever ref was
selected in the UI, and that selector remembers the last branch used. An
unreviewed branch could therefore be published to nuget.org, where a version can
be unlisted but never removed.

The check lives in the existing version-resolution step so a blocked dispatch
fails with an explanation rather than skipping silently. Tag pushes are
unaffected, since the guard only applies to workflow_dispatch.

An explicit ref comparison is used rather than github.ref_protected so the guard
does not quietly weaken if branch protection is ever reconfigured.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012QPG3s4DPZZ6E4qJ6q8kSs
@ipjohnson
ipjohnson merged commit 78ef733 into main Aug 7, 2026
2 checks passed
@ipjohnson
ipjohnson deleted the fix/guard-release-dispatch branch August 7, 2026 17:22
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