## 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>
90 lines
3.8 KiB
Markdown
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.
|