Skip to content

Make the generated action references configurable#535

Open
lahma wants to merge 3 commits into
Fallout-build:mainfrom
lahma:configurable-actions
Open

Make the generated action references configurable#535
lahma wants to merge 3 commits into
Fallout-build:mainfrom
lahma:configurable-actions

Conversation

@lahma

@lahma lahma commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

The generator hard-coded the four actions it emits, so a consumer who wants a newer major — or a SHA pin — has to subclass GitHubActionsAttribute and reimplement step emission. #533 bumped three of them; this makes all four overridable so lagging a release behind never blocks anyone.

Closes #534.

Attribute properties

CheckoutAction, CacheAction, SetupDotNetAction, UploadArtifactAction.

A value containing an @ is a complete reference, emitted as-is; anything else is a bare ref appended to the default action:

CheckoutAction = "v8",                                             // actions/checkout@v8
CacheAction = "0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.4", // SHA pin, version as comment
UploadArtifactAction = "my-org/upload-artifact@v7"                 // fork or drop-in replacement

The trailing # comment is split off before the value is classified, so a slash or an @ inside it changes nothing — that's what makes the SHA-pinning idiom work. A ref that itself contains a / (a branch like releases/v1) reads exactly like an owner/repo, so it takes a leading @"@releases/v1" — and the ambiguous bare form is rejected rather than guessed at. Null or whitespace restores the default.

The reference token must carry no whitespace: emitted into an unquoted uses: scalar, a stray : would otherwise corrupt the whole workflow file. All four are resolved up-front in GetConfiguration, so an override is validated even when its step isn't emitted (caching off, artifacts disabled) — errors name the property and the workflow.

Resolution lives in the step setters, so subclasses and hand-built steps get the same shorthand and validation. GitHubActionsCustomStep.Uses has no default to resolve against, so it now must name its action in full instead of silently emitting an unusable uses: v8.

GitHubActionsDefaults is internal — the properties take a plain string, so nothing a consumer writes needs the type.

actions/cache v4 → v6 (first commit)

Cache was never in #533's scope and sat two majors behind. Separate commit; the diff touches only actions/cache@v4@v6 lines.

  • v5.0.0 — node24 runtime, requires a self-hosted runner ≥ 2.327.1
  • v6.0.0 — dependency update + ESM migration

The path:/key: inputs are unchanged. Consumers on older self-hosted runners set CacheAction = "v4" — the feature demonstrating itself.

Verification

  • dotnet build fallout.slnx clean; dotnet test tests/Fallout.Common.Specs — 168 passed, 0 failed
  • The pre-existing Verify snapshots are untouched by the override commit — that's the assertion that the default path is byte-identical
  • New snapshot action-overrides covers all four properties in one workflow, one per accepted form
  • GitHubActionsActionReferenceSpecs covers the resolver: fallback, bare ref, @-prefixed slash-bearing ref, SHA + comment, comment containing a URL, complete reference, fork, local/container refs, the ambiguous form, comment-only, and YAML-corrupting values — plus validation of a skipped step's override
  • Confirmed the transition-shim generator no longer mirrors GitHubActionsDefaults into Nuke.* as an empty class (it forwards static methods, never consts)
  • Dogfooded: set CheckoutAction = "v8" and a CacheAction override on this repo's build workflow, regenerated, confirmed both lines changed, reverted — regeneration with no overrides leaves .github/workflows untouched

Not in scope

ITaluone's original "heads-up when a new action release ships" — worth a follow-up issue; this removes the urgency but not the need.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Vn8JQZRwdoovPuctD21Wjx

lahma and others added 3 commits July 23, 2026 18:33
The generator's cache pin sat at v4 while Fallout-build#533 moved checkout, setup-dotnet,
and upload-artifact to their current majors — cache was never in that PR's
scope. v6.1.0 is current; v4 is two majors behind.

- v5.0.0 moved to the node24 runtime and requires a self-hosted runner
  >= 2.327.1
- v6.0.0 updated dependencies and migrated to ESM

The `path:` and `key:` inputs the generator emits are unchanged across both,
so the emitted step needs no other edits.

Covers the generated workflows, the hand-written publish-packages-* ones, the
14 Verify snapshots with a cache step, and the docs sample.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vn8JQZRwdoovPuctD21Wjx
The generator hard-coded the four actions it emits, so a consumer who wanted a
newer major — or a SHA pin — had to subclass GitHubActionsAttribute and
reimplement step emission. Two repos migrating off NUKE carried custom step
classes for exactly that (Fallout-build#533).

Each emitting step now carries a normalizing property, forwarded from four new
attribute properties:

    CheckoutAction, CacheAction, SetupDotNetAction, UploadArtifactAction

A value without a '/' is a bare ref appended to the default action name, so
"v8" emits actions/checkout@v8. Anything else is a complete reference emitted
verbatim — a fork, or a commit pin whose version rides along as a trailing
YAML comment. Both forms take that comment, which is what makes the
SHA-pinning idiom readable. Null or whitespace restores the default.

GitHubActionsDefaults exposes every pinned version as a const — name, version,
and the composed reference — so the values are referenceable from an attribute
argument and there is one place to bump them.

Resolution lives in the step setters rather than the attribute, so subclasses
and hand-built steps get the same shorthand and validation.

Additive: with no property set the emitted YAML is byte-identical, which the
untouched Verify snapshots assert.

Closes Fallout-build#534

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vn8JQZRwdoovPuctD21Wjx
Review follow-up on the configurable action references.

Resolution was classifying the override with a bare Contains('/') over the
whole string, so a slash anywhere — in a branch ref like releases/v1, or in a
URL inside the trailing comment — misread the value and then failed the
ref-presence check. That check was itself a substring Contains('@'), which an
'@' in a comment satisfied, letting a reference with no version through to the
workflow file. And with only a CR/LF guard, a value carrying ': ' was emitted
into the unquoted uses: scalar and corrupted the whole document.

Resolution now splits the trailing comment off first and classifies on what
remains: an '@' means a complete reference, otherwise it is a bare ref. A ref
containing a '/' reads exactly like an owner/repo, so it takes a leading '@' to
disambiguate — '@releases/v1' — and the ambiguous form is rejected with a
message naming both fixes rather than guessed at. The reference token must
carry no whitespace, which is what keeps a stray key out of the YAML.

The four overrides are now resolved up-front in GetConfiguration. Cache and
artifact steps are conditional, so their overrides were previously unvalidated
and silently ignored whenever caching or publishing was off — the typo only
surfaced later, attributed to the unrelated edit that re-enabled the step.
Errors name the property and the workflow.

GitHubActionsDefaults is internal, and keeps only the four composed constants.
The properties take a plain string, so nothing a consumer writes needs the
type; the split *Name/*Version constants had no callers outside XML docs. This
also stops the transition-shim generator mirroring it into Nuke.* as an empty
class — it forwards static methods, never consts — which would have given
mid-migration consumers CS0117 on every member, and CS0104 on the bare name.

A custom step's Uses is emitted verbatim and has no default to resolve against,
so it now must name its action in full. Without that, the shorthand documented
for the built-in steps silently produced an unusable 'uses: v8', and a
multi-line value could inject arbitrary keys into the generated workflow.

Also makes the local/container prefix checks ordinal — as a culture-sensitive
comparison the guard's outcome depended on the machine's globalization mode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vn8JQZRwdoovPuctD21Wjx
@lahma
lahma marked this pull request as ready for review July 23, 2026 16:29
@lahma
lahma requested a review from a team as a code owner July 23, 2026 16:29
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.

Should we setup some sort of "heads-up tests"? Meaning: something that notifies us, when a new release is out..

1 participant