Skip to content

Per-project restore skip misses paket.lock content changes when paket.references is unchanged #4333

@randrag

Description

@randrag

Description

Paket.Restore.targets' Step 2a invalidates the per-project restore skip only when the content hash of paket.references changes. When paket.lock is updated by a transitive change — e.g. a merge that bumps a transitive package version — paket.references content is unchanged, Step 2a's skip kicks in, and the per-project obj/<rid>/<proj>.fsproj.paket.props stays at the pre-merge content. Subsequent dotnet restore reads it via $(BaseIntermediateOutputPath) and project.assets.json continues resolving the package to the previous version indefinitely. Neither dotnet paket restore, dotnet paket restore --force, nor dotnet restore --force-evaluate fixes the project's resolved view.

The bug shows up most visibly when BaseIntermediateOutputPath is redirected (e.g. obj/$(_BuildPlatform)/) because the platform-agnostic obj/<proj>.fsproj.paket.props does get rewritten by the CLI's full restore but the platform-specific copy at obj/<rid>/<proj>.fsproj.paket.props (which is what MSBuild actually reads) does not.

Repro steps

  1. Small SDK-style F# project that pulls in a single package transitively. Directory.Build.props redirects BaseIntermediateOutputPath to obj/custom/ (mirrors the existing i003527 scenario layout).
  2. dotnet restore project.fsproj — establishes steady state. Per-project files in obj/custom/ reflect the lock.
  3. Mutate paket.lock content (e.g. append a comment line to simulate a transitive bump). Do not touch paket.references.
  4. dotnet restore project.fsproj again, with PAKET_ERROR_ON_MSBUILD_EXEC=true to assert whether a per-project paket restore fires.

Expected behavior

The per-project skip should treat a paket.lock content change as an invalidation event and re-run the per-project restore, just as it does on a paket.references content change.

Actual behavior

PAKET_ERROR_ON_MSBUILD_EXEC=true does not fire — Step 2a sets PaketRestoreRequired=false because paket.references hash matches the cached hash, and Step 2b sees the resolved files exist. obj/<rid>/<proj>.fsproj.paket.props stays at the pre-mutation content; project.assets.json continues resolving the affected package to the old version.

Known workarounds

  • Delete obj/<rid>/ for the affected projects to force DoAllResolvedFilesExist=false (Step 2b then fires). Heavy hammer; loses build outputs.
  • dotnet paket install --force (re-resolves and rewrites all per-project state). Heavy hammer; touches the lock.
  • Set PAKET_SKIP_RESTORE_TARGETS=true and patch Paket.Restore.targets locally to add the missing check. Workable but requires every developer / CI / IDE to set the env var.

I have a candidate fix and an integration test (red/green) that follows the same shape as i002684-fast-restore and i003527; PR to follow this issue.

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