Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Two ecosystems, on purpose narrow.
#
# github-actions keeps the SHA-pinned actions in the workflows current — a pin without an update
# path just ages. Weekly, one grouped PR, so a security release in an action is not waiting a month.
#
# nuget covers the test projects only. The shipping projects under src/ are deliberately excluded:
# their EF Core provider and NodaTime references are the floors consumers restore against —
# raising one raises every consumer's minimum, which is a release decision, not a dependency
# update. The test projects' EF Core and Npgsql references track those floors for the same reason
# and are ignored here; bump them by hand together with the floor. Security advisories still
# surface for everything through Dependabot security updates and NuGetAudit at build time.
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
groups:
actions:
patterns: [ "*" ]

- package-ecosystem: nuget
directories:
- /test/*
schedule:
interval: monthly
groups:
test-dependencies:
patterns: [ "*" ]
ignore:
- dependency-name: "Microsoft.EntityFrameworkCore*"
- dependency-name: "Npgsql*"
50 changes: 41 additions & 9 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
# `dotnet test` and cost minutes to fix — cheap to discover late. Reinstate it if outside PRs
# become common.
#
# Actions are pinned to commit SHAs, not tags. A tag can be moved to a different commit — that is
# how the tj-actions/changed-files compromise (March 2025) reached every workflow that trusted
# `@v45` — a SHA cannot. The trailing `# vX.Y.Z` is what Dependabot reads to propose an update,
# so keep it next to the SHA (see .github/dependabot.yml).
#
# For information on GitHub Actions with .NET, see:
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net

Expand Down Expand Up @@ -51,10 +56,10 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0

- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
with:
dotnet-version: 10.0.x

Expand All @@ -78,7 +83,7 @@ jobs:
done

- name: Upload test results
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
if: always()
with:
name: test-results-ubuntu
Expand All @@ -90,10 +95,10 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0

- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
with:
dotnet-version: 10.0.x

Expand All @@ -116,23 +121,42 @@ jobs:
--results-directory TestResults

- name: Upload test results
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
if: always()
with:
name: test-results-integration
path: TestResults/*.trx
if-no-files-found: ignore

# The only job that exercises the delivery chain rather than the code: NuGet restore against the
# packed feed, the nuspec dependency chain, the public surface as packaged, and the EF plugins
# registering from the packaged assemblies. Deliberately independent of the other jobs so it
# reports in parallel. No database is involved — ToQueryString never connects.
consumer:
name: Consumer smoke test
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0

- name: Setup .NET
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
with:
dotnet-version: 10.0.x

- name: Restore the packages into a throwaway consumer project
run: ./test/consumer-smoke-test.sh

pack:
name: Pack
runs-on: ubuntu-latest
needs: [ test, integration ]

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0

- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
with:
dotnet-version: 10.0.x

Expand Down Expand Up @@ -161,6 +185,14 @@ jobs:
printf '%-60s %s\n' "$id" "$heading"
done

# The smoke test's *other* invocation mode. The `consumer` job runs it with no argument, where
# it packs its own feed; here it is handed a pre-built one, exactly as the release workflow
# does. The two are not interchangeable — packing pre-populates NUGET_PACKAGES with the
# solution's dependencies, and a restore that only works because of that side effect passes
# one way and fails the other. That difference broke a release in the sibling repository.
- name: Consumer smoke test against the packed feed
run: ./test/consumer-smoke-test.sh dist

# Generating here too means a broken SBOM step surfaces on a PR rather than on release day.
#
# No --exclude-filter: unlike sibling projects, nothing here is referenced with
Expand All @@ -179,7 +211,7 @@ jobs:
done

- name: Upload packages
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: nupkg
path: |
Expand Down
26 changes: 18 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
# The job that can mint a token cannot write to the repository, and the job that can write to
# the repository cannot mint a token.
#
# Actions are pinned to commit SHAs, not tags — this is the publish path, and NuGet/login runs in
# the one job that can mint a token. A moved tag reaches every workflow trusting it; a SHA does
# not. The trailing `# vX.Y.Z` is what Dependabot reads to propose an update; keep it by the SHA.
#
# Three names must agree with the nuget.org policy, or it silently stops matching: this file's
# *name* (release.yml), the environment (`nuget`), and the repository owner/name.
# ReleaseWiringConventionTests pins the first two.
Expand Down Expand Up @@ -63,10 +67,10 @@ jobs:
version: ${{ steps.version.outputs.version }}

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0

- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
with:
dotnet-version: 10.0.x

Expand Down Expand Up @@ -108,6 +112,12 @@ jobs:
- name: Pack
run: dotnet pack -c Release --no-build -o dist

# Installs the packages just packed into throwaway projects outside this repository and runs
# real code against them. Passing dist means the artifacts published are the artifacts tested,
# rather than a fresh pack that happens to be built from the same commit.
- name: Consumer smoke test
run: ./test/consumer-smoke-test.sh dist

# No --exclude-filter: nothing in this repository is referenced with PrivateAssets=all, so
# every dependency in the graph is one a consumer actually takes on and the SBOM matches
# the nuspec. ReleaseWiringConventionTests fails if that stops being true.
Expand Down Expand Up @@ -140,7 +150,7 @@ jobs:
printf ' %s\n' "${packages[@]##*/}"

- name: Upload packages
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: nupkg-${{ steps.version.outputs.version }}
path: |
Expand All @@ -165,13 +175,13 @@ jobs:

steps:
- name: Setup .NET
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
with:
dotnet-version: 10.0.x

# Publishes exactly what was verified — the packages are not rebuilt here.
- name: Download packages
uses: actions/download-artifact@v7
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: nupkg-${{ needs.verify.outputs.version }}
path: dist
Expand All @@ -189,7 +199,7 @@ jobs:
# publishing policy, valid for one hour. The token is single-use and nothing is stored.
- name: NuGet login (OIDC)
id: login
uses: NuGet/login@v1
uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1.2.0
with:
user: ${{ secrets.NUGET_USER }}

Expand Down Expand Up @@ -252,10 +262,10 @@ jobs:

steps:
# The checkout is for CHANGELOG.md, the source of the release notes.
- uses: actions/checkout@v6
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0

- name: Download packages
uses: actions/download-artifact@v7
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: nupkg-${{ needs.verify.outputs.version }}
path: dist
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ so moving a project cannot silently retarget a test.
- `test/CodoMetis.ValueRanges.EFCore.PostgreSQL.NodaTime.Tests/` — NodaTime EF translation tests (no database)
- `test/CodoMetis.ValueRanges.EFCore.PostgreSQL.IntegrationTests/` — Live PostgreSQL via Testcontainers (needs Docker; Inconclusive without, but hard failure under CI=true). Covers BCL and NodaTime types. Authority on PostgreSQL semantics — run when changing translations or range algebra
- `test/CodoMetis.ValueRanges.Conventions.Tests/` — Repo-level conventions: changelog consistency, packaging metadata, release wiring, the SECURITY.md supported-versions table, value set contract compliance, EF mapping parity. Everything is discovered (projects by globbing `src/`, types by reflection), so adding a package or a type needs no edit here
- `test/consumer-smoke-test.sh` — The only check of *delivery* rather than code: packs the four packages, restores them into throwaway projects created outside the repository (inside it, `Directory.Build.props` would apply), compiles and runs real code against them, and asserts on printed output and translated SQL — never on the exit code alone. Two consumers: core only, and the NodaTime EF satellite, which pulls in all four through the nuspec chain. `NUGET_PACKAGES` is redirected to a private folder because NuGet resolves id+version from the global cache before any source, so a locally built package is otherwise shadowed by whatever build of that version was restored before; the generated `nuget.config` uses package source mapping so this repo's ids come from the local feed only and everything else from nuget.org. Runs on every PR in both modes (own feed; the pre-built `dist` the release workflow passes). Everything is discovered (projects by globbing `src/`, types by reflection), so adding a package or a type needs no edit here
- `docs/` — Agent docs (read relevant doc before starting work)

## Commands
Expand Down
Loading
Loading