Skip to content

eng/common/msbuild.{sh,ps1} no longer exports NUGET_PACKAGES in CI (regression from #16814) #16898

@akoeplinger

Description

@akoeplinger

Note

This issue was drafted with AI/Copilot assistance.

Summary

After #16814 ("Remove the Arcade msbuild logger"), the MSBuild function in eng/common/tools.sh and eng/common/tools.ps1 no longer routes through InitializeToolset. As a side effect, GetNuGetPackageCachePath is no longer invoked, which means NUGET_PACKAGES is never exported when a repo enters Arcade via eng/common/msbuild.{sh,ps1} instead of eng/common/build.{sh,ps1}.

In CI, RepoLayout.props sets the MSBuild NuGetPackageRoot to $(RepoRoot)/.packages/ based on ContinuousIntegrationBuild=true, but NuGet's RestoreTaskEx no longer sees NUGET_PACKAGES and falls back to ~/.nuget/packages/. Restore and build now resolve different package roots; any generated obj/<proj>.nuget.g.props <Import> predicated on Exists('$(NuGetPackageRoot)…') is silently skipped.

Concrete failure

dotnet/runtime CI test build fails with:

src/tests/Common/external/external.csproj(81,5): error : looks the package Microsoft.DotNet.XUnitConsoleRunner is not restored or missing xunit.console.dll.

PkgMicrosoft_DotNet_XUnitConsoleRunner (baked in at restore time) correctly points at ~/.nuget/packages/microsoft.dotnet.xunitconsolerunner/…, but the package's own build/Microsoft.DotNet.XUnitConsoleRunner.props is never imported because the Import path is evaluated against the wrong root. XunitConsoleNetCoreAppPath is never defined → <Error> fires.

Diagnosis from binlog

Property (external.csproj evaluation) Value
NuGetPackageRoot (build-time, via RepoLayout.props) /Users/runner/work/1/s/.packages/
NuGetPackageFolders (from project.assets.json) /Users/runner/.nuget/packages/
PkgMicrosoft_DotNet_XUnitConsoleRunner /Users/runner/.nuget/packages/microsoft.dotnet.xunitconsolerunner/2.9.3-beta.26277.104
XunitConsoleNetCoreAppPath (unset)

Root cause

In the pre-#16814 code, the MSBuild function in tools.sh called:

if [[ "$pipelines_log" == true ]]; then
  InitializeBuildTool
  InitializeToolset      # ← calls GetNuGetPackageCachePath → exports NUGET_PACKAGES
  ...
fi

pipelines_log defaulted to true when ci=true, so any caller invoking eng/common/msbuild.sh in CI got NUGET_PACKAGES exported as a side effect. #16814 deleted the entire pipelines_log branch (along with the now-defunct logger plumbing), removing the InitializeToolset call. The PR description states "The surrounding pipelines_log block (NUGET timeout env vars, Enable-Nuget-EnhancedRetry, InitializeToolset) is retained", but the diff shows InitializeToolset was dropped along with the logger.

Affected entry points

  • eng/common/msbuild.sh / eng/common/msbuild.ps1 (never called InitializeToolset themselves, depended on MSBuild to do it)
  • Any repo whose secondary build scripts enter via msbuild.{sh,ps1} rather than build.{sh,ps1}. dotnet/runtime's src/tests/build.sh is one example.

Repos that go through eng/common/build.{sh,ps1} are unaffected because those scripts call InitializeToolset directly.

Suggested fix

Lightweight: ensure NUGET_PACKAGES is exported whenever ci=true in tools.{sh,ps1}. Cleanest spot is the CI block at the bottom of tools.sh/tools.ps1 (next to the MSBUILDDEBUGPATH setup), so every entry point inherits it:

# eng/common/tools.sh — bottom, alongside MSBUILDDEBUGPATH setup
if [[ "$ci" == true ]]; then
  GetNuGetPackageCachePath
fi
# eng/common/tools.ps1 — equivalent
if ($ci) {
  Get-NuGetPackageCachePath | Out-Null
}

GetNuGetPackageCachePath is idempotent (no-op when NUGET_PACKAGES already set), so this is safe for callers that already invoke InitializeToolset later.

Alternative: restore the InitializeToolset call inside the MSBuild function (smaller diff, but heavier than necessary).

Workaround (already applied in dotnet/runtime)

Export NUGET_PACKAGES="$RepoRoot/.packages/" in src/tests/build.{sh,cmd} before invoking eng/common/msbuild.{sh,ps1}.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions