Skip to content

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

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions