Skip to content

ci: tag-triggered + multi-channel fan-out for release.yml - #288

Merged
ChrisonSimtian merged 2 commits into
mainfrom
ci/release-yml-tag-triggered-multi-channel
May 29, 2026
Merged

ci: tag-triggered + multi-channel fan-out for release.yml#288
ChrisonSimtian merged 2 commits into
mainfrom
ci/release-yml-tag-triggered-multi-channel

Conversation

@ChrisonSimtian

Copy link
Copy Markdown
Collaborator

Draft for review — opens as draft because this is the biggest single piece of milestone #13 and deserves a careful eyeball before any tag fires.

Implements #274 per RFC #267. Replaces the workflow_dispatch-only stopgap (#268) with the proper release-branch + multi-channel CD shape.

Trigger

  • Primary: push: tags: ['v*']. The validate-ref job verifies the tag is reachable from a release/v* branch and fails fast otherwise. Random tags on main or feature branches can't accidentally fire a release.
  • Fallback: workflow_dispatch with a required tag input — for re-running after a transient publish-API failure. The workflow checks out that tag and re-runs the publish chain. --skip-duplicate keeps re-runs idempotent.

Job graph

validate-ref  ──→  test-and-pack  ──→  publish-nuget-org      (env: nuget-org, gated)
                                  ──→  publish-github-packages (env: github-packages)
                                  ──→  publish-github-releases (env: github-releases)

test-and-pack runs dotnet fallout Test Pack once and uploads output/packages/*.nupkg as an Actions artifact. Each publish job downloads it and pushes its slice.

Channel routing

Channel What ships Where
Tier 1 — nuget.org Fallout.*.nupkg only https://api.nuget.org/v3/index.json (gated by nuget-org env approval)
Tier 2 — GitHub Packages Nuke.*.nupkg (transition shims, per #47) https://nuget.pkg.github.com/ChrisonSimtian/index.json
Bundled — GitHub Releases All *.nupkg attached to tag's release page gh release create --generate-notes (or --clobber if release exists)

Nuke.* IDs are owned by the original NUKE maintainer on nuget.org — routing them to GitHub Packages instead preserves the existing behaviour (#47).

Tag protection (applied separately, in scope of this issue)

Created a repository ruleset (id 17017817, visible at https://github.com/ChrisonSimtian/Fallout/rules/17017817):

  • Target: tags matching refs/tags/v*
  • Rules: creation, deletion, update
  • Bypass actors: RepositoryRole 5 (admin) — currently only @ChrisonSimtian
  • Enforcement: active

Combined with the env approval gate on nuget-org, production releases are now gated at two layers: who-can-tag + who-can-approve-publish.

Build.cs deliberately not touched

The existing IPublish.Publish target and ICreateGitHubRelease from build/Build.cs are bypassed by CI — the workflow now calls dotnet nuget push and gh release create directly. Targets stay around for local invocation; cleanup/consolidation is a follow-up worth tracking once the model has settled.

Test plan

Tricky because we can't really dry-run a publish without affecting the world. Verification approach:

  • YAML parses (CI will run on this PR for the YAML-lint side).
  • Tag-protection ruleset created and verified — current_user_can_bypass: always for Chris.
  • Once merged, smoke test via workflow_dispatch with tag: <some-existing-tag>. The validate-ref step is skipped on dispatch, so this exercises test-and-pack → publish-* end-to-end. Approve at the nuget-org gate to confirm the wiring; --skip-duplicate makes the nuget.org push safe even on an already-published tag.
  • First real release (whenever you're ready): bump a patch on release/v11, push a v11.0.X tag, watch the fan-out.

Open follow-ups

🤖 Generated with Claude Code

Refactors .github/workflows/release.yml from the workflow_dispatch-only
stopgap (#268) into the proper release-branch model shape per RFC #267
and milestone #13.

## Trigger

- push: tags 'v*' is the primary trigger. The new validate-ref job
  verifies the tag is reachable from a release/v* branch and fails fast
  otherwise.
- workflow_dispatch with a required `tag` input remains as a fallback
  for re-runs after a transient API failure. --skip-duplicate on each
  publish step keeps re-runs idempotent.

## Job graph

  validate-ref (skipped on workflow_dispatch)
      ↓
  test-and-pack (Test + Pack via `dotnet fallout`, uploads
                 output/packages/*.nupkg as Actions artifact)
      ↓ ↓ ↓  (fan-out)
  publish-nuget-org       (env: nuget-org,       Fallout.*.nupkg → nuget.org,    gated)
  publish-github-packages (env: github-packages, Nuke.*.nupkg → GH Packages,    unguarded)
  publish-github-releases (env: github-releases, all *.nupkg → GH Release,      unguarded)

Each publish job declares `environment:` so a per-channel deployment
record appears under the Deployments tab. The nuget-org env requires
approval (set in #272); the other two are unguarded.

## Channel routing

- nuget.org gets ONLY Fallout.* — Nuke.* shim IDs belong to the
  original NUKE maintainer on nuget.org (#47).
- GitHub Packages gets the Nuke.* shims — preserves the existing
  behaviour from the previous workflow.
- GitHub Releases gets all *.nupkg attached to the tag's release page,
  with auto-generated notes via `gh release create --generate-notes`.
  Idempotent — if the release already exists (workflow_dispatch retry),
  uploads/replaces assets via --clobber.

## Tag protection ruleset (applied separately via API)

A repository ruleset now blocks creation/deletion/update of tags
matching v* for non-admins. Bypass actors: RepositoryRole 5 (admin).
Combined with the env approval gate on nuget-org, this gates
production releases at two layers.

The ruleset is configured via API rather than in this YAML
(ruleset:17017817). It was created in the same change but lives in
repo Settings → Rules, not in the workflow file.

## Build.cs not touched

The existing `IPublish.Publish` target and `ICreateGitHubRelease` from
Build.cs are bypassed by this workflow — CI now calls `dotnet nuget
push` and `gh release create` directly. The targets are still available
for local invocation; consolidating or removing them is a follow-up.

## Docs

docs/agents/release-and-versioning.md "Release pipeline" section
rewritten to describe the new shape (trigger, jobs, channel routing,
tag protection, fallback dispatch).

CHANGELOG entry deferred to the umbrella docs PR #276 per the pattern
set in earlier sub-issues — one consolidated v11-restructure entry
rather than fragmented per-PR entries.

Refs #274, milestone #13.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ChrisonSimtian
ChrisonSimtian merged commit b9b2a43 into main May 29, 2026
2 checks passed
@ChrisonSimtian
ChrisonSimtian deleted the ci/release-yml-tag-triggered-multi-channel branch May 29, 2026 07:05
ChrisonSimtian added a commit that referenced this pull request May 29, 2026
…default channel (#299)

Implements the v11-off-nuget direction. Tag pushes on release/v11 no
longer auto-publish to nuget.org — they publish to GitHub Packages +
GitHub Releases only. To publish Fallout.* to nuget.org you must
invoke workflow_dispatch with `publish-to-nugetorg=true`, an explicit
"this release is stabilised enough for nuget.org" switch.

This *modifies* the routing originally documented in milestone #13's
release.yml refactor (#288). The release-branch model, multi-channel
fan-out, and CD gating are all unchanged — only the routing policy
shifts.

## Workflow changes (`.github/workflows/release.yml`)

- New `workflow_dispatch` input `publish-to-nugetorg` (boolean, default false).
- `publish-nuget-org` job gains `if:` condition requiring both
  `github.event_name == 'workflow_dispatch'` and
  `inputs.publish-to-nugetorg == true`. Tag pushes skip this job.
- `publish-github-packages` job changes its glob from `Nuke.*.nupkg`
  to `*.nupkg` — now publishes both Fallout.* and Nuke.* to GH Packages.
  GH Packages is the v11 release channel; Nuke.* shim routing per #47
  is preserved (still goes here permanently).
- Header comment + per-job comments rewritten to describe the new
  routing policy explicitly.

Three layers of safety on the nuget.org path now:
  1. Tag protection ruleset (only repo admins can create v* tags).
  2. workflow_dispatch flag opt-in.
  3. nuget-org env approval gate.

## Doc updates

- `docs/agents/release-and-versioning.md` — Release pipeline section
  rewritten. Table updated. New "Why nuget.org is opt-in for v11"
  subsection. workflow_dispatch inputs documented.
- `docs/branching-and-release.md` — Channel taxonomy table extended
  with "Current v11 use" column. "Cutting a release" split into two
  sub-runbooks: routine (GitHub Packages only) and stabilised (with
  the opt-in flag).
- `docs/adr/0002-v11-off-nuget-by-default.md` — new ADR capturing the
  decision and three rejected alternatives.

## CHANGELOG

Entry under [Unreleased] — 11.0 → Process explicitly noting this
*modifies* the routing originally documented in the milestone #13
umbrella entry (rather than backdating that entry; preserves history
of how the decision evolved).

## Out of scope

- Unlisting existing 11.0.x on nuget.org — separate work (see worktree
  `unlist-10.3` for the parallel 10.3 unlisting).
- When/how v11 transitions to "stabilised" and opts into nuget.org by
  default — a future ADR / decision when that moment arrives.

Co-authored-by: Claude Opus 4.7 (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