From 13f9f0dab71b9b082ba5eb4dac7afc96fde30b25 Mon Sep 17 00:00:00 2001 From: mgravell Date: Fri, 11 Sep 2026 16:06:16 +0100 Subject: [PATCH] Push each package by path: pwsh does not expand the glob The 1.1.0 release got as far as the push and then failed with error: File does not exist (nupkgs/*.nupkg). because this job runs on windows-latest, where pwsh does not expand wildcards in arguments - so `dotnet nuget push nupkgs/*.nupkg` arrived literally. The workflow this was modelled on runs on Linux, where bash expands the glob before dotnet sees it, which is why the line looked fine. No dry run could catch it: the push is gated on the release event, so it had never executed. Now enumerates the files and pushes each by full path, which does not depend on the shell, and fails loudly if there are none rather than silently pushing nothing. Kept --skip-duplicate so a partial push stays safely re-runnable, and checks $LASTEXITCODE per file, since a failing dotnet inside a foreach would not otherwise fail the step. 1.1.0 itself was published by hand from the run artifact - the packages were already built correctly from the tagged commit, and re-running would have rebuilt from the tag anyway, without this fix in it. Also records the release procedure in state-of-play, including the two things that cost real time: NUGET_USER must be the policy *creator* (not the org that owns the policy), and versionHeightOffset is a fixed shift that drifts with every commit landing on main before the tag. No offset change: with 1.1.0 published, main should move on to 1.1.1, which -4 gives. --- .github/workflows/release.yml | 13 ++++++++++++- notes/state-of-play.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cb1ef078..32e95136 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -99,6 +99,17 @@ jobs: with: user: ${{ secrets.NUGET_USER }} + # push each file by full path rather than passing a glob: this job runs on Windows, where + # pwsh does not expand wildcards in arguments, so `dotnet nuget push nupkgs/*.nupkg` arrives + # literally and fails with "File does not exist". (The upstream this was modelled on runs on + # Linux, where bash expands it first.) --skip-duplicate so a partial push is safely re-runnable - name: Push to nuget.org if: github.event_name == 'release' - run: dotnet nuget push nupkgs/*.nupkg --api-key ${{ steps.login.outputs.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate + run: | + $packages = Get-ChildItem nupkgs/*.nupkg + if (-not $packages) { throw "no packages to push" } + foreach ($p in $packages) { + Write-Output "Pushing $($p.Name)" + dotnet nuget push $p.FullName --api-key ${{ steps.login.outputs.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate + if ($LASTEXITCODE -ne 0) { throw "push failed for $($p.Name)" } + } diff --git a/notes/state-of-play.md b/notes/state-of-play.md index 67840d37..c44db8b3 100644 --- a/notes/state-of-play.md +++ b/notes/state-of-play.md @@ -12,6 +12,9 @@ Phases 1 and 2 of [plan.md](plan.md) are done and merged. Phase 3 (close the gap per round, each verified by a DB-backed run) is in progress; see [harness-baseline.md](harness-baseline.md) for the round log and the current numbers. +**1.1.0 shipped 2026-09-11** - the first release since 1.0.52 (May), and the first to publish via +release.yml rather than by hand. See "Releasing" below for the procedure and its two traps. + Last measured baseline (**round 15, 2026-09-11**, Linux rig): **729 passed / 800** on the Dapper suite with **432 of 736** call-sites handled; the vanilla control on the same box is 770/800. All 41 divergences are known gaps, ×2 providers - no new failure class. @@ -109,6 +112,35 @@ pushed**. Two things to know before trusting a number from it: and the round-12 generator does not reproduce it either, so the difference is configuration that no longer exists. Compare within a rig. +## Releasing (learned the hard way, 2026-09-11 cutting 1.1.0) + +The procedure that works: + +1. read the version off a **green main run**'s step summary ("Report computed version" in + dotnet.yml). A PR run reports the `refs/pull/N/merge` number, which is *not* what will ship - + the step says which it is; +2. create a GitHub Release tagged with exactly that, **unprefixed** (`1.1.0`, not `v1.1.0`); + `publicReleaseRefSpec` accepts both since the `v?` fix, and every tag this repo has ever cut + is unprefixed; +3. release.yml verifies tag == computed version and refuses to publish on a mismatch. + +Two things that cost time, so they are written down: + +- **`NUGET_USER` must be the policy *creator*, not the policy *owner*.** Trusted Publishing + policies are created from your own nuget.org account with an owner dropdown; choosing the + `Dapper` org there is what scopes the policy to org-owned packages, but the token exchange + looks up policies *created by* the username you pass. Passing `Dapper` gives + `HTTP 401 ... No matching trust policy owned by user`; +- `versionHeightOffset` is a **fixed shift, not a pin**. Every commit that lands on main before + the tag moves the computed patch, so re-check `nbgv get-version` on main and decrement the + offset if the target has drifted. Simulate a squash-merge (`git merge --squash` onto a temp + branch off main) rather than reasoning about it - branch-local numbers are misleading, since + the repo squash-merges. + +Not a real concern, having checked: the per-version "uploaded by" on the Versions tab is visible +only to owners. Anonymously the package page shows **Owners** only, and both packages already +list `Dapper` and `marc.gravell`, so which identity pushes changes nothing a consumer sees. + ## What is next, in the order parity.md argues for 0. **say something at the 34 mute overloads** - 22 skipped silently + 12 unsupported-undiagnosed.