Files
james.maesandClaude Opus 5.5 ae3df7af77 feat(ci): reusable .NET CI and NuGet release workflows (#3)
## What

- **`dotnet.yml`** (reusable): locked restore, zero-warning build, tests
with a per-package line-coverage gate, the Kingsrook format check
(`dotnet format --verify-no-changes --severity warn`), optional FlaUI UI
tests and pack. Callers named `ci` get `ci / build-test`, `ci / format`,
`ci / ui-tests`.
- **`release-nuget.yml`** (reusable, tag-triggered by the caller):
strict SemVer tag, Release build/test/coverage gates, `dotnet pack` with
the tag version, a CycloneDX 1.6 SBOM per package from its
`packages.lock.json` (Syft, digest-pinned), `SHA256SUMS`, SLSA build
provenance and SBOM attestations, then a draft GitHub release that is
published last. Builds run here, so provenance names
`KofTwentyTwo/standards` as the builder (SLSA Build L3).
- **nuget.org push stays in the caller.** nuget.org trusted publishing
validates the token's `repository` and `job_workflow_ref` claims against
the package's own repository
([source](https://github.com/NuGet/NuGetGallery/blob/main/src/NuGetGallery.Services/Authentication/Federated/GitHubTokenPolicyValidator.cs)),
so a push inside a reusable workflow from this repo would always be
rejected. The `release-nuget.yml` template's `publish` job downloads the
packages, runs `gh attestation verify --signer-repo
KofTwentyTwo/standards`, then pushes via `NuGet/login`. Policy fields
documented in `standards/releases.md`.
- Caller templates `dotnet-ci.yml` and `release-nuget.yml`; docs (ci-cd
required checks and K22-CI-30, releases, README, configs/dotnet adoption
steps).
- Conformance checker: K22-CI-30 now requires a **SHA-pinned**
`KofTwentyTwo/standards/.github/workflows/release-*.yml` call in every
tag workflow; K22-CI-01 expects `ci / build-test` and `ci / format` when
the repo has a solution.

## Why

AppKit and the Windows app template need shared .NET gates and a release
path that meets K22-CI-30/31 and K22-REL-01..07 without each repository
re-implementing them.

## How tested

- actionlint: clean. zizmor 1.30.1 (default and pedantic) on workflows
and templates: no findings.
- Syft 1.54.1 locally against an AppKit project with a lock file:
CycloneDX 1.6 SBOM listing `Velopack 1.2.161` and the project reference.
- Conformance `-SelfTest`: passed (new release-call and context
assertions). Static run against a scratch repo built from the new
templates: K22-CI-10/11/13/14/15/30 PASS; with the release call pinned
to `@main` instead of a SHA, K22-CI-30 FAILs as intended.
- markdownlint 0.23.3: 0 issues; internal links: 0 problems; gitleaks:
no leaks.
- Not yet run in GitHub Actions: the integration test is AppKit's
compliance PR calling this branch's head SHA.

Stacked on #2 (which is stacked on #1).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: James Maes <james@kof22.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:36:50 -05:00

4.4 KiB

.NET

Drop-in files that implement the C# / .NET profile: the Kingsrook layout plus the shared quality and supply-chain rules. With them in place, dotnet build fails locally on exactly the same problems that fail CI.

File Copy to Purpose
.editorconfig repository root Kingsrook layout (3-space indent, Allman braces, if(), naming, code-style rules at build-failing severity, Rider/ReSharper blank-line keys, justified analyzer adjustments
Directory.Build.props repository root Warnings as errors, analyzers, style in build, nullable, deterministic builds, NuGet audit (all) and lock files, Meziantou.Analyzer
Directory.Packages.props.example repository root as Directory.Packages.props Central package versions, including the pinned analyzer

A polyglot repository uses ../editorconfig/.editorconfig instead, whose [*.cs] sections are identical to this file's.

No Rider/ReSharper .DotSettings layer is needed: Rider and ReSharper read the resharper_* keys in .editorconfig, which carry the one Kingsrook rule the .NET formatter cannot express (three blank lines between members).

Adopting in an existing repository

  1. Copy the three files. Keep any repository-specific groups of an existing Directory.Build.props (version, package metadata) and replace its quality settings.

  2. Set * text=auto eol=lf in .gitattributes (K22-CODE-07).

  3. Add a global.json pinning the SDK feature band ("rollForward": "latestFeature"); CI installs the SDK from it.

  4. Restore once so packages.lock.json files are created, and commit them. CI restores with --locked-mode, which fails on any drift from the lock files, and the release workflow builds each package's SBOM from them.

  5. Reformat and fix:

    dotnet format                                   # layout, var, usings, naming, line endings
    dotnet build -warnaserror                       # remaining analyzer findings
    
  6. Commit the reformat on its own (style: adopt Kingsrook layout) so git blame can skip it: add that commit's hash to .git-blame-ignore-revs.

  7. Add the CI and release callers from templates/workflows/dotnet-ci.yml and templates/workflows/release-nuget.yml.

IDEs

  • Visual Studio 2026: reads .editorconfig natively, including the formatting and naming rules; Code Cleanup applies them. Keep Tools → Options → Text Editor → C# → Code Style → General → "Analyze code style during build" on (it is the build's setting anyway).
  • Rider / ReSharper: reads .editorconfig (including the resharper_* keys). Settings → Editor → Code Style shows the values as "from EditorConfig". Run Code → Reformat and Cleanup or enable cleanup on save.
  • VS Code (C# Dev Kit): reads .editorconfig; see ../vscode.

Command line

Purpose Command
Build with every rule dotnet build -warnaserror
Fix layout and style dotnet format
CI gate dotnet format <solution> --verify-no-changes --severity warn --no-restore
Locked restore (automatic in CI) dotnet restore --locked-mode

What is deliberately relaxed

Rule Where Why
CA1716 everywhere Flags member names that are VB keywords (Error); KofTwentyTwo code is C#-only
MA0004 (ConfigureAwait) everywhere App code resumes on UI threads; libraries opt in (K22-CODE-CS-13)
MA0049 everywhere Conflicts with the App / App.Core project layout
CA1707, CA1861, CS1591, MA0048 tests/, test/ xUnit naming, inline test data, undocumented tests, one file per feature's tests
CS1591 non-packable projects Only shipped libraries must document their public API

Verified

On .NET SDK 10.0.401 with Meziantou.Analyzer 3.0.296: a Kingsrook-style class library and console app build with 0 warnings and pass dotnet format --verify-no-changes, and the build fails on each of: an unused using (IDE0005), 4-space indentation, a same-line (K&R) brace, foreach ( with a space (all IDE0055), and a Meziantou rule (MA0006, MA0011). Locked restore rejects a version that differs from the lock file (NU1004).