Cut a release
Every Trax package (Trax.Core, Trax.Effect, Trax.Mediator, Trax.Scheduler, Trax.Api, Trax.Cli
and Trax.Samples.Templates) is released from the one
TraxSharp/Trax repository, at one shared version, by
semantic-release. Releases are cut by
hand. A merge to main runs no workflow and publishes nothing, since the pull request already built
and tested the change: work accumulates there until someone runs the release workflow, which runs
the full CI over every folder again before it releases everything merged since the last v* tag.
Commit types
semantic-release reads the commits since the last v* tag with its default angular preset. The
squash merge uses the pull request title as the commit on main, so the PR title's type is what
counts:
type(scope): description| Commit | Release |
|---|---|
feat: | minor (1.4.2 → 1.5.0) |
fix: | patch (1.4.2 → 1.4.3) |
perf: | patch |
revert: | patch, but only with This reverts commit <sha>. in the body, which git revert writes. A bare revert: ... subject releases nothing |
refactor:, docs:, test:, chore:, ci:, style:, build: | none |
feat!:, fix!: | none. The angular preset does not read !: the header does not parse, so the commit releases nothing |
BREAKING CHANGE: in the body or footer | major |
The type decides what a release contains. A real fix committed as refactor: or chore: is
invisible to the analyzer, so running the workflow does not ship it and the package stays at the
old version. If that has already happened, an empty commit on main gives the next release
something to cut:
git commit --allow-empty -m "fix: <what the earlier commit fixed>"A major release is permanent on NuGet. Do not write BREAKING CHANGE: in a commit unless a major
release is intended; describe a breaking change in the pull request instead.
Cutting a release
- Open the repository's Actions tab, choose Release, and Run workflow on
main. - The
build-testjob runs the full CI (.github/workflows/ci.yml) over every folder. - The
releasejob waits for approval on the protectedreleaseenvironment. Once approved, semantic-release reads every commit since the lastv*tag and cuts one version at the highest bump those commits call for, with one set of release notes. It creates the git tag (v1.61.0) and the GitHub release, and writes the version to.release-version. If no commit calls for a release, it stops there. - The same job restores the locked dependencies of every folder and runs
dotnet pack -p:Version=<version>on each, so every package,Trax.Samples.Templatesincluded, carries that one version. It uploads the packages as an artifact. - The
publishjob, also gated on thereleaseenvironment, attests the packages and pushes them to nuget.org with a short-lived Trusted Publishing key. See Supply Chain Security. - The site is not part of the release. Dispatch Website with the new tag (
v1.61.0); its job, gated on thewebsite-deployenvironment, moves thewebsitebranch to the tag, which publishes the site for that release.
The configuration lives in .releaserc.json at the repository root and the workflow in
.github/workflows/release.yml. The plugins are commit-analyzer (default rules),
release-notes-generator, exec (writes .release-version) and github (tag, release and a
comment on the released pull requests). There is no changelog or git plugin: no CHANGELOG.md is
written and nothing is committed back to main. The release notes are on the GitHub release.
No Directory.Build.props is updated. Each folder's stays at 1.99.99 permanently, which is the
version a local dotnet pack produces; the real version comes from the tag and is passed to
dotnet pack by the release.
The first shared release is 1.61.0. The tags from before the packages shared a version are kept,
prefixed with the folder name (Trax.Core/v1.9.1), and semantic-release never reads them.
One version, no release order
Inside the repository the Trax packages reference each other with ProjectReference, so a change
that touches several packages builds and tests as one pull request, and one release ships all of it.
There are no Trax pins to bump between releases and no order to release in. A package whose code did
not change still gets the new version.
After a release, raise PackageValidationBaselineVersion in each Directory.Build.props that sets
it to the version just released, so package validation compares the next pack against it.
Finding unreleased work
A merged fix that nobody releases never reaches a consumer. To see what is waiting:
git fetch --tags
git log --oneline "$(git describe --tags --abbrev=0 --match 'v*' origin/main)"..origin/mainAny feat:, fix:, perf: or revert: in that list is unreleased.
Troubleshooting
The workflow ran and nothing was released. No commit since the last v* tag has a releasing type,
a commit used feat!:/fix!:, which the angular preset ignores, or a revert: has no
This reverts commit <sha>. line. Add an empty fix: or feat:
commit as above.
The release job is waiting. It needs an approval on the release environment.