Skip to content

fix: restore SourceLink and deterministic builds in published packages#5579

Merged
thomhurst merged 1 commit intomainfrom
fix/sourcelink-deterministic
Apr 16, 2026
Merged

fix: restore SourceLink and deterministic builds in published packages#5579
thomhurst merged 1 commit intomainfrom
fix/sourcelink-deterministic

Conversation

@thomhurst
Copy link
Copy Markdown
Owner

Summary

  • Remove explicit Microsoft.SourceLink.GitHub GlobalPackageReference; rely on SDK 10's implicit SourceLink SDK instead
  • Fixes published .nupkg/.snupkg showing "No Source Link" and "Non deterministic" in NuGet Package Explorer

Root cause

The GlobalPackageReference "Microsoft.SourceLink.GitHub" PrivateAssets="all" made NuGet:

  1. Restore Common + Build.Tasks.Git transitively as buildTransitive/_._ (empty marker — developmentDependency packages don't flow through private refs)
  2. Still set PkgMicrosoft_SourceLink_Common path property

Microsoft.NET.Sdk.SourceLink.props then flips SuppressImplicitGitSourceLink=true, skipping SDK-bundled Common/Build.Tasks.Git imports. Only Microsoft.SourceLink.GitHub.targets imported → LocateRepository task and InitializeSourceControlInformationFromSourceControlManager target never ran → @(SourceRoot) empty → no SourceLink JSON generated, no PathMap normalization, non-deterministic paths in PE.

Fix

Delete the GlobalPackageReference. .NET 10 SDK bundles SourceLink at $(DotnetRoot)/sdk/<ver>/Sdks/Microsoft.SourceLink.*. With no NuGet ref, PkgMicrosoft_SourceLink_Common stays empty, suppression doesn't fire, SDK auto-imports all three (Common + Build.Tasks.Git + GitHub).

Test plan

  • MSBuild preprocess (-pp) shows SDK's implicit imports activating (InitializeSourceControlInformationFromSourceControlManager, LocateRepository, GenerateSourceLinkFile)
  • obj/*/TUnit.Core.sourcelink.json generated with {"/_/*":"https://raw.githubusercontent.com/thomhurst/TUnit/<sha>/*"}
  • Built TUnit.Core + TUnit.Assertions × {netstandard2.0, net8.0, net9.0, net10.0}: each DLL has Reproducible debug entry, each PDB has SourceLink CDI (cc110556-a091-4d38-9fec-25ab9a351a6a)
  • CI build produces packages that pass "Source Link" and "Deterministic" checks in NuGet Package Explorer

Remove the explicit `Microsoft.SourceLink.GitHub` GlobalPackageReference so
the .NET 10 SDK's implicit SourceLink activates.

The reference caused NuGet to set `PkgMicrosoft_SourceLink_Common`, which
triggered `SuppressImplicitGitSourceLink=true` in
`Microsoft.NET.Sdk.SourceLink.props`. Transitive deps (Common,
Build.Tasks.Git) were resolved as `buildTransitive/_._` empty markers, so
their targets never imported. Result: `LocateRepository` and
`InitializeSourceControlInformationFromSourceControlManager` never ran,
`@(SourceRoot)` stayed empty, no SourceLink JSON was generated, and paths
were not PathMap-normalized (breaking determinism).

With the reference removed, the SDK imports Common + Build.Tasks.Git +
GitHub from its bundled Sdks folder. Verified on TUnit.Core and
TUnit.Assertions across netstandard2.0/net8.0/net9.0/net10.0: Reproducible
debug entry present and SourceLink CDI embedded in each PDB.
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Summary

This is a minimal, well-reasoned fix: a 3-line deletion that removes the explicit GlobalPackageReference for Microsoft.SourceLink.GitHub and relies on the .NET 10 SDK's implicit SourceLink integration instead.

Analysis

The root cause diagnosis in the PR description is accurate and thorough. The suppression chain is:

  1. GlobalPackageReference with PrivateAssets="all" causes NuGet to resolve PkgMicrosoft_SourceLink_Common
  2. Microsoft.NET.Sdk.SourceLink.props detects the non-empty property and sets SuppressImplicitGitSourceLink=true
  3. Only Microsoft.SourceLink.GitHub.targets gets imported — without Common + Build.Tasks.Git, LocateRepository never runs
  4. Empty @(SourceRoot) → no SourceLink JSON → no PathMap normalization → non-deterministic PEs

Removing the NuGet reference keeps PkgMicrosoft_SourceLink_Common empty, suppression never fires, and the SDK's bundled SourceLink takes over cleanly.

Prerequisites correctly in place

Directory.Build.props already has the required companion properties:

  • PublishRepositoryUrl=true — embeds the repo URL in the package metadata
  • EmbedUntrackedSources=true — includes files not tracked by Git in the PDB
  • ContinuousIntegrationBuild=true — enables deterministic source paths on CI
  • SymbolPackageFormat=snupkg — publishes symbols as a separate .snupkg

Nothing else needs changing.

SDK version lock is appropriate

global.json pins 10.0.202 (with rollForward: latestMajor), matching the version of the removed GlobalPackageReference. The implicit SourceLink support has been available since SDK 8.0.2xx, so this is safe across the project's target frameworks (net8.0 / net9.0 / net10.0).

One consideration worth noting

The PrivateAssets="all" on the removed reference meant the SourceLink package was a development-only dependency and never leaked into consumers' dependency graphs — the SDK-bundled approach behaves the same way, so there's no regression there.

Verdict

The fix is correct, minimal, and well-documented. The PR description contains more explanation than the diff, which is exactly right for a build-tooling change like this. No issues found.

@codacy-production
Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity

Metric Results
Complexity 0

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

@thomhurst thomhurst enabled auto-merge (squash) April 16, 2026 19:45
@thomhurst thomhurst merged commit be45677 into main Apr 16, 2026
15 checks passed
@thomhurst thomhurst deleted the fix/sourcelink-deterministic branch April 16, 2026 20:13
@claude claude bot mentioned this pull request Apr 16, 2026
1 task
intellitect-bot pushed a commit to IntelliTect/EssentialCSharp.Web that referenced this pull request Apr 17, 2026
Updated [TUnit](https://github.com/thomhurst/TUnit) from 1.33.0 to
1.35.2.

<details>
<summary>Release notes</summary>

_Sourced from [TUnit's
releases](https://github.com/thomhurst/TUnit/releases)._

## 1.35.2

<!-- Release notes generated using configuration in .github/release.yml
at v1.35.2 -->

## What's Changed
### Other Changes
* fix: restore SourceLink and deterministic builds in published packages
by @​thomhurst in thomhurst/TUnit#5579
### Dependencies
* chore(deps): update tunit to 1.35.0 by @​thomhurst in
thomhurst/TUnit#5578


**Full Changelog**:
thomhurst/TUnit@v1.35.0...v1.35.2

## 1.35.0

<!-- Release notes generated using configuration in .github/release.yml
at v1.35.0 -->

## What's Changed
### Other Changes
* fix: support open generic transitive auto-mocks by @​thomhurst in
thomhurst/TUnit#5568
* refactor: separate test and lifecycle tracing by @​thomhurst in
thomhurst/TUnit#5572
* fix: expand nested And/Or expectations in failure messages (#​5573) by
@​thomhurst in thomhurst/TUnit#5577
### Dependencies
* chore(deps): update tunit to 1.34.5 by @​thomhurst in
thomhurst/TUnit#5566
* chore(deps): bump follow-redirects from 1.15.11 to 1.16.0 in /docs by
@​dependabot[bot] in thomhurst/TUnit#5538
* chore(deps): update verify to 31.16.0 by @​thomhurst in
thomhurst/TUnit#5570
* chore(deps): update verify to 31.16.1 by @​thomhurst in
thomhurst/TUnit#5574
* chore(deps): update gittools/actions action to v4 by @​thomhurst in
thomhurst/TUnit#5575


**Full Changelog**:
thomhurst/TUnit@v1.34.5...v1.35.0

## 1.34.5

<!-- Release notes generated using configuration in .github/release.yml
at v1.34.5 -->

## What's Changed
### Other Changes
* fix: cap test output at 1M chars to prevent OOM by @​thomhurst in
thomhurst/TUnit#5561
* fix: handle explicit interface impl with different return types in
mock generator by @​thomhurst in
thomhurst/TUnit#5564
* fix: include XML documentation files in NuGet packages by @​thomhurst
in thomhurst/TUnit#5565
### Dependencies
* chore(deps): update tunit to 1.34.0 by @​thomhurst in
thomhurst/TUnit#5562


**Full Changelog**:
thomhurst/TUnit@v1.34.0...v1.34.5

## 1.34.0

<!-- Release notes generated using configuration in .github/release.yml
at v1.34.0 -->

## What's Changed
### Other Changes
* refactor: move CorrelatedTUnitLogger to TUnit.Logging.Microsoft and
auto-inject handlers by @​thomhurst in
thomhurst/TUnit#5532
* feat: add Dev Drive setup for Windows in CI workflow by @​thomhurst in
thomhurst/TUnit#5544
* fix: start session activity before discovery so discovery spans parent
correctly by @​thomhurst in thomhurst/TUnit#5534
* feat: cross-process test log correlation via OTLP receiver by
@​thomhurst in thomhurst/TUnit#5533
* refactor: use natural OTEL trace propagation instead of synthetic
TraceIds by @​thomhurst in thomhurst/TUnit#5557
* fix: route ITestOutput writes through synchronized
ConcurrentStringWriter by @​thomhurst in
thomhurst/TUnit#5558
### Dependencies
* chore(deps): update tunit to 1.33.0 by @​thomhurst in
thomhurst/TUnit#5527
* chore(deps): update dependency dompurify to v3.4.0 by @​thomhurst in
thomhurst/TUnit#5537
* chore(deps): update dependency docusaurus-plugin-llms to ^0.3.1 by
@​thomhurst in thomhurst/TUnit#5541
* chore(deps): update dependency microsoft.sourcelink.github to 10.0.202
by @​thomhurst in thomhurst/TUnit#5543
* chore(deps): update dependency microsoft.entityframeworkcore to 10.0.6
by @​thomhurst in thomhurst/TUnit#5542
* chore(deps): update dependency
microsoft.templateengine.authoring.templateverifier to 10.0.202 by
@​thomhurst in thomhurst/TUnit#5546
* chore(deps): update dependency microsoft.templateengine.authoring.cli
to v10.0.202 by @​thomhurst in
thomhurst/TUnit#5545
* chore(deps): update dependency system.commandline to 2.0.6 by
@​thomhurst in thomhurst/TUnit#5547
* chore(deps): update microsoft.aspnetcore to 10.0.6 by @​thomhurst in
thomhurst/TUnit#5548
* chore(deps): update dependency nuget.protocol to 7.3.1 by @​thomhurst
in thomhurst/TUnit#5549
* chore(deps): update microsoft.extensions to 10.0.6 by @​thomhurst in
thomhurst/TUnit#5550
* chore(deps): update dependency dotnet-sdk to v10.0.202 by @​thomhurst
in thomhurst/TUnit#5551
* chore(deps): update opentelemetry by @​thomhurst in
thomhurst/TUnit#5552
* chore(deps): update microsoft.extensions to 10.5.0 by @​thomhurst in
thomhurst/TUnit#5554


**Full Changelog**:
thomhurst/TUnit@v1.33.0...v1.34.0

Commits viewable in [compare
view](thomhurst/TUnit@v1.33.0...v1.35.2).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=TUnit&package-manager=nuget&previous-version=1.33.0&new-version=1.35.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This was referenced Apr 17, 2026
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