The complete release procedure. Written so that someone who has never cut a release can do it.
- One product version for: core library (NuGet
NpgsqlRest), theNpgsqlRestClientbinary, release binaries/Docker tags, and thenpgsqlrestnpm package. - Single source of truth:
version.txtin the repo root. Everything derives from it:Directory.Build.propsreads it into$(NpgsqlRestProductVersion)→ core + client.csprojversions.github/workflows/build-test-publish.ymlreads it per job (RELEASE_VERSION=v$(cat version.txt)) → release tag, release name, changelog file lookup, Docker tags- the
npm-publishjob syncsnpm/package.jsonfrom it before publishing npm/postinstall.jsdownloads the binary from the release tag matching its own package version (v${package.json version}) — no hardcoded version anywhere in the npm package
- Plugins (
plugins/*) version independently — bump a plugin's.csprojversion only when that plugin changes. NuGet publish uses--skip-duplicate, so unchanged plugin versions are skipped automatically.
- Finish the changelog:
changelog/v<X.Y.Z>.mdmust exist — the release notes are extracted from it verbatim (title line is skipped). Cover: headline, new features, breaking changes (⚠️ ), fixes, tests. - Bump the version: edit
version.txt(e.g.3.18.0). Nothing else needs editing. Sanity check locally:dotnet pack NpgsqlRest/NpgsqlRest.csproj -c Release -o /tmp/packcheck # expect NpgsqlRest.<X.Y.Z>.nupkg - Bump plugin versions in
plugins/*/*.csprojfor any plugin that changed since its last published version. - Full test suite green locally (
dotnet test, needs PostgreSQL onlocalhost:5432,postgres/postgres). - Merge/push to
master. That's the trigger. The workflow then automatically:- builds + runs the full test suite (PostgreSQL 17 service container),
- publishes all
.nupkgto NuGet (--skip-duplicate), - creates the GitHub release
v<X.Y.Z>with notes fromchangelog/v<X.Y.Z>.md, - builds AOT binaries: win-x64, linux-x64 (+ smoke test:
--version+--validateagainst a live PostgreSQL), linux-arm64, osx-arm64 — and uploads them as release assets together withappsettings.json, - builds + pushes Docker images:
vbilopav/npgsqlrest:{v,latest}{,-aot,-jit,-arm,-bun}, - publishes the npm package (version synced from
version.txt, OIDC trusted publishing).
- After the release: verify the GitHub release page,
docker pull vbilopav/npgsqlrest:latest,npm view npgsqlrest version, and NuGet listing. - Docs: update the docs site (separate repo,
npgsqlrest-docs):- create
docs/guide/changelog/v<X.Y.Z>.mdfrom this repo'schangelog/v<X.Y.Z>.md, - add it to the sidebar in
docs/.vitepress/config.tsand move the "(Latest)" marker, - update
docs/guide/changelog/index.md("Version X.Y (Latest)" heading), - regenerate
docs/config/latest.mdfrom the releasedNpgsqlRestClient/appsettings.json(update its version references and download links), - update any feature pages affected by the release.
- create
| Secret | Used by |
|---|---|
NUGET_API_KEY |
NuGet publish |
DOCKER_HUB_USERNAME / DOCKER_HUB_TOKEN |
Docker Hub push |
(npm: OIDC trusted publishing — no token; configured on npmjs.com for the npgsqlrest package) |
npm publish |
test.yml— runs on every push/PR to non-master branches: build + full suite on a PostgreSQL 15/16/17 matrix.build-test-publish.yml— runs on push/PR tomaster: the full release pipeline above.⚠️ Note: it runs onpull_requestto master too — the NuGet publish step is effectively a no-op there (secrets are not exposed to fork PRs), but be aware when reviewing runs.
- Branch from the release tag, fix, add
changelog/v<X.Y.Z+1>.md, bumpversion.txt, test, merge tomaster. The pipeline does the rest.