diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..5002b71 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,54 @@ +# Publishes the Writeback packages to nuget.org with Trusted Publishing (no stored API key). +# Trigger: push a tag such as v0.1.0. One-time setup is described in docs/RELEASING.md. +name: release + +on: + push: + tags: ["v*"] + +jobs: + publish: + runs-on: ubuntu-latest + environment: nuget + permissions: + contents: read + id-token: write # lets the job request a GitHub OIDC token for nuget.org + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 8.0.x + 10.0.x + + - name: Check that the tag matches the project version + run: | + tag="${GITHUB_REF_NAME#v}" + version=$(sed -n 's:.*\(.*\).*:\1:p' Directory.Build.props) + if [ "$tag" != "$version" ]; then + echo "::error::Tag $GITHUB_REF_NAME does not match $version in Directory.Build.props" + exit 1 + fi + + - run: dotnet build Writeback.slnx -c Release + - run: dotnet test --project tests/Writeback.Tests -c Release --no-build + - run: dotnet pack Writeback.slnx -c Release --no-build -o artifacts + + - name: NuGet login (OIDC -> short-lived API key) + uses: NuGet/login@v1 + id: login + with: + user: ${{ secrets.NUGET_USER }} # nuget.org profile name, not the email address + + - name: Push to nuget.org + run: > + dotnet nuget push "artifacts/*.nupkg" + --api-key "${{ steps.login.outputs.NUGET_API_KEY }}" + --source https://api.nuget.org/v3/index.json + --skip-duplicate + + - uses: actions/upload-artifact@v4 + with: + name: packages + path: artifacts/ diff --git a/README.md b/README.md index 0058670..751ece8 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,7 @@ Methodology, tables and caveats are in [docs/BENCHMARKS.md](https://github.com/M | [Recipes](https://github.com/Murat7Ay/Writeback/blob/master/docs/examples/RECIPES.md) | aggregates, many-to-many, paging, upsert, type handlers, DI, bulk | | [Benchmarks](https://github.com/Murat7Ay/Writeback/blob/master/docs/BENCHMARKS.md) | raw Dapper vs Writeback vs EF Core | | [Migration](https://github.com/Murat7Ay/Writeback/blob/master/docs/MIGRATION.md) | from the original 2017 DapperHelper API | +| [Releasing](https://github.com/Murat7Ay/Writeback/blob/master/docs/RELEASING.md) | publishing to nuget.org with Trusted Publishing | ## Building and testing diff --git a/docs/RELEASING.md b/docs/RELEASING.md new file mode 100644 index 0000000..9fcd551 --- /dev/null +++ b/docs/RELEASING.md @@ -0,0 +1,37 @@ +# Releasing + +Packages are published to nuget.org by [`.github/workflows/release.yml`](../.github/workflows/release.yml) using +**NuGet Trusted Publishing**. GitHub Actions exchanges a short-lived OIDC token for a one-hour API key, so no +long-lived key is stored anywhere. + +## One-time setup + +1. **nuget.org:** sign in, click your user name, choose **Trusted Publishing**, and add a policy: + - **Repository Owner:** `Murat7Ay` + - **Repository:** `Writeback` + - **Workflow File:** `release.yml` (the file name only) + - **Environment:** `nuget` + - **Scopes:** allow pushing new packages and new versions for `Writeback*`. +2. **GitHub:** add a repository secret `NUGET_USER` containing your nuget.org **profile name** (not your email): + ```bash + gh secret set NUGET_USER --repo Murat7Ay/Writeback + ``` +3. **Optional:** under *Settings → Environments → nuget*, add yourself as a required reviewer, so every publish + waits for your approval. + +## Publishing a version + +1. Set `` in `Directory.Build.props` (e.g. `0.2.0`) and merge to `master`. +2. Tag the merged commit and push the tag: + ```bash + git tag v0.2.0 + ``` + ```bash + git push origin v0.2.0 + ``` +3. The workflow checks that the tag matches ``, builds, runs the unit tests, packs, and pushes + `Writeback`, `Writeback.SqlServer` and `Writeback.PostgreSql` (with symbol packages). Re-running a tag is + safe (`--skip-duplicate`). + +The policy may start as *temporarily active* for 7 days and becomes permanent after the first successful publish. +The temporary API key is valid for one hour and is requested right before the push.