Skip to content

Release pack job can fail with NETSDK1064: it packs on a runner that never restored #388

Description

@turbobobbytraykov

Summary

The pack job in .github/workflows/igniteui-blazor-lite-release.yml (branch btraykov/release-workflow-refactoring) runs on its own runner and packs with --no-build --no-restore, so no dotnet restore ever runs there. Whether that succeeds depends on an MSBuild cache file that happens to travel inside the artifact, not on anything the workflow controls. When that cache is rejected, the job fails with NETSDK1064. Infragistics.QueryBuilder.Executor, whose release workflow was modelled on this one and has the same shape, hit exactly this in production on 2026-09-01.

Where it is in this repo

The pack job declares needs: sign-assemblies and its own runs-on: windows-latest, then checks out, sets up .NET, downloads the signed-assemblies artifact into src, and runs:

dotnet pack ./src/IgniteUI.Blazor.Lite.csproj
  --configuration ${{ env.BUILD_CONFIGURATION }}
  --no-build
  --no-restore
  -p:PackageVersion=${{ env.VERSION }}
  -p:RepositoryUrl=${{ env.REPOSITORY_URL }}
  -p:RepositoryType=git
  -p:RepositoryCommit=${{ github.sha }}
  -o "${{ github.workspace }}/artifacts"

The packages restored by the build job live in that runner's global NuGet cache, which is not part of the artifact and does not exist on the pack runner.

Why this usually works anyway

The artifact carries src/bin/** and src/obj/**, and src/obj/<Configuration>/<tfm>/<project>.assets.cache travels with it. When MSBuild's ResolvePackageAssets accepts that cache, it reuses the previously resolved package assets and never looks for the packages on disk, so the missing package cache goes unnoticed. When it rejects the cache, it re-resolves from src/obj/project.assets.json, probes the package folders recorded there (C:\Users\runneradmin\.nuget\packages\), finds nothing, and fails.

So --no-restore on a separate runner is not safe by design — it is safe only while that cache keeps being accepted.

Evidence from the QueryBuilder failure

Failing job: https://github.com/IgniteUI/Infragistics.QueryBuilder.Executor/actions/runs/33509115228/job/99862048354

C:\Program Files\dotnet\sdk\9.0.317\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets(266,5): 
error NETSDK1064: Package Microsoft.EntityFrameworkCore.Analyzers, version 9.0.4 was not found.
It might have been deleted since NuGet restore.
Otherwise, NuGet restore might have only partially completed, which might have been due to maximum path length restrictions.
[D:\a\Infragistics.QueryBuilder.Executor\Infragistics.QueryBuilder.Executor\Infragistics.QueryBuilder.Executor.csproj::TargetFramework=net8.0]

The preceding release (https://github.com/IgniteUI/Infragistics.QueryBuilder.Executor/actions/runs/33004665173, 1.0.2-prerelease.36) succeeded on the same workflow shape: same split jobs, same --no-build --no-restore, no restore in pack, identical bin/** + obj/** artifacts with include-hidden-files: true, and identical setup-dotnet steps.

The failure was reproduced from the failing run's own signed-assemblies artifact, replayed at the same workspace path:

Scenario Result
Artifact exactly as delivered pack succeeds
Same files, obj/project.assets.json made newer than obj/Release/<tfm>/*.assets.cache NETSDK1064, identical message
Restore first, then the identical pack command pack succeeds

Ruled out as the cause: the SDK version (9.0.315 and 10.x both fail identically once the cache is rejected, so this repo's global.json pin of 10.0.100 does not protect it), the -p:RepositoryUrl / -p:RepositoryCommit pack arguments — which this workflow also passes — the signing job refreshing DLL timestamps, and any difference in artifact contents between the passing and failing runs.

Why it has not manifested here yet

The split-job release workflow is not merged into master, and no release has been produced from btraykov/release-workflow-refactoring, so this pack job has never executed in a real release run. That is the only reason — not a structural difference from QueryBuilder.

The exposure is the same once it does run: the same split-job handoff, the same --no-restore, and net8.0;net9.0;net10.0 multi-targeting (QueryBuilder failed on its net8.0 inner build).

Why it appeared suddenly in QueryBuilder

Nothing in that release changed the pack path. The necessary and sufficient condition for the failure is that ResolvePackageAssets rejects the transferred cache, and acceptance is decided by a timestamp comparison between obj/project.assets.json and the per-framework assets.cache — both of which are written by actions/download-artifact when it materialises the artifact, not by the build that produced them. Earlier releases landed on the winning side of that comparison; this one did not.

The precise trigger for run 33509115228 is not recoverable from the logs. What is reproducible is that the error appears exactly when the cache is rejected and disappears when a restore runs first.

Suggested fix

Restore on the pack runner before packing. This is not a rebuild, so the Authenticode-signed assemblies downloaded from sign-assemblies are left untouched and every downstream signature and strong-name check still applies:

      - name: Restore .NET dependencies
        run: dotnet restore ./src/IgniteUI.Blazor.Lite.csproj

placed between Download signed assemblies and Pack NuGet package.

The equivalent alternative is to drop --no-restore from dotnet pack and let it restore implicitly. Either option removes the dependency on cache acceptance; keeping --no-restore without a restore step means release correctness rests on artifact extraction ordering.

One repo-specific note: the Razor SDK reads src/wwwroot when it builds the static web asset manifest, and the build job already depends on the web assets being in place before restore. A restore in the pack job runs after signed-assemblies (which includes src/wwwroot/**) has been downloaded, so the assets are present at that point.

Notes

Filed pre-emptively — there is no failure in this repository to point at yet. The same issue is being filed against IgniteUI/IgniteUI.Blazor.GridLite, and the fix has been applied in the QueryBuilder release-hardening PR where the failure actually occurred.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions