--- name: new-release description: 'Cut a new release: tag the appropriate commit, generate the changelog, and produce build artifacts. Use when: releasing a new version, tagging a release, publishing NuGet packages, preparing release artifacts.' argument-hint: 'Branch or commit to release from (default: current main) and the kind of release (stable, prerelease, hotfix)' --- # New Release Versioning is **GitVersion-driven** (`GitVersion.yml`). Do not hand-pick versions — let GitVersion compute them from branch and tag history. ## Versioning | Branch | Label | Resulting version | | --- | --- | --- | | `main` | — | `MAJOR.MINOR.PATCH` (stable, held until the next tag) | | `feature/*`, `hotfix/*` | branch name | `…-.N` | - Tags use the prefix `v` (regex `[vV]?` also accepts `V` or none). - Each `src/*` project is versioned **independently**: only changes under that project's own directory, `Directory.Packages.props`, or `GitVersion.yml` increment its version (see `GenerateProjectGitVersionConfig` in `src/Directory.Build.props`, which renders the root `GitVersion.yml` into a per-project copy). A change to one library does not bump a sibling's version. - Increments are suppressed for already-merged branches and already-tagged commits. ## Changelog Release notes are generated at pack time and embedded as `PackageReleaseNotes`. The `GenerateChangelogFromGit` target writes `git log` subjects to `$(ChangeLogPath)`, and `CreateReleaseNotesFromFile` reads that file into the nuspec. Both run before `GenerateNuspec`, and generation is skipped when the repository root has no `.git` directory. The commit range is resolved in this order, first match wins: 1. `$(ChangeLogFromRef)..HEAD` — when `ChangeLogFromRef` is set. 2. `..HEAD` — from `git describe --tags --abbrev=0`. 3. `..HEAD` — the last first-parent merge whose subject matches `$(ChangeLogUpstreamMergePattern)`. 4. `..HEAD` — the last first-parent merge. 5. No range; the log is capped at `$(ChangeLogCommitLimit)` commits. Commits are filtered with `--no-merges` and scoped to the project's own directory, plus the directories of its `ProjectReference`s unless `ChangeLogIncludeReferences` is `false`. This mirrors the per-project versioning rule above: a sibling library's commits do not appear in this package's notes. | Property | Default | Purpose | | --- | --- | --- | | `ChangeLogFromRef` | *(empty)* | Explicit range start; overrides the fallback chain | | `ChangeLogPath` | `$(BaseIntermediateOutputPath)changes.log` | Where the log is written and read from | | `ChangeLogCommitLimit` | `100` | Cap used only when no range resolves | | `ChangeLogUpstreamMergePattern` | `from .*/` | Subject matcher for the upstream-merge fallback | | `ChangeLogIncludeReferences` | `true` | Include `ProjectReference` directories in scoping | | `GenerateChangelogFromGit` | *(unset)* | Set to `false` to skip generation entirely | > [!IMPORTANT] > Step 2 uses the **nearest reachable** tag, not the newest. Packing from a commit that already carries a release tag therefore yields only the commits added since it — often a single entry. Pass `ChangeLogFromRef` to widen the range: > > ```shell > dotnet pack -c Release -p:ChangeLogFromRef=v4.0.0 > ``` To ship hand-written notes instead, disable generation and point `ChangeLogPath` at your own file. Generation must be disabled, otherwise the target overwrites it: ```shell dotnet pack -c Release -p:GenerateChangelogFromGit=false -p:ChangeLogPath=notes.md ``` Confirm the resolved range in the build output before publishing: ```text Generated changelog at using range v4.0.0..HEAD. ``` ## Procedure 1. **Sync.** `git fetch --all --tags && git status` — confirm clean. 2. **Compute.** `dotnet build src\\.csproj -getProperty:Version` — verify the value matches expectations for that specific project (there is no single repo-wide version). 3. **Pack.** `dotnet pack -c Release` (or use the [docker-build](../docker-build/SKILL.md) `pack` target). Artifacts land in `artifacts/nuget/`. Check the logged changelog range and pass `ChangeLogFromRef` if it is narrower than the release warrants. 4. **Tag.** `git tag -a v -m "Release v" && git push origin v`. 5. **Publish** packages to the configured feed. 6. **Create the GitHub release** linked to the tag, using the generated changelog as notes. For hotfixes: branch from the latest stable tag (`git switch -c hotfix/ v`), follow the procedure, then merge back into `main`. There is no `develop` branch to reconcile with — this repo is trunk-based. ## Guardrails - **Tags are immutable** once pushed; never move or delete them. - **Never skip a `PATCH`** — ship a fix forward. - **Do not edit `GitVersion.yml` or a generated `src/*/GitVersion.yml` to force a version.** Address the underlying cause (missing tag, branch base, path filter, commit messages). Generated per-project files are gitignored build artifacts, re-derived from the root `GitVersion.yml` on every build — never hand-edit them.