From 77c7e44581a55172d064c63fb0fb335e4c793701 Mon Sep 17 00:00:00 2001 From: Kieron Lanning Date: Wed, 7 Oct 2026 00:21:00 +0100 Subject: [PATCH] ci: make global.json the single source of truth for the SDK version The shared workflows installed whatever dotnet-version said and left global.json to fend for itself. Those two can disagree, and when they do the job cannot recover: every dotnet command resolves against global.json, so the first one - `dotnet tool install Purview.Build` - fails with "A compatible .NET SDK was not found" and takes the run with it. A consuming repository had to state its SDK three times (global.json, pr.yml, release.yml) and keep the copies in step by hand, with nothing checking that it had. Both workflows now read sdk.version from global.json and install exactly that. The dotnet-version input still applies when global.json pins nothing, so every repository that does not pin keeps the behaviour it has today. Verified against all eight consuming repositories: zodsharp, value-objects and results resolve their pinned 11.0.100-rc.1.26425.128, and build-sdk, sourcegenerator-framework, telemetry-sourcegenerator, aspirec4 and aspire-resourcekit fall back to the 10.0.x input unchanged. Consumers can drop their explicit dotnet-version once this is on main; until then it is simply redundant rather than wrong, because an explicit value that matches global.json installs the same SDK. --- .github/workflows/purview-build.yml | 34 +++++++++++++++++++++++++-- .github/workflows/purview-release.yml | 28 ++++++++++++++++++++-- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/.github/workflows/purview-build.yml b/.github/workflows/purview-build.yml index 012256d..086810c 100644 --- a/.github/workflows/purview-build.yml +++ b/.github/workflows/purview-build.yml @@ -10,7 +10,10 @@ on: required: false type: string dotnet-version: - description: .NET SDK used by the consuming repository. + description: >- + .NET SDK used by the consuming repository. Ignored when global.json pins an sdk.version, + because every dotnet command resolves against global.json and installing anything else + fails the job. required: false default: 10.0.x type: string @@ -77,9 +80,36 @@ jobs: fetch-depth: 0 fetch-tags: true + # global.json is the single source of truth for the SDK whenever it pins one. Every dotnet + # command in the job resolves against it, so installing anything else fails the whole job at + # the first one - "A compatible .NET SDK was not found" - no matter what dotnet-version says. + # Deriving it here means a consuming repository states its SDK once, instead of repeating it + # in global.json, pr.yml and release.yml and keeping three copies in step by hand. + # + # When global.json pins nothing, the dotnet-version input applies, which is the behaviour + # every repository had before. + - name: Resolve the .NET SDK version + id: resolve-sdk + shell: bash + run: | + set -euo pipefail + + pinned="" + if [ -f global.json ]; then + pinned="$(jq -r '.sdk.version // empty' global.json)" + fi + + if [ -n "$pinned" ]; then + echo "global.json pins $pinned; installing that." + echo "dotnet-version=$pinned" >> "$GITHUB_OUTPUT" + else + echo "global.json pins no sdk.version; using the dotnet-version input (${{ inputs.dotnet-version }})." + echo "dotnet-version=${{ inputs.dotnet-version }}" >> "$GITHUB_OUTPUT" + fi + - uses: actions/setup-dotnet@v6 with: - dotnet-version: ${{ inputs.dotnet-version }} + dotnet-version: ${{ steps.resolve-sdk.outputs.dotnet-version }} - uses: oven-sh/setup-bun@v2 diff --git a/.github/workflows/purview-release.yml b/.github/workflows/purview-release.yml index b3dbcfb..280d62f 100644 --- a/.github/workflows/purview-release.yml +++ b/.github/workflows/purview-release.yml @@ -10,7 +10,10 @@ on: required: false type: string dotnet-version: - description: .NET SDK used by the consuming repository. + description: >- + .NET SDK used by the consuming repository. Ignored when global.json pins an sdk.version, + because every dotnet command resolves against global.json and installing anything else + fails the job. required: false default: 10.0.x type: string @@ -119,9 +122,30 @@ jobs: fetch-depth: 0 fetch-tags: true + # global.json is the single source of truth for the SDK whenever it pins one; see the matching + # step in purview-build.yml. When it pins nothing, the dotnet-version input applies. + - name: Resolve the .NET SDK version + id: resolve-sdk + shell: bash + run: | + set -euo pipefail + + pinned="" + if [ -f global.json ]; then + pinned="$(jq -r '.sdk.version // empty' global.json)" + fi + + if [ -n "$pinned" ]; then + echo "global.json pins $pinned; installing that." + echo "dotnet-version=$pinned" >> "$GITHUB_OUTPUT" + else + echo "global.json pins no sdk.version; using the dotnet-version input (${{ inputs.dotnet-version }})." + echo "dotnet-version=${{ inputs.dotnet-version }}" >> "$GITHUB_OUTPUT" + fi + - uses: actions/setup-dotnet@v6 with: - dotnet-version: ${{ inputs.dotnet-version }} + dotnet-version: ${{ steps.resolve-sdk.outputs.dotnet-version }} - uses: oven-sh/setup-bun@v2