Skip to content

fix: stop purview-release declaring permissions its callers cannot grant - #38

Merged
kieronlanning merged 1 commit into
mainfrom
fix/release-caller-permissions
Oct 27, 2026
Merged

kieronlanning merged 1 commit into
mainfrom
fix/release-caller-permissions

Conversation

@kieronlanning

Copy link
Copy Markdown
Contributor

The break

purview-release.yml declared a workflow-level permissions: block when trusted publishing was wired up in 1c1a6c9. That fails every caller at parse time:

Error calling workflow 'purview-dev/build/.github/workflows/purview-release.yml@main'.
The workflow is requesting 'id-token: write', but is only allowed 'id-token: none'.

GitHub validates a reusable workflow's permissions against the caller statically, before any input is read, and grants id-token as none by default however permissive the repository's default token is. So trusted-publishing: false did not help, and neither did having no permissions: block in the caller.

The fix

Remove the block. A reusable workflow can never elevate its caller, so it granted nothing; all it added was a ceiling that can fail runs but never reduce them. Jobs inherit the caller's token again, as before.

Callers grant what the feature they use needs:

Scope Needed for
contents: write tagging and creating the release
packages: write the GitHub Packages mirror
id-token: write trusted-publishing: true

Also in here

  • Every documented caller snippet updated: README.md, docs/wiki/Release-Flow.md, docs/wiki/Getting-Started.md, skills/purview-build-adoption/SKILL.md. The trusted publishing example granted contents and id-token but not packages, so an opted-in repository following it would publish to nuget.org and then fail on the mirror push.
  • SECURITY.md gains a Commitments entry stating the reusable workflows do not cap the caller's token, and why a block there could only fail runs.
  • AGENTS.md records the invariant next to the existing concurrency and release-branch rules.
  • WorkflowParityTests gains ReleaseWorkflow_DeclaresNoPermissionsBlock (catches job-level blocks too) and a data-driven check that every documented caller block grants the full set, with a "guards the guard" test in the style of the existing one.
  • .editorconfig version marker corrected to the actual SDK version, 1.1.0.

No version bump: this touches workflows, docs and tests only, no shipping tool code. Consumers pick it up from @main on merge.

Verification

dotnet build -c Release --warnaserror clean, just test green (92 + 436 + 9, zero failures), just lint-check and just lint-yaml clean.

The workflow-level permissions block added with trusted publishing broke every
caller at parse time: GitHub validates a reusable workflow's permissions against
the caller statically, before any input is read, and grants id-token as none by
default whatever the repository's default-token setting. Callers failed with
"is only allowed 'id-token: none'" whether or not they used trusted-publishing,
and whether or not they declared a permissions block of their own.

A block there could never have helped. A reusable workflow cannot elevate its
caller, so it grants nothing, and it can only fail runs rather than reduce them.
Remove it and let the jobs inherit the caller's token, as they did before.

Callers now grant what the feature they use needs: contents: write to tag and
release, packages: write for GitHub Packages, id-token: write for trusted
publishing. Every documented caller snippet is updated to match, including the
trusted publishing example, which granted contents and id-token but not packages.

WorkflowParityTests gains two guards: the workflow must declare no permissions
block at any level, and every documented caller block must grant the full set.
@kieronlanning
kieronlanning merged commit 823529d into main Oct 27, 2026
1 check passed
@kieronlanning
kieronlanning deleted the fix/release-caller-permissions branch October 27, 2026 11:36
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