Skip to content

Publish to nuget.org when Version Increment completes - #8

Merged
TorinKS merged 1 commit into
fix/pin-vs2022-toolchainfrom
feat/auto-publish-after-version-increment
Aug 30, 2026
Merged

Publish to nuget.org when Version Increment completes#8
TorinKS merged 1 commit into
fix/pin-vs2022-toolchainfrom
feat/auto-publish-after-version-increment

Conversation

@TorinKS

@TorinKS TorinKS commented Aug 30, 2026

Copy link
Copy Markdown
Owner

Stacked on #7.

Why the tag push does not reach the publisher

increment-version.yaml pushes its tag with secrets.GITHUB_TOKEN, and GitHub deliberately does not start workflows from events created with that token — the recursion guard. So publish-nuget.yaml's push: tags: ['v*'] trigger never fires for an automated release, and every version bump would need a manual dispatch afterwards.

Approach, and the two rejected ones

workflow_run. It fires when Version Increment finishes, which is not a token-created event, so the chain completes without storing a PAT.

Rejected: duplicating the publish steps into increment-version.yaml. The Trusted Publisher policy on nuget.org is bound to this workflow's file name, so publishing from another file is refused.

Rejected: converting this into a reusable workflow called from Version Increment. That splits the OIDC claim — workflow_ref becomes the caller, job_workflow_ref the callee — and which of the two nuget.org validates against is not something worth discovering through a live publish.

Details worth noting

  • workflow_run fires regardless of outcome, so the job is skipped unless github.event.workflow_run.conclusion == 'success'. A failed version bump must not reach the publisher.
  • For workflow_run, checkout pins github.event.workflow_run.head_sha rather than the branch tip, which may already have moved.
  • The publish decision now lives in one gate step. Three separate steps depended on the same condition and could have drifted; they now read one output.

Interaction with the exact-tag guard from #6

The guard still applies. On the workflow_run path, HEAD is the commit Version Increment just tagged, so git describe --exact-match succeeds and the version resolves clean, without the -dev suffix. If the tag were somehow absent, publishing is refused rather than producing a mislabelled package.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XnzCrAM46SttZnU6msznyN

Version Increment pushes its tag with GITHUB_TOKEN, and events created with
that token deliberately do not start other workflows. The tag-push trigger
therefore never fires for an automated release, and publishing had to be
dispatched by hand after every version bump.

Trigger on workflow_run instead. It fires when Version Increment finishes
rather than from a token-created event, so no personal access token has to
be stored to make the chain work.

Two alternatives were rejected. Duplicating the publish steps inside
increment-version.yaml would break Trusted Publishing, because the policy on
nuget.org is bound to this workflow's file name. Converting this into a
reusable workflow called from there splits the OIDC claim between
workflow_ref and job_workflow_ref, and which of the two nuget.org validates
is not something to discover through a live publish.

Also:

- the job is skipped unless the triggering run succeeded, since workflow_run
  fires on failure too
- for workflow_run the checkout pins the triggering run's head_sha rather
  than the branch tip, which may already have moved
- the publish decision moves into one gate step, so the three conditional
  steps that depend on it cannot drift apart

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnzCrAM46SttZnU6msznyN
@TorinKS
TorinKS merged commit 6c6bca6 into fix/pin-vs2022-toolchain Aug 30, 2026
@TorinKS
TorinKS deleted the feat/auto-publish-after-version-increment branch August 30, 2026 15:29
TorinKS added a commit that referenced this pull request Aug 30, 2026
* ci: build the native library on VS 2022, not on whatever is latest

WinDevices.dll links the dynamic CRT. dumpbin on the published 0.1.1 shows
MSVCP140.dll, VCRUNTIME140.dll and VCRUNTIME140_1.dll among its imports,
because CMake defaults to /MD and nothing overrides it.

The toolset that builds it therefore sets the minimum Visual C++
Redistributable every consumer's machine must carry: v14x runtimes are
compatible forward, not backward. windows-latest silently became
windows-2025-vs2026, so 0.1.1 was built with the VS 2026 toolset and shipped
inside a package consumed by components that are built, and deployed, on
VS 2022. It loaded during verification only because this workstation has a
current redistributable installed, which a clean endpoint does not.

Pin the three jobs that compile to windows-2022. The reporting job and the
git-only version job keep windows-latest; neither produces a binary.

Also drop the CMake 3.29 pin from increment-version.yaml, which would have
failed the same way build-test.yaml did: install.cmd configures without a
generator, and a CMake older than the image's Visual Studio falls back to
NMake Makefiles.

Static linking of the CRT would remove the redistributable question
entirely, but it needs the C API checked for heap ownership crossing the
DLL boundary first, so it is left to its own issue.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnzCrAM46SttZnU6msznyN

* feat(ci): publish to nuget.org when Version Increment completes (#8)

Version Increment pushes its tag with GITHUB_TOKEN, and events created with
that token deliberately do not start other workflows. The tag-push trigger
therefore never fires for an automated release, and publishing had to be
dispatched by hand after every version bump.

Trigger on workflow_run instead. It fires when Version Increment finishes
rather than from a token-created event, so no personal access token has to
be stored to make the chain work.

Two alternatives were rejected. Duplicating the publish steps inside
increment-version.yaml would break Trusted Publishing, because the policy on
nuget.org is bound to this workflow's file name. Converting this into a
reusable workflow called from there splits the OIDC claim between
workflow_ref and job_workflow_ref, and which of the two nuget.org validates
is not something to discover through a live publish.

Also:

- the job is skipped unless the triggering run succeeded, since workflow_run
  fires on failure too
- for workflow_run the checkout pins the triggering run's head_sha rather
  than the branch tip, which may already have moved
- the publish decision moves into one gate step, so the three conditional
  steps that depend on it cannot drift apart


Claude-Session: https://claude.ai/code/session_01XnzCrAM46SttZnU6msznyN

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(ci): do not check out an arbitrary commit in the publishing job

CodeQL flagged actions/untrusted-checkout at high severity against the
checkout I added for the workflow_run path.

It is right. A workflow_run job is privileged: it holds id-token: write and
can exchange it for a nuget.org publishing token. This job then builds what
it checks out, running cmake and dotnet over that tree. Pinning
workflow_run.head_sha means executing code from a commit this workflow did
not choose, in exactly the context that can publish a package. Version
Increment is dispatch-only today, so reaching it needs write access, but the
shape is an escalation path to the package feed and should not exist.

Take the default checkout instead, which is the default branch and therefore
trusted. Version Increment tags a commit on that branch, so the tag is
present when this runs. If the branch has moved past the tag in between, the
exact-tag guard refuses to publish rather than releasing a mislabelled
package - the correct failure, and the reason that guard was added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XnzCrAM46SttZnU6msznyN

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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