feat(ci): CI host integration as ports & adapters (ADR-0005)#341
Open
ChrisonSimtian wants to merge 10 commits into
Open
feat(ci): CI host integration as ports & adapters (ADR-0005)#341ChrisonSimtian wants to merge 10 commits into
ChrisonSimtian wants to merge 10 commits into
Conversation
Seeds the fast/AI lane (ADR-0004). experimental is a non-public NB.GV ref, so builds get -alpha.<height>.g<commit>, sorting below main's -preview. Same core as main (2026.1.0); breaking surface rides [Experimental] until the yearly cut. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…338) * ci: realign triggers to the 3-tier ladder + hygiene (milestone #18) (#330) Implements the trigger/hygiene slice of milestone #18 (CI cost & pipeline structure). #325 (publish-lane realignment) already landed in the ladder PR. - #318/#326 Cross-platform gated to release intent. windows/macos no longer run on main/experimental pushes (or any routine push). They run only on PR-to- release/* or support/*, and on v* tag pushes. ("On main we've got our edge": the ubuntu-latest PR gate + alpha/preview pipelines.) workflow_dispatch is not emitted — the generator only writes it with inputs; GitHub's run re-run covers on-demand cross-platform. - #322 concurrency cancel-in-progress on ubuntu/windows/macos (generator) + experimental.yml + preview.yml. NOT on release.yml (never cancel a publish). - #323/#328 Canonical CI-ignore list (docs/**, .assets/**, **/*.md) on every PR/push trigger. (release.yml is tag-triggered, so path-ignore is N/A there.) - #327 Codified "feature branches run zero CI until PR'd" + the trigger model in docs/agents/conventions.md, with what-not-to-do guards. - #329 Dropped dead 'submodules: recursive' from all checkouts + the generator (no .gitmodules; full build passes without it) and the stale vendor comment. Generated workflows regenerated from build/Build.CI.GitHubActions.cs. Deferred (own follow-ups): #324 split Build/Test/Pack stages; #328 caching deep-dive; #327 automated reflective guard-test (docs guard in place now). Also unblocked publishing separately: the github-packages environment deployment policy now allows experimental/main/release/*/support/* + v* tags. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: test before publishing on every lane + cache restore-keys (#324, #328) (#331) #324 — experimental.yml and preview.yml ran `dotnet fallout Pack` only, publishing alpha/preview packages WITHOUT running tests. Both now run `dotnet fallout Test Pack` (release.yml + the PR gate already did). One invocation = NUKE's discrete internal stages (Restore → Compile → Test → Pack), failing at the breaking stage; a test failure stops the job before the push step, so untested packages never publish. Separate per-step `dotnet fallout` invocations are avoided on purpose — each re-runs the dependency graph (double-compile); the single invocation is the staged build. #328 — added `restore-keys:` prefix fallback to the hand-written workflows' caches for faster partial restores on key miss. Evaluation: current key (global.json + *.csproj + Directory.Packages.props) is the right dependency set; no packages.lock.json exists to add; build-output (bin/obj) caching deliberately not done (stale-artifact risk). Canonical ignore list (docs/.assets/md) already applied in the trigger PR. Codified both in docs/agents/conventions.md. Deferred: #327 automated reflective guard-test (docs guard already in place). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…he lanes (#333) (#339) * feat(publish): multi-channel, package-ID-aware publishing surface (#333, FALLOUT001) IPublish gains [Experimental("FALLOUT001")] PublishTargets + a --publish-to selector; Publish now routes one Pack output across multiple feeds via the pure, unit-tested PublishPackageRouter (glob include/exclude by package name). Existing single-source members stay as a back-compat default target. Build.cs wires the two real channels: github-packages (every package incl Nuke.*, keyed by the GitHub token) and nuget.org (Fallout.* only, never Nuke.*, keyed by NUGET_API_KEY) — replacing the legacy single-feed push. - src/Fallout.Components/PublishTarget.cs — PublishTarget record + PublishPackageRouter - tests/Fallout.Components.Tests — 10 router tests (added to fallout.slnx) - docs/experimental-apis.md — FALLOUT001 registered Framework compiles clean (0 errors); router tests green. Workflow rewire to `dotnet fallout Publish --publish-to …` follows once experimental is synced with main. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(publish): dogfood `dotnet fallout Publish` on the alpha/preview lanes (#333) experimental.yml and preview.yml now publish via `dotnet fallout Publish --publish-to github-packages` instead of a hand-rolled `dotnet nuget push` loop. Publish depends on Test + Pack, so one invocation runs Restore → Compile → Test → Pack → Publish as NUKE stages; package routing (Fallout.* + Nuke.* → GitHub Packages) lives in Build.cs IPublish.PublishTargets. The GitHub token is passed via the GitHubToken env. Also restore IPublish.PackagePushSettings + PushSettingsBase (kept for back-compat) so the multi-channel reshape stays additive — the new PublishTargets/PublishTo surface is the only opt-in change, behind [Experimental("FALLOUT001")]. release.yml's publish jobs still use raw nuget push (they're coupled to the artifact handoff + nuget-org approval gate) — dogfooding those is folded into #336. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(publish): make PublishTarget a sealed class so the shim generator skips it CI caught CS0509: the TransitionShimGenerator tried to derive a Nuke.Components shim from the sealed *record* PublishTarget. Sealed classes are skipped by design (SHIM001), but the sealed-record shape slipped past that guard. PublishTarget is a new type with no pre-rename consumers, so skipping its shim is correct; switching record→sealed class hits the documented skip path. We don't use record equality/`with`. Verified: Nuke.Components builds 0 errors (SHIM001 warning only). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(generators): accept solution-generator snapshot for new Fallout.Components.Tests Adding tests/Fallout.Components.Tests to fallout.slnx makes the StronglyTypedSolution generator emit a Fallout_Components_Tests accessor; update the Verify snapshot to match (one added line — the new project's strongly-typed Solution property). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… (#340) The dogfood `dotnet fallout Publish` run failed with "Publish target 'github-packages' has no API key". The github-packages target resolves its key via From<ICreateGitHubRelease>().GitHubToken → GitHubActions.Token, which reads the GITHUB_TOKEN env var (EnvironmentInfo.GetVariable("GITHUB_TOKEN")). The lane workflows set `GitHubToken` instead, which didn't match (ICreateGitHubRelease is also [ParameterPrefix]-ed, so the unprefixed name wouldn't bind anyway). Set GITHUB_TOKEN. Caught by letting the post-merge experimental publish actually run. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#346) Feed URL nuget.pkg.github.com/ChrisonSimtian → /Fallout-build (Build.cs IPublish github-packages target, release.yml, consumer docs) + CODEOWNERS → @Fallout-build/maintainers. Sibling of the main repoint (#345). Feed stays PAT-gated (#344). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ChrisonSimtian
added a commit
that referenced
this pull request
Jun 3, 2026
…ts (onion step 5b) Move the concrete CI host providers (AppVeyor, AzurePipelines, TeamCity, GitHubActions, GitLab, TravisCI, Jenkins, Bitrise, Bitbucket, Bamboo, SpaceAutomation) and their config generators from Fallout.Common.CI.* to Fallout.Infrastructure.CI.*. The Application ring uses provider-SPECIFIC capabilities (PublishTestResults, PushArtifact, SetBuildNumber, UpdateBuildNumber, Token, …), so a generic host abstraction can't capture them. Instead, per-provider ports in Fallout.Application.CI — IAppVeyor/IAzurePipelines/ITeamCity/IGitHubActions — plus a CiHost accessor that casts the detected Host.Instance to the port (null when not running on that host). No registration needed: Host.Instance is the existing detection seam, and the providers (subclasses of Host) implement the ports. Components (ITest/IReportCoverage/ISignPackages/ICreateGitHubRelease) and version/coverage attributes now call CiHost.X instead of X.Instance, so the Application ring keeps no dependency on Fallout.Infrastructure.* — the fitness gate still passes. The two enums the ports expose move to Fallout.Application.CI as vocabulary. Nuke.Common CI host shims repointed to the new namespace. (The generic CI host abstraction — ADR-0005 IBuildHost/IBuildReporter, #341 — stays a separate additive effort.) Tooling: OnionRewriter rules retargeted for 5b (CI → Infrastructure.CI, enums overridden to Application.CI). Lesson #13: alias-qualified `global::Ns.Type` refs aren't rewritten (Left includes `global::`) — hit in the shims, mopped up. Full suite green; Application-ring fitness gate green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Model CI host integration as ports & adapters (hexagonal seam) with an additive-now / deletions-batched-to-the-year-cut compatibility strategy, plus the GitHub-adapter-end-to-end spike that validates the runtime-host port shape. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Additive seam per ADR-0005: IBuildHost supersedes the anemic IBuildServer as the build's-eye-view runtime-host port (context + reporting). GitHubActions implements it via explicit interface implementation, leaving the public surface and generated workflows untouched. Adds an architecture fitness test asserting the ports layer (Fallout.Build) never references the adapters layer (Fallout.Common). Spike work on experimental; LogEventSink rewire deferred (see spike verdict #2). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two ports, justified by differing implementor sets: every Host reports (so the local Terminal is an IBuildReporter), but only CI hosts carry run context (so Terminal is not an IBuildHost). IBuildHost rescoped to context (branch/ commit/is-PR); new IBuildReporter (warnings/errors/grouping) implemented by the Host base via explicit impls over the existing protected-virtual hooks — so adapters override reporting exactly as before. No behavior change. Fitness tests pin the split (Terminal: reporter not host; GitHubActions: both). Corrects spike finding #2 (reporting was already wired in the *.Theming.cs partials; there was no latent bug). Updates ADR-0005 + spike verdict. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Host.Default now probes an overridable Host.IsActive member instead of
reflecting for the magic-string IsRunning{TypeName} static. The default
IsActive falls back to that legacy convention, so all existing hosts work
unchanged; new adapters override IsActive directly (no static, no name match).
Host ctors are side-effect-free, so construct-then-probe is cheap and the
active host (constructed last) remains Host.Instance.
Covers ADR-0005 #3. Tests exercise both the override and fallback paths.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First adapter built on the finished ADR-0005 seam, proving it generalizes:
- Runtime host overrides Host.IsActive (no IsRunning{Name} static — first user
of the #3 detection style) and implements both ports (IBuildHost context via
GitHub-compatible GITHUB_* vars; IBuildReporter reporting).
- Config generation composes the GitHub config model + a shared WorkflowCommands
helper for the ::cmd:: dialect, rather than inheriting GitHubActionsAttribute
(which is blocked anyway by ConfigurationAttributeBase.Build's internal set).
Common-case scaffold; full parity needs the GH model-builder extracted.
- Detection (FORGEJO_ACTIONS) is a naive guess pending the live instance.
Tests assert both ports, the IsActive detection path, and non-inheritance from
the GitHubActions adapter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9735c5e to
64b1ea9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the runtime-host ports & adapters seam for CI host integration (ADR-0005), validated by a GitHub-adapter spike and a new Forgejo adapter. Additive / non-breaking. Targets
experimental.What
IBuildHost(context — branch/commit/is-PR — CI hosts only) andIBuildReporter(warnings/errors/grouping — every host, including localTerminal).Host.Defaultprobes an overridableHost.IsActiveinstead of the magic-stringIsRunning{Name}convention. The old convention stays as fallback, so existing hosts are unchanged.Terminalis a reporter, not a context-host).IsActiveoverride (no static), both ports, and config generation by composing the GitHub config model + a sharedWorkflowCommandshelper rather than inheritingGitHubActions.Why
Establishes a stable, enforced CI-adapter seam ahead of the public plugin SDK (milestone #7), without breaking consumers. Full rationale in ADR-0005.
Validation
.githubworkflows byte-identical (config path untouched).Follow-ups (in the spike doc)
FORGEJO_ACTIONSguess — confirm against a live instance.WorkflowCommandsontoGitHubActions(deferred to protect that adapter's byte-identical output).Docs:
docs/adr/0005-ci-host-integration-ports-and-adapters.md,docs/spikes/0001-ci-ports-and-adapters.md.🤖 Generated with Claude Code