Skip to content

feat(ci): extract the global.json SDK resolution into actions/dotnet-sdk - #48

Merged
kieronlanning merged 1 commit into
mainfrom
feat/dotnet-sdk-action
Nov 11, 2026
Merged

kieronlanning merged 1 commit into
mainfrom
feat/dotnet-sdk-action

Conversation

@kieronlanning

Copy link
Copy Markdown
Contributor

Why

actions/setup-dotnet's global-json-file input resolves the version itself, and when it cannot install that version it falls back to the LTS channel, reports success, and leaves the job to fail at its first dotnet command:

A compatible .NET SDK was not found.
Requested SDK version: 11.0.100
global.json file: /home/runner/work/…/global.json
Installed SDKs: 8.0.133 … 10.0.404
##[error]Process completed with exit code 155.

purview-build.yml already avoided that by resolving .sdk.version from global.json and installing an explicit version — but ci.yml and release.yml carried their own copies of the script, and a consuming repository's own jobs had no way to reach it at all. purview-dev/telemetry-sourcegenerator PR #157 is the proof: its reusable-workflow build job is green, and its four hand-rolled pr.yml jobs all fail with the message above.

What

actions/dotnet-sdk is now the one implementation, following the nuget-sources precedent:

  • reads sdk.version from the caller's global.json and installs exactly that
  • uses its dotnet-version input (default 10.0.x) only when global.json pins nothing
  • exposes the resolved version as a dotnet-version output
  • no permissions: block, no vars/secrets context, so WorkflowParityTests still passes

Callers:

File Call
.github/workflows/purview-build.yml purview-dev/build/actions/dotnet-sdk@main, passing the workflow's dotnet-version input
ci.yml (×2), release.yml ./actions/dotnet-sdk — dogfooded from the working tree, as nuget-sources already is

The three duplicated resolve scripts are gone: −107/+31 lines across the workflows.

Validation

  • just lint-check (CSharpier + actionlint) — clean
  • just test — 1100 passed, 0 failed (184 + 898 + 18 across the three suites)
  • No version bump: the tool itself is unchanged, this is CI wiring. A consuming repository can call the action as soon as this merges, with no package release.

Follow-up

purview-dev/telemetry-sourcegenerator pr.yml will switch its four jobs (dependency-floor, samples, pack-validation, packed-consumers) to purview-dev/build/actions/dotnet-sdk@main once this is on main.

actions/setup-dotnet's global-json-file input resolves the version itself and, when it cannot
install it, falls back to the LTS channel and reports success. The job then fails at its first
dotnet command with "A compatible .NET SDK was not found", several steps after the message that
named the version.

purview-build.yml already resolved the pin from global.json and installed an explicit version, but
ci.yml and release.yml duplicated that script, and a consuming repository's own jobs had no way to
get it at all: telemetry-sourcegenerator's four pr.yml jobs hand-rolled setup-dotnet with
global-json-file and failed for exactly that reason.

This is the one implementation, following the nuget-sources precedent: the reusable workflow calls
it, ci.yml and release.yml dogfood it from ./actions, and a consuming job calls
purview-dev/build/actions/dotnet-sdk@main.
@kieronlanning
kieronlanning merged commit 4ca27a2 into main Nov 11, 2026
4 checks passed
@kieronlanning
kieronlanning deleted the feat/dotnet-sdk-action branch November 11, 2026 16:01
kieronlanning added a commit to purview-dev/telemetry-sourcegenerator that referenced this pull request Nov 11, 2026
Four jobs failed on PR #157 before running a single dotnet command: dependency-floor, samples,
pack-validation and packed-consumers each installed the SDK with actions/setup-dotnet's
global-json-file input, which resolved and installed LTS 10.0.404, reported success, and left the
pinned 11.0.100 unmet. rollForward: latestMajor cannot help - it only rolls forward.

purview-dev/build#48 extracted the resolution the reusable build workflow already did into
actions/dotnet-sdk, so the same one line replaces the broken input in all four jobs. The build job
was green throughout because it calls that workflow.
kieronlanning added a commit to purview-dev/telemetry-sourcegenerator that referenced this pull request Nov 11, 2026
* feat: updated net 11 as the new base

* feat: updated union support with tests

* perf(benchmarks): consume both sides of a comparison through the interface

MetricsBenchmarks and LoggerBenchmarks held the generated implementation as its interface and the
hand-written baseline as its concrete type. At nanosecond scale that is not a fair pair: a concrete
call is inlined into the measurement loop and an interface call is not, so the delta was dispatch
reported as generator overhead. Every generated metrics row came out at a flat 1.3 ns whatever the
instrument or tag count, and every disabled logging row at 1.13 ns against a 0.77 ns baseline.

ManualMetricsTelemetry and ManualLoggerMessageTelemetry now implement the benchmark interfaces, the
benchmark classes hold them as the interface, and the factories return them that way. The generated
and hand-written rows then track each other: 1.29-1.31 ns against 1.28-1.34 ns for instruments on
.NET 10.0, 1.11-1.13 ns for a disabled log call, within 3% on .NET Framework.

The developer skill records the rule, so a new family does not reintroduce the asymmetry.

* docs: refresh the performance tables from a fresh three-runtime run

A full Release run of all seven benchmark classes on .NET 10, .NET 11 and .NET Framework 4.8 (198
benchmarks, no NA rows), plus a like-for-like re-run of MetricsBenchmarks and LoggerBenchmarks after
their baselines were moved to the interface.

docs/wiki/Performance.md carries the new environment header, a row per runtime and parameter in
every family, a new "Generator model overhead" section for GeneratorModelBenchmarks, and rewritten
interpretations. The stale caveats are gone: .NET 11 is measured, no .NET 8/9 rows are missing, and
.NET Framework produced results. The raw-results link went with them, because
BenchmarkDotNet.Artifacts is gitignored and the link to it never resolved.

README.md keeps its .NET 10.0 digest and says the ratios compare against the hand-written
implementation of the same scenario. The logging claims are the ones that moved: v1 is at parity
rather than faster, and v2 is the fastest of the three on every runtime. The metrics table no
longer implies generated code is 1.7x slower, because it was not.

Platform-Support.md now cites the measured model comparison instead of saying the tables predate it.
AGENTS.md records the three benchmark target frameworks.

* ci: install the SDK from the shared actions/dotnet-sdk action

Four jobs failed on PR #157 before running a single dotnet command: dependency-floor, samples,
pack-validation and packed-consumers each installed the SDK with actions/setup-dotnet's
global-json-file input, which resolved and installed LTS 10.0.404, reported success, and left the
pinned 11.0.100 unmet. rollForward: latestMajor cannot help - it only rolls forward.

purview-dev/build#48 extracted the resolution the reusable build workflow already did into
actions/dotnet-sdk, so the same one line replaces the broken input in all four jobs. The build job
was green throughout because it calls that workflow.

* fix(build): drop dotnet-inspect from the tool manifest again

The pack-validation job fails at `dotnet tool restore` with

    The command "purview-build" specified in the tool manifest file is not contained in the
    package with Package Id purview.build. The commands contained in the package are
    dotnet-inspect.

That is 7427652's failure returning: 2f1c731 re-added the dotnet-inspect entry. The package is still
a DotNetCliTool Version="2" with RuntimeIdentifierPackages, laid out under tools/any/any, and beside
another tool NuGet validates the wrong package's commands on the Linux runner. It restores on
Windows, which is why the local loop never saw it. Nothing in the repository runs the tool.

Removed again, and ToolManifestTests now rejects it by name so it cannot come back a third time.
AGENTS.md records the manifest rule: csharpier and purview.build, nothing RID-specific.

Verified with an empty NUGET_PACKAGES folder and --no-cache: csharpier and purview.build restore,
and the guard fails when the entry is put back.

* ci: make the Native AOT prerequisite install survive a missing mirror

The packed-consumers job failed twice in one afternoon at

    E: Failed to fetch https://security.ubuntu.com/.../clang_18.0-59~exp2_amd64.deb
       Temporary failure resolving 'azure.archive.ubuntu.com'

Every other job in both runs was green, so this was the last red on PR #157. The mirror is the one
the runner's /etc/apt/apt-mirrors.txt points at, and the failure is DNS, not a package problem: the
apt call is the first thing the job does after the preflight, so nothing downstream of it had a
chance to run.

The step now probes with dpkg first - the ubuntu-latest image usually carries clang and zlib1g-dev,
in which case no network is touched at all - and retries the install with backoff three times when
it does not.

* ci: stop the Native AOT prerequisites step hanging on a dead mirror

The retry loop added in fadb8d2 made the failure worse: each apt call blocked on DNS for
azure.archive.ubuntu.com for minutes, three attempts in a row, so the step ran 24 minutes and still
failed. It is the only step in the job that needs the network, and the first after the preflight, so
nothing downstream ran at all.

The step now prefers what the image already has - a versioned clang-18 linked as clang when the
unversioned name is missing, and /usr/include/zlib.h - and only touches apt when a package really is
absent, with -o Acquire::Retries=0, 15s fetch timeouts, timeout(1) around each call and a 5 minute
step timeout. A dead mirror now costs seconds instead of half an hour.

If the image has neither a clang nor the headers, the next suspect is the runner label: the run
warns that ubuntu-latest migrated to Ubuntu 26 on 2026-10-19, and the mirror this job fails to reach
is the one that image's apt-mirrors.txt points at.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant