Skip to content

Invalidate per-project restore skip when paket.lock content changes - #4334

Merged
dsyme merged 3 commits into
fsprojects:masterfrom
AgricoZA:i4333-lock-content-invalidates-project-props
Aug 27, 2026
Merged

dsyme merged 3 commits into
fsprojects:masterfrom
AgricoZA:i4333-lock-content-invalidates-project-props

Conversation

@randrag

@randrag randrag commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

This is to fix #4333 — Step 2a's skip in Paket.Restore.targets previously only watched paket.references, so transitive changes in paket.lock left the per-project paket.props stale. The fix adds a paket.lock content-hash check (Step 2 a-bis) and a one-line write of that hash after each successful per-project restore (Step 3 a-bis), plus a fall-through that fires once on first encounter for legacy checkouts that pre-date the cache file.

Test in RestoreSpecs.fs follows the existing #2684 / #3527 patterns: full restore, mutate paket.lock content, assert that the next dotnet restore requests a per-project restore (via PAKET_ERROR_ON_MSBUILD_EXEC=true).

randrag added 2 commits April 26, 2026 20:17
…ket.props stale

Step 2a's per-project skip optimisation in Paket.Restore.targets only
invalidates on paket.references content change. When a merge bumps a
transitive package version, paket.lock and paket.resolved change but
paket.references does not, and (with BaseIntermediateOutputPath
redirected per-platform) the per-project paket.props stays at the
pre-merge content. NuGet then resolves to the previous version
indefinitely.

This test reproduces that path: full restore, mutate paket.lock content,
expect the next restore to invalidate the per-project skip. With the
unfixed targets file the skip stays in effect and the test fails.

Companion fix to follow.
Add Step 2 a-bis to Paket.Restore.targets that hashes paket.lock and
compares to a per-project hash cache file written after each successful
per-project restore. If the cached hash differs from the current hash,
the per-project skip is invalidated and a paket restore runs. A
fall-through case fires once on first encounter for legacy checkouts
that pre-date the cache file, so installations upgrading to this
version self-correct on the next restore.

This complements Step 2a (which hashes paket.references content) so
that pure-transitive package version changes — where paket.lock and
paket.resolved change but paket.references does not — also invalidate
the per-project skip.

The hash is computed locally rather than reusing $(PaketRestoreLockFileHash)
from Step 1 because Step 1 may be disabled via PaketDisableGlobalRestore.
A new file $(PaketIntermediateOutputPath)\<proj>.fsproj.paket.lock.hash
records the lock hash at the time of the last successful per-project
restore.

Greens the integration test added in the previous commit.
@nicholi

nicholi commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Would really like to see this reviewed and commented on.

I think it possibly resolves an issue I've had forever. My only solution was just manually purging bin/obj dirs between any paket.lock changes (or even more mundane things like switching branches with different paket.locks).

@dsyme dsyme closed this Aug 26, 2026
@dsyme dsyme reopened this Aug 26, 2026
@dsyme
dsyme requested a lite review from Copilot August 26, 2026 19:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Paket’s MSBuild “skip per-project restore” optimization so it also invalidates when paket.lock content changes (not just when paket.references changes). This prevents stale per-project paket.props / *.paket.resolved under redirected BaseIntermediateOutputPath, which can otherwise cause NuGet to keep resolving old transitive versions indefinitely.

Changes:

  • Extend Paket.Restore.targets Step 2 with a per-project cached SHA256 of paket.lock to force per-project restore when lock content changes (and a one-time fallback when the cache file is missing).
  • Stamp the per-project lock hash cache after successful per-project restore.
  • Add a new integration scenario plus an integration test asserting that a lock-content change triggers per-project restore (via PAKET_ERROR_ON_MSBUILD_EXEC=true).
Show a summary per file
File Description
src/Paket.Core/embedded/Paket.Restore.targets Adds per-project paket.lock hash invalidation and writes the hash cache after per-project restore.
integrationtests/Paket.IntegrationTests/RestoreSpecs.fs Adds a regression test for lock-content invalidation of the per-project restore skip.
integrationtests/scenarios/i4333-lock-content-invalidates-project-props/before/Directory.Build.props New scenario that redirects BaseIntermediateOutputPath and disables global restore (to reproduce the original bug).
integrationtests/scenarios/i4333-lock-content-invalidates-project-props/before/paket.dependencies New scenario dependencies file for the repro project.
integrationtests/scenarios/i4333-lock-content-invalidates-project-props/before/paket.lock New scenario lockfile used to establish and then mutate lock content.
integrationtests/scenarios/i4333-lock-content-invalidates-project-props/before/project/paket.references New scenario references file that remains unchanged while the lock is mutated.
integrationtests/scenarios/i4333-lock-content-invalidates-project-props/before/project/project.fsproj New SDK-style repro project importing Paket.Restore.targets.
integrationtests/scenarios/i4333-lock-content-invalidates-project-props/before/project/Program.fs Minimal program for the new repro project.

Review details

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 7/8 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread src/Paket.Core/embedded/Paket.Restore.targets
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@dsyme

dsyme commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

/repo-assist Check if the test added is actually running as part of the CI or is it in the integration tests we haven't fully enabled yet

@github-actions

github-actions Bot commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

✓ Repo Assist completed successfully, see workflow run.

Generated by 🌈 Repo Assist, see workflow run. Learn more.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 This is an automated response from Repo Assist.

Checked how the new test (#4333 paket.lock content change invalidates per-project restore skip in RestoreSpecs.fs) is wired up, and it is included in the required CI run, not just the integration-test suites — here's the trail:

  1. Included in the project: RestoreSpecs.fs is compiled into Paket.IntegrationTests.fsproj (<Compile Include="RestoreSpecs.fs" />), and the test has no [<Category("Flaky")>] or other category attribute, so it's a normal, always-run integration test.
  2. CI job that runs it: .github/workflows/ci.yml's linux job has a build matrix entry (skip_env: SkipIntegrationTests=true is only set for that entry) plus a flaky entry (PAKET_TESTSUITE_FLAKYTESTS=true) — there is no longer a dedicated net/netcore matrix entry (those were removed in commit 7088d11, "Exclude rotted integration suites from Actions CI"). That means on Linux, integration tests including this one only run via the fallback All → BuildPackage pipeline in build.fsx, which chains RunIntegrationTestsNet and RunIntegrationTestsNetCore unless SkipIntegrationTests* build params are set. Since the current Linux matrix has no entry that runs integration tests without skipping them, this test does not currently execute on the Linux CI job — only the windows job (.\build.cmd BuildPackage, no skip flags) runs the full RunIntegrationTestsNet/RunIntegrationTestsNetCore targets, which include this test (filtered only by TestCategory!=Flaky).
  3. So in practice: this test does run in CI, but only on the Windows job, not on Linux (where integration tests are currently skipped entirely per the build matrix entry). It is not "not fully enabled" — it will execute and gate windows CI — but Linux coverage for it is currently absent because of the CI trim done in 7088d11.

No code changes proposed here since this reflects an existing, intentional-looking trim of Linux integration-test coverage (not something introduced by this PR). Flagging for the maintainer's awareness in case Linux integration coverage should be restored.

Generated by 🌈 Repo Assist, see workflow run. Learn more.
Comment /repo-assist to run again

Add this agentic workflow to your repo

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

4 participants