Files
james.maesandClaude Opus 5.5 839fb72bec chore: adopt the KofTwentyTwo standards (#2)
## What

Brings AppKit into conformance with
[KofTwentyTwo/standards](https://github.com/KofTwentyTwo/standards).
Reviewable commit by commit:

| Commit | Change |
| --- | --- |
| build | Quality gate from `configs/dotnet` (analyzers incl. Meziantou,
warnings as errors), `packages.lock.json` per project, locked-mode
restore |
| style | Kingsrook layout and LF line endings, produced mechanically by
`dotnet format` (formatting only) |
| docs | SPDX license header on every C# file |
| refactor | Real code changes: analyzer findings fixed (one type per
file, regex timeouts, async void, …) and header comments on every type
and member |
| ci | Old workflows replaced by callers of the shared standards
workflows (PR gates, security, CodeQL, .NET CI, NuGet release with SBOM
+ attestation + trusted publishing), Scorecard |
| docs | README, CONTRIBUTING (GitHub Flow, DCO, signing), SECURITY,
CODEOWNERS, templates, Dependabot, threat model, ADRs, RELEASING |

Also includes `dev`'s CI fix, so it supersedes #1; the `dev` branch goes
away under GitHub Flow.

## Why

AppKit is the first Product repository to adopt the standards. Static
conformance before: 16 FAIL; after: 0 FAIL, 0 WARN (the remaining
UNKNOWN rows are GitHub settings and rulesets, applied separately).

## How tested

Locally with `CI=true` (lock files enforced): locked restore x64 and
ARM64; zero-warning x64 build; 115 unit tests with 100% line coverage of
`KofTwentyTwo.AppKit` and `KofTwentyTwo.AppKit.Updates`; 12 FlaUI UI
tests (WinUI + WPF samples); `dotnet format --verify-no-changes
--severity warn` clean; pack produces 4 packages + symbols; actionlint,
zizmor (pedantic), gitleaks clean.

The UI tests caught a regression introduced while fixing MA0147 (the WPF
splash stopped dismissing because no UI context existed yet); fixed by
starting the countdown through the dispatcher in both packages.

## Notes

- `ci.yml` and `release.yml` are pinned to KofTwentyTwo/standards#3's
head commit; they are repinned after that PR merges.
- This pull request's new check names become required checks once the
repository's rulesets are updated.

🤖 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:43:17 -05:00

3.8 KiB

Releasing AppKit

Releases follow the KofTwentyTwo release standard. Pushing a protected v* tag on main runs .github/workflows/release.yml, which calls the shared release-nuget.yml workflow in KofTwentyTwo/standards. All four packages always release together under one version.

Status: the shared workflows are pinned to a commit on KofTwentyTwo/standards main while the standards are pre-release; they move to a release tag's commit once KofTwentyTwo/standards publishes v1.0.0 (Dependabot proposes the update).

Versioning

The version is the tag without the leading v, in strict semver.

Tag Version GitHub Release nuget.org
v1.2.3 1.2.3 stable stable
v1.2.3-beta.1 1.2.3-beta.1 prerelease prerelease

AppKit is a library every app depends on, so semver is a promise:

  • major: any breaking change to a public API
  • minor: new capabilities, backward compatible
  • patch: fixes only

Directory.Build.props holds VersionPrefix, the next expected release; local builds are <prefix>-dev. Bump the prefix right after each release, in a pull request.

What a release does

The shared workflow (SLSA Build L3: the build definition lives in KofTwentyTwo/standards, not here):

  1. Validates the tag and re-runs every gate that guards main: locked restore, zero-warning build, unit tests, and the 100% coverage gate.
  2. Packs the four packages and their symbol packages.
  3. Generates a CycloneDX 1.6 SBOM per package (Syft, from packages.lock.json) and attests it.
  4. Writes SHA256SUMS over every asset and attests build provenance.
  5. Creates a draft GitHub Release with the assets and generated notes, then publishes it (releases are immutable).

Then the publish job in this repository's release.yml downloads the packages, verifies their attestations were signed by the shared workflow, and pushes them to nuget.org through trusted publishing (OIDC; no API key is stored anywhere).

One-time setup (owner)

  1. nuget.org trusted publishing policy: nuget.org → your profile → Trusted Publishing → Add policy:
    • Repository owner: KofTwentyTwo
    • Repository: AppKit
    • Workflow file: release.yml
    • Environment: release
  2. Repository variable NUGET_USER (Settings → Secrets and variables → Actions → Variables): your nuget.org profile name. Without it the publish job is skipped and the release still succeeds on GitHub.
  3. Environment release (Settings → Environments) with a deployment rule that allows only tags matching v*.

Cutting a release

  1. Make sure main is green and the conformance check passes: pwsh ../standards/tools/Test-RepoConformance.ps1 -Repository KofTwentyTwo/AppKit -LocalPath .

  2. For a minor or major release, open a release checklist issue from the standards template and complete it (security assessment, threat model review, scan results).

  3. Tag main and push the tag:

    git switch main
    git pull
    git tag -s v0.1.0 -m "v0.1.0"
    git push origin v0.1.0
    
  4. Watch the release workflow, then verify the release as described in the README.

  5. Open a pull request that bumps VersionPrefix in Directory.Build.props.

When something fails

  • Before the release is published: nothing is public. Delete the draft (gh release delete v0.1.0 --yes), fix the cause, and re-run the workflow.
  • At publish (nuget.org): the GitHub release is published and immutable; re-run only the failed publish job from the Actions page.
  • A published release is broken: never reuse its version. Fix forward with a new patch release, and unlist the bad version on nuget.org.