Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: release-please

# Maintains a standing "release PR" that accumulates Conventional Commits since the last
# release and computes the next semver bump from them (feat: -> minor, fix: -> patch,
# "BREAKING CHANGE:" footer -> major). Merging that PR creates and pushes a vX.Y.Z tag,
# which is what actually triggers release.yml's build/test/pack/publish pipeline - this
# workflow only ever decides the version number and creates the tag, nothing here touches
# NuGet or creates a GitHub Release (skip-github-release in release-please-config.json,
# since release.yml's own publish job already creates the Release after a successful
# publish - having both would create two Releases for the same tag).
on:
push:
branches: [main]

permissions:
contents: write
pull-requests: write

jobs:
release-please:
runs-on: ubuntu-latest
steps:
# Deliberately NOT the default GITHUB_TOKEN: GitHub blocks the default token's
# pushes from triggering other workflows (loop prevention), which would silently
# break this chain at the exact point it needs to trigger release.yml. Needs a
# fine-grained PAT (Contents: read/write) stored as the RELEASE_PLEASE_TOKEN secret.
- uses: googleapis/release-please-action@v4
with:
token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
".": "0.1.0"
}
49 changes: 41 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@
- Public members need XML doc comments (enforced via `GenerateDocumentationFile`).
- Run `dotnet format` before submitting a PR; CI verifies formatting.

## Commit messages / PR titles

This repo follows [Conventional Commits](https://www.conventionalcommits.org/)
(`feat: ...`, `fix: ...`, `chore: ...`, etc., with a `BREAKING CHANGE:` footer
when needed). This isn't just a style preference — `release-please` (see
below) parses these to decide the next version number and to write the
changelog automatically, so it needs to be the PR title if you squash-merge,
or every commit message if you merge/rebase.

## Pull request checklist

- [ ] Tests added/updated and passing (`dotnet test`)
Expand All @@ -34,15 +43,39 @@

## Versioning and releasing

Versioning is computed by Nerdbank.GitVersioning from `version.json` and git
history — every commit gets a meaningful prerelease version
(`0.1.18-alpha.gb301a5cb61`-style) automatically; there's no manual version
bump.
The version number and the release tag are both decided automatically by
[release-please](.github/workflows/release-please.yml), not by hand:

1. Every PR merged to `main` is analyzed by `release-please` for Conventional
Commits since the last release. It maintains a single standing **Release
PR** that accumulates these, with the computed next version
(`fix:` → patch, `feat:` → minor, `BREAKING CHANGE:` → major) and an
auto-written `CHANGELOG.md` section.
2. Merging that Release PR creates and pushes a `vX.Y.Z` tag.
3. That tag push is what triggers [release.yml](.github/workflows/release.yml),
which builds, tests, and packs, then **requires manual approval** before
publishing: the publish job runs under a `nuget-release` GitHub Environment
(already configured with required reviewers).

Don't create or push a `vX.Y.Z` tag by hand — that races with
`release-please`'s own bookkeeping in `.release-please-manifest.json` and can
leave it confused about what's already shipped.

`release-please` pushes its tag using a fine-grained PAT in the
`RELEASE_PLEASE_TOKEN` repo secret, not the default `GITHUB_TOKEN` - GitHub
blocks the default token's pushes from triggering other workflows (its
built-in loop-prevention), which would otherwise silently break the chain
right before it reaches `release.yml`. `skip-github-release: true` in
`release-please-config.json` stops `release-please` from also creating a
GitHub Release itself - `release.yml`'s own publish job already creates one
after a successful NuGet push, and having both would create two Releases for
the same tag.

Pushing a `vX.Y.Z` tag triggers [release.yml](.github/workflows/release.yml),
which builds, tests, and packs, then **requires manual approval** before
publishing: the publish job runs under a `nuget-release` GitHub Environment
(already configured with required reviewers).
Nerdbank.GitVersioning still computes the actual assembly/package version
from `version.json` and git history at build time, the same as before -
`release-please` only decides *which* tag to create; once that tag exists and
matches `version.json`'s `publicReleaseRefSpec`, NBGV treats it as the public
release version directly.

Publishing itself uses [NuGet Trusted Publishing](https://learn.microsoft.com/en-us/nuget/nuget-org/trusted-publishing)
(OIDC) rather than a stored API key — the workflow exchanges a short-lived
Expand Down
10 changes: 10 additions & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"skip-github-release": true,
"packages": {
".": {
"release-type": "simple",
"changelog-path": "CHANGELOG.md"
}
}
}
Loading