fix: restore SourceLink and deterministic builds in published packages#5579
fix: restore SourceLink and deterministic builds in published packages#5579
Conversation
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.
There was a problem hiding this comment.
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:
GlobalPackageReferencewithPrivateAssets="all"causes NuGet to resolvePkgMicrosoft_SourceLink_CommonMicrosoft.NET.Sdk.SourceLink.propsdetects the non-empty property and setsSuppressImplicitGitSourceLink=true- Only
Microsoft.SourceLink.GitHub.targetsgets imported — withoutCommon+Build.Tasks.Git,LocateRepositorynever runs - 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 metadataEmbedUntrackedSources=true— includes files not tracked by Git in the PDBContinuousIntegrationBuild=true— enables deterministic source paths on CISymbolPackageFormat=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.
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 0 |
TIP This summary will be updated as you push new changes. Give us feedback
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> [](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>
Summary
Microsoft.SourceLink.GitHubGlobalPackageReference; rely on SDK 10's implicit SourceLink SDK instead.nupkg/.snupkgshowing "No Source Link" and "Non deterministic" in NuGet Package ExplorerRoot cause
The
GlobalPackageReference "Microsoft.SourceLink.GitHub" PrivateAssets="all"made NuGet:buildTransitive/_._(empty marker — developmentDependency packages don't flow through private refs)PkgMicrosoft_SourceLink_Commonpath propertyMicrosoft.NET.Sdk.SourceLink.propsthen flipsSuppressImplicitGitSourceLink=true, skipping SDK-bundled Common/Build.Tasks.Git imports. OnlyMicrosoft.SourceLink.GitHub.targetsimported →LocateRepositorytask andInitializeSourceControlInformationFromSourceControlManagertarget 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_Commonstays empty, suppression doesn't fire, SDK auto-imports all three (Common + Build.Tasks.Git + GitHub).Test plan
-pp) shows SDK's implicit imports activating (InitializeSourceControlInformationFromSourceControlManager,LocateRepository,GenerateSourceLinkFile)obj/*/TUnit.Core.sourcelink.jsongenerated with{"/_/*":"https://raw.githubusercontent.com/thomhurst/TUnit/<sha>/*"}Reproducibledebug entry, each PDB has SourceLink CDI (cc110556-a091-4d38-9fec-25ab9a351a6a)