Skip to content

Commit 8becae8

Browse files
authored
Publish to nuget.org via Trusted Publishing (OIDC), and retire MyGet/AppVeyor (#2230)
* Fix the release-tag pattern in version.json publicReleaseRefSpec requires `^refs/tags/v\d+\.\d+`, but every tag this repo has ever cut is unprefixed - 2.1.79, 2.1.72, 2.1.66 and so on - so the regex cannot match any of them. Checking out the real 2.1.79 tag and asking nbgv gives `2.1.79-g72a54c475f`, PublicRelease False: a tag-triggered build has always produced a -g suffixed version, which is fine while releases are pushed by hand and fatal once a workflow does it. Relaxed to `v?` so both spellings work. Same fix, same reason, as DapperAOT b5194b4 and StackExchange.Redis 611e478. Also adds the `$schema` line the file was missing. * CI: report the computed version, build on SDK 11, and test net481 **Report the computed version.** A step writes NuGetPackageVersion to the job summary and the log, before the build so it stays legible when a later step fails. Cutting a release now means reading that line off a green main run and tagging with exactly it - so the number has to be visible. A PR run builds refs/pull/N/merge, whose height includes the branch commits and the merge, so its number is *not* the one that will ship; the summary says so on anything that is not main, rather than inviting a mis-tag. This needs full history, so checkout gains fetch-depth: 0 (and moves v1 -> v4). **Build on SDK 11.** global.json already says rollForward:latestMajor, so simply installing the SDK 11 preview alongside 10 makes it the one that builds. Breakage from a new major toolchain then surfaces on an ordinary PR instead of when a release is being cut. Target frameworks are untouched - this changes what compiles the code, not what the code runs on - and global.json stays the single lever if we ever want to pin back to 10. **Test net481.** tests/Directory.Build.targets sets IsTestProject=false for net4x outside Windows, so the Ubuntu job has never run net481 - and net481 is the only TFM that compiles the ENTITY_FRAMEWORK, LINQ2SQL and OLEDB code paths. AppVeyor was quietly the only thing covering them, so retiring it (next commit) without this would drop that cover silently. New windows-job runs exactly that leg. GitHub's Windows images ship no SQL Server engine - only the OLEDB drivers - hence the install step; AppVeyor's image had an MSSQL$SQL2019 instance built in. MySQL and PostgreSQL are deliberately not provisioned there: FactMySqlAttribute and FactPostgresqlAttribute probe the server in a static constructor and skip the tests when it is unreachable, and the Ubuntu job already covers both providers against real servers. Also fixes `.NET Lib Pack`, which passed a cmd-style `%CD%\.nupkgs` to a job that has run on Ubuntu for years, and so has been writing to a directory named literally that. * Add release.yml: verify the tag, then publish to nuget.org over OIDC There has never been a workflow that publishes to nuget.org - AppVeyor pushed to two MyGet feeds on every main build, and the actual nuget.org releases were pushed by hand. That is also why the broken tag pattern went unnoticed for years. Triggered by a published GitHub Release, with workflow_dispatch as a dry run that does everything except the tag check and the push, so the pipeline can be proven (and the packages inspected, via the run artifact) without cutting a release. The guard is the point: nbgv computes the version from version.json plus commit height, so the tag name does not set it and a mistyped tag would otherwise ship a package that disagrees with its release. The step compares the two and fails loudly instead. A leading "v" is stripped, matching the `v?` pattern. Auth is Trusted Publishing (OIDC) - no long-lived key anywhere, which is the whole reason for the move. Setup lives outside this file and is recorded in the header comment: a GitHub environment named "release", a NUGET_USER secret, and a trusted-publishing policy on nuget.org for each of the six published packages. A package with no matching policy fails its push, so all six need one up front. Runs on Ubuntu, matching main.yml: that is the configuration CI actually proves, and it means bash expands the push glob (DapperAOT 13f9f0d had to unpick exactly that on Windows). Packages are uploaded as a run artifact before the push, so a failed push does not cost the build, and --skip-duplicate makes a partial push safely re-runnable. Output goes to `nupkgs`, not `.nupkgs`: upload-artifact skips hidden files by default and a dot-directory uploads as an empty artifact. No tests here - the commit being released already passed CI, and this job's only job is to reproduce that build and push it. * Retire AppVeyor, and drop MyGet from the badges release.yml now publishes to nuget.org on a tagged release, so AppVeyor's two MyGet deploy providers are both redundant and a second, unversioned place for packages to appear. The rest of AppVeyor went with them: its Windows build and test run is now main.yml's windows-job, which is the part worth keeping. That also retires the two encrypted MyGet API keys the file carried. Badges and package tables follow: the AppVeyor build badge becomes the GitHub Actions one, and the MyGet pre-release column and feed link come out of both readmes, since that feed is no longer fed by anything. build.ps1 / build.cmd are left in place - AppVeyor was their only caller, but they still work as a local build-test-pack entry point. * Stop packing Dapper.ProviderTools `dotnet pack Build.csproj` produces it, so a release workflow would push it - but it was last released as 2.0.90 in April 2021 and has had no work since. Shipping it now means a dormant package jumping to 2.1.x on the next release, and a nuget.org trusted-publishing policy that has to be kept alive for something nobody is maintaining. IsPackable=false instead, which drops the push set to the six packages the readme actually lists. The project still builds and is still referenced; un-set the property to resume shipping it. * Bump Microsoft.SourceLink.GitHub to clear the NU1902 build break Microsoft.Build.Tasks.Git 10.0.300, pulled in transitively by SourceLink, has picked up GHSA-23fw-v26w-5fgq. TreatWarningsAsErrors turns the NU1902 audit warning into an error, so Dapper and Dapper.StrongName fail to build - tests and benchmarks only warn, since they set TreatWarningsAsErrors=false. This is not caused by anything else in this PR: it reproduces on main, under SDK 10 and SDK 11 alike. It is bundled here only because it blocks the checks, and with them the first real run of the new net481 job. There is no fixed 10.x - the only versions above 10.0.300 are 11.0.100 previews - so this takes the rc. That is tolerable for this particular dependency: SourceLink is PrivateAssets=all, a build-time-only asset that never becomes a dependency of the packages we publish, and it already tracks the SDK we now build with. Verified locally: NU1902 is gone and the build exits 0, leaving the pre-existing NU1903 warnings in tests/benchmarks (SQLitePCLRaw, System.Security.Cryptography.Xml) which are warnings only and are a separate matter. * Windows: reach SQL Server over shared memory, not TCP First real run of this job: 735 of 825 net481 tests failed, every one of them "TCP Provider, error: 0 - The remote computer refused the network connection." The install itself was fine - ankane/setup-sqlserver puts in a default MSSQLSERVER instance with SQL auth - but a Windows SQL Server install leaves the TCP/IP protocol disabled, so nothing is listening on 1433 and every connection to tcp:localhost,1433 is refused. AppVeyor never hit this because it connected to `(local)\SQL2019`, not over TCP. Dropping the `tcp:`/port from both connection strings uses shared memory against the local default instance, which needs no protocol configuration. Also pins the engine to 2019: that is what the Ubuntu job's service container runs and what AppVeyor ran these tests against, and the action otherwise installs SQL Server 2025. This job exists to preserve cover that already existed - moving the engine version underneath it at the same time is a separate decision, and the OLEDB tests in particular use the legacy SQLOLEDB provider. Adds a reachability check before the tests, so the next time something is wrong with the server the job says so in one line instead of several hundred identical connection failures. * Windows: stop pinning SQL Server 2019, it hangs the install The pin added in the previous commit hung "Install SQL Server" for 28 minutes before the run was cancelled; the same step with the action's default took 2m48s on the same image a run earlier. SQL Server 2019 predates the Windows Server 2025 runner image, and its setup stalls rather than failing outright. Reverting to the action's default. The conservatism was not worth its cost: it was meant to keep this job as close as possible to what AppVeyor proved, but it cannot install at all. The net481 leg therefore runs against a newer engine than AppVeyor used - worth knowing, and called out in the comment, because the OLEDB tests reach SQL Server through the legacy SQLOLEDB provider and that combination has not been exercised before. The shared-memory connection strings and the reachability check stay: those were the actual fix for the 735 connection failures, and are independent of version.
1 parent 6d48ef6 commit 8becae8

8 files changed

Lines changed: 218 additions & 95 deletions

File tree

‎.github/workflows/main.yml‎

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
name: Main Build
22

33
on:
4+
workflow_dispatch:
45
pull_request:
56
push:
67
branches:
@@ -39,11 +40,39 @@ jobs:
3940
MYSQL_DATABASE: test
4041
steps:
4142
- name: Checkout code
42-
uses: actions/checkout@v1
43+
uses: actions/checkout@v4
44+
with:
45+
fetch-depth: 0 # depth is needed for nbgv
4346
- name: Setup dotnet
4447
uses: actions/setup-dotnet@v4
4548
with:
4649
dotnet-version: '10.0.x'
50+
# SDK 11 is installed pre-emptively and, because global.json says rollForward:latestMajor,
51+
# it is the SDK that actually builds. Breakage from a new major toolchain then shows up on
52+
# an ordinary PR rather than when a release is being cut. Target frameworks are untouched:
53+
# this is about what compiles the code, not what the code runs on.
54+
- name: Setup dotnet (SDK 11 preview)
55+
uses: actions/setup-dotnet@v4
56+
with:
57+
dotnet-version: '11.0.x'
58+
dotnet-quality: 'preview'
59+
- uses: dotnet/nbgv@master
60+
with:
61+
setAllVars: true
62+
# deliberately before build, so the version stays legible even when a later step fails.
63+
# Cutting a release means tagging with exactly what a *main* run prints - a PR run builds
64+
# refs/pull/N/merge, whose height includes the branch commits and the merge, so its number
65+
# is not the one that will ship
66+
- name: Report computed version
67+
run: |
68+
version=$(nbgv get-version --variable NuGetPackageVersion)
69+
echo "### Computed package version: \`$version\`" >> "$GITHUB_STEP_SUMMARY"
70+
if [ "$GITHUB_REF" = "refs/heads/main" ]; then
71+
echo "Tag the release with exactly this." >> "$GITHUB_STEP_SUMMARY"
72+
else
73+
echo "_This is a \`$GITHUB_REF\` build - **not** the version that would ship. Read the release version off a main run._" >> "$GITHUB_STEP_SUMMARY"
74+
fi
75+
echo "Computed package version: $version (ref: $GITHUB_REF)"
4776
- name: .NET Build
4877
run: dotnet build Build.csproj -c Release /p:CI=true
4978
- name: Dapper Tests
@@ -53,5 +82,55 @@ jobs:
5382
OLEDBConnectionString: Provider=SQLOLEDB;Server=tcp:localhost,${{ job.services.sqlserver.ports[1433] }};Database=tempdb;User Id=sa;Password=Password.;
5483
PostgesConnectionString: Server=localhost;Port=${{ job.services.postgres.ports[5432] }};Database=test;User Id=postgres;Password=postgres;
5584
SqlServerConnectionString: Server=tcp:localhost,${{ job.services.sqlserver.ports[1433] }};Database=tempdb;User Id=sa;Password=Password.;
85+
# proves the packages still build; release.yml is what actually pushes them
5686
- name: .NET Lib Pack
57-
run: dotnet pack Build.csproj --no-build -c Release /p:PackageOutputPath=%CD%\.nupkgs /p:CI=true
87+
run: dotnet pack Build.csproj --no-build -c Release -p:PackageOutputPath="$GITHUB_WORKSPACE/.nupkgs" -p:CI=true
88+
89+
# The net4x test legs are switched off outside Windows - tests/Directory.Build.targets sets
90+
# IsTestProject=false for them - so the Ubuntu job above never runs net481, and net481 is the
91+
# only TFM that compiles the ENTITY_FRAMEWORK, LINQ2SQL and OLEDB code paths. This job exists
92+
# solely to run that leg; until now it only ever ran on AppVeyor.
93+
#
94+
# GitHub's Windows images ship no SQL Server engine (only the OLEDB drivers), hence the install
95+
# step. MySQL and PostgreSQL are deliberately *not* provisioned here: those tests self-skip when
96+
# the server is unreachable (FactMySqlAttribute / FactPostgresqlAttribute probe it in a static
97+
# constructor), and the Ubuntu job already covers both against real servers.
98+
windows-job:
99+
name: Windows (net481)
100+
runs-on: windows-latest
101+
steps:
102+
- name: Checkout code
103+
uses: actions/checkout@v4
104+
with:
105+
fetch-depth: 0 # depth is needed for nbgv
106+
- name: Setup dotnet
107+
uses: actions/setup-dotnet@v4
108+
with:
109+
dotnet-version: '10.0.x'
110+
- name: Setup dotnet (SDK 11 preview)
111+
uses: actions/setup-dotnet@v4
112+
with:
113+
dotnet-version: '11.0.x'
114+
dotnet-quality: 'preview'
115+
# deliberately unpinned. Pinning sqlserver-version: '2019' - to match AppVeyor and the Ubuntu
116+
# job's container - hung this step for 28 minutes before the run was cancelled, where the
117+
# action's default installs in under three: SQL Server 2019 predates the Windows Server 2025
118+
# runner image. So the net481 leg runs against a newer engine than AppVeyor used; the OLEDB
119+
# tests are the ones to watch, since they go through the legacy SQLOLEDB provider.
120+
- name: Install SQL Server
121+
uses: ankane/setup-sqlserver@v1
122+
with:
123+
accept-eula: true
124+
# the install leaves TCP/IP disabled, as a Windows SQL Server install does by default, so
125+
# nothing is listening on 1433; these connect to the local default instance over shared
126+
# memory instead. Fails fast and legibly here rather than as several hundred identical
127+
# "connection refused" test failures
128+
- name: Verify SQL Server is reachable
129+
run: sqlcmd -S . -U sa -P 'YourStrong!Passw0rd' -Q "select @@version"
130+
- name: .NET Build
131+
run: dotnet build Build.csproj -c Release /p:CI=true
132+
- name: Dapper Tests (net481)
133+
run: dotnet test tests/Dapper.Tests/Dapper.Tests.csproj -c Release -f net481 --no-build --logger GitHubActions -p:CI=true
134+
env:
135+
OLEDBConnectionString: Provider=SQLOLEDB;Data Source=.;Initial Catalog=tempdb;User Id=sa;Password=YourStrong!Passw0rd;
136+
SqlServerConnectionString: Server=.;Database=tempdb;User Id=sa;Password=YourStrong!Passw0rd;

‎.github/workflows/release.yml‎

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Publishes the Dapper packages to nuget.org when a GitHub Release is published.
2+
#
3+
# Auth is NuGet Trusted Publishing (OIDC): no long-lived API key is stored anywhere. This
4+
# replaces the AppVeyor MyGet deploy, which was the only automated push this repo had.
5+
#
6+
# One-time setup on nuget.org, for *each* package below: username menu -> Trusted Publishing ->
7+
# add a policy with
8+
# Repository Owner: DapperLib / Repository: Dapper / Workflow File: release.yml
9+
# Environment: release
10+
# plus a NUGET_USER repository secret holding the nuget.org profile name to publish as, and a
11+
# GitHub environment named "release". The packages are:
12+
# Dapper, Dapper.StrongName, Dapper.EntityFramework, Dapper.EntityFramework.StrongName,
13+
# Dapper.Rainbow, Dapper.SqlBuilder
14+
# A package with no matching policy fails its push, so all six need one before the first release.
15+
#
16+
# Versioning note: Nerdbank.GitVersioning computes the version from version.json plus commit
17+
# height - the tag name does not set it. Read the version to tag off a green main run's step
18+
# summary ("Report computed version" in main.yml) and create the release with exactly that tag;
19+
# the guard step below fails the run on any mismatch rather than publishing a package that
20+
# disagrees with its release. A leading "v" is accepted and stripped, matching
21+
# publicReleaseRefSpec in version.json.
22+
#
23+
# Deliberately does not run tests: the commit being released already passed CI, and this job's
24+
# only job is to reproduce that build and push it. Runs on Ubuntu to match main.yml's build leg,
25+
# which is the configuration CI actually proves.
26+
27+
name: Release
28+
29+
on:
30+
release:
31+
types: [published]
32+
# dry run: everything except the tag check and the push, so the pipeline can be proven
33+
# (and the packages inspected, via the run artifact) without cutting a release
34+
workflow_dispatch:
35+
36+
jobs:
37+
publish:
38+
runs-on: ubuntu-latest
39+
environment: release
40+
permissions:
41+
id-token: write # for the OIDC exchange with nuget.org
42+
contents: read
43+
44+
steps:
45+
- name: Checkout code
46+
uses: actions/checkout@v4
47+
with:
48+
fetch-depth: 0 # depth is needed for nbgv
49+
50+
- name: Setup dotnet
51+
uses: actions/setup-dotnet@v4
52+
with:
53+
dotnet-version: '10.0.x'
54+
# same pair as main.yml, so the release build uses the same toolchain CI proved; global.json
55+
# (rollForward:latestMajor) is the single lever that decides which one of them is used
56+
- name: Setup dotnet (SDK 11 preview)
57+
uses: actions/setup-dotnet@v4
58+
with:
59+
dotnet-version: '11.0.x'
60+
dotnet-quality: 'preview'
61+
62+
- uses: dotnet/nbgv@master
63+
with:
64+
setAllVars: true
65+
66+
- name: Verify tag matches computed version
67+
if: github.event_name == 'release'
68+
run: |
69+
computed=$(nbgv get-version --variable NuGetPackageVersion)
70+
tag="${TAG_NAME#v}"
71+
if [ "$computed" != "$tag" ]; then
72+
echo "::error::Tag '$tag' does not match the computed version '$computed'; retag the release commit so the two agree."
73+
exit 1
74+
fi
75+
echo "### Publishing: \`$computed\`" >> "$GITHUB_STEP_SUMMARY"
76+
echo "Publishing $computed"
77+
env:
78+
TAG_NAME: ${{ github.event.release.tag_name }}
79+
80+
- name: .NET Build
81+
run: dotnet build Build.csproj -c Release /p:CI=true
82+
83+
# not ".nupkgs": actions/upload-artifact excludes hidden files by default, so a dot-directory
84+
# uploads as an empty artifact (with only a warning)
85+
- name: .NET Lib Pack
86+
run: dotnet pack Build.csproj --no-build -c Release -p:PackageOutputPath="$GITHUB_WORKSPACE/nupkgs" -p:CI=true
87+
88+
- name: List packages
89+
run: ls -l nupkgs/
90+
91+
# the packages survive as a run artifact even if the push fails
92+
- name: Upload packages
93+
uses: actions/upload-artifact@v4
94+
with:
95+
name: packages
96+
path: nupkgs/*.nupkg
97+
98+
- name: NuGet login (OIDC to temp API key)
99+
if: github.event_name == 'release'
100+
uses: NuGet/login@v1
101+
id: login
102+
with:
103+
user: ${{ secrets.NUGET_USER }}
104+
105+
# --skip-duplicate so a partially-completed push is safely re-runnable
106+
- name: Push to nuget.org
107+
if: github.event_name == 'release'
108+
run: |
109+
shopt -s nullglob
110+
packages=(nupkgs/*.nupkg)
111+
if [ ${#packages[@]} -eq 0 ]; then echo "::error::no packages to push"; exit 1; fi
112+
for p in "${packages[@]}"; do
113+
echo "Pushing $p"
114+
dotnet nuget push "$p" --api-key "${{ steps.login.outputs.NUGET_API_KEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate
115+
done

‎Dapper.ProviderTools/Dapper.ProviderTools.csproj‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
<Authors>Marc Gravell</Authors>
88
<TargetFrameworks>net461;netstandard2.0;net8.0</TargetFrameworks>
99
<SignAssembly>true</SignAssembly>
10+
<!-- not shipped: last release was 2.0.90 (April 2021) and it has had no work since.
11+
Packing it would push a 2.1.x of a dormant package on the next release, and would
12+
need a nuget.org trusted-publishing policy kept alive for it. Un-set this to
13+
resume shipping it. -->
14+
<IsPackable>false</IsPackable>
1015
<Nullable>enable</Nullable>
1116
</PropertyGroup>
1217
<ItemGroup>

‎Dapper.SqlBuilder/Readme.md‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
Dapper.SqlBuilder - a simple sql formatter for .Net
22
========================================
3-
[![Build status](https://ci.appveyor.com/api/projects/status/1w448i6nfxd14w75?svg=true)](https://ci.appveyor.com/project/StackExchange/dapper-SqlBuilder)
3+
[![Build](https://github.com/DapperLib/Dapper/actions/workflows/main.yml/badge.svg)](https://github.com/DapperLib/Dapper/actions/workflows/main.yml)
44

55
Packages
66
--------
77

8-
MyGet Pre-release feed: https://www.myget.org/gallery/dapper
9-
10-
| Package | NuGet Stable | NuGet Pre-release | Downloads | MyGet |
11-
| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
12-
| [Dapper.SqlBuilder](https://www.nuget.org/packages/Dapper.SqlBuilder/) | [![Dapper.SqlBuilder](https://img.shields.io/nuget/v/Dapper.SqlBuilder.svg)](https://www.nuget.org/packages/Dapper.SqlBuilder/) | [![Dapper.SqlBuilder](https://img.shields.io/nuget/vpre/Dapper.SqlBuilder.svg)](https://www.nuget.org/packages/Dapper.SqlBuilder/) | [![Dapper.SqlBuilder](https://img.shields.io/nuget/dt/Dapper.SqlBuilder.svg)](https://www.nuget.org/packages/Dapper.SqlBuilder/) | [![Dapper.SqlBuilder MyGet](https://img.shields.io/myget/dapper/vpre/Dapper.SqlBuilder.svg)](https://www.myget.org/feed/dapper/package/nuget/Dapper.SqlBuilder) |
8+
| Package | NuGet Stable | NuGet Pre-release | Downloads |
9+
| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
10+
| [Dapper.SqlBuilder](https://www.nuget.org/packages/Dapper.SqlBuilder/) | [![Dapper.SqlBuilder](https://img.shields.io/nuget/v/Dapper.SqlBuilder.svg)](https://www.nuget.org/packages/Dapper.SqlBuilder/) | [![Dapper.SqlBuilder](https://img.shields.io/nuget/vpre/Dapper.SqlBuilder.svg)](https://www.nuget.org/packages/Dapper.SqlBuilder/) | [![Dapper.SqlBuilder](https://img.shields.io/nuget/dt/Dapper.SqlBuilder.svg)](https://www.nuget.org/packages/Dapper.SqlBuilder/) |
1311

1412
Features
1513
--------

‎Directory.Packages.props‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.8" />
77
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
88
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
9-
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.300" />
9+
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="11.0.100-rc.1.26425.128" />
1010
<PackageVersion Include="Microsoft.SqlServer.Types" Version="14.0.1016.290" />
1111
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.9.50" />
1212
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="3.3.4" />

0 commit comments

Comments
 (0)