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

90 lines
3.8 KiB
Markdown

# Releasing AppKit
Releases follow the
[KofTwentyTwo release standard](https://github.com/KofTwentyTwo/standards/blob/main/standards/releases.md).
Pushing a protected `v*` tag on `main` runs [`.github/workflows/release.yml`](../.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](https://semver.org).
| 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:
```powershell
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](../README.md#verifying-a-release).
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.