Skip to content

Fix npm workspace package-lock.json entries incorrectly updated when requirement unchanged#14480

Closed
Copilot wants to merge 12 commits intomainfrom
copilot/fix-package-lock-issue
Closed

Fix npm workspace package-lock.json entries incorrectly updated when requirement unchanged#14480
Copilot wants to merge 12 commits intomainfrom
copilot/fix-package-lock-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 18, 2026

What are you trying to accomplish?

When updating a workspace dependency (e.g., cli/package.json) with a versioning strategy like increase-if-necessary, Dependabot runs npm install dep@newversion --workspace=cli --package-lock-only. npm v11 writes the new version range into packages["cli"].dependencies.dep in the lockfile even though cli/package.json itself wasn't changed. This results in a PR that modifies the lockfile's workspace entry without a corresponding package.json change. See npm/documentation#1914.

Anything you want to highlight for special attention from reviewers?

The existing restore_locked_package_dependencies already handles this for packages[""] (root) — it restores requirement strings that npm overwrites with the exact installed version. This PR extends the same logic to workspace package entries (packages["<workspace>"]).

The gsub-based string replacement is inherited from the existing root-package approach. It carries the same theoretical fragility (could affect multiple lockfile locations if the same dep+version string appears elsewhere), but in practice this is safe since workspace entries use dependency keys ("dep": "version") that don't appear in node_modules/* entries.

New helpers:

  • workspace_package_files — non-root package.json files associated with the current lockfile
  • workspace_lockfile_key — maps a workspace file to its packages["<key>"] entry using relative path from lockfile directory

Before fix — after running npm install dep@0.0.2 --workspace=app, lockfile contains:

"packages/app": {
  "dependencies": { "dep": "^0.0.2" }  // npm bumped range; package.json unchanged
}

After fix — restored to mirror packages/app/package.json:

"packages/app": {
  "dependencies": { "dep": "^0.0.1" }  // matches package.json requirement
}

How will you know you've accomplished your goal?

Extended the existing workspace_outdated_deps_not_in_root_package_json test to assert that packages["bump-version-for-cron"]["devDependencies"]["@swc/core"] remains "^1.3.37" after updating @swc/core from 1.3.401.3.44. Manually confirmed with npm v11.9.0 that without the fix npm changes "^0.0.1" to "^0.0.2" in the workspace lockfile entry.

Checklist

  • I have run the complete test suite to ensure all tests and linters pass.
  • I have thoroughly tested my code changes to ensure they work as expected, including adding additional tests for new functionality.
  • I have written clear and descriptive commit messages.
  • I have provided a detailed description of the changes in the pull request, including the problem it addresses, how it fixes the problem, and any relevant details about the implementation.
  • I have ensured that the code is well-documented and easy to understand.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.launchpad.net
    • Triggering command: /usr/bin/add-apt-repository add-apt-repository -y ppa:git-core/ppa grep rity.crt -q la/emSign_Root_CA_-_G1.crt e u3_amd64.deb (dns block)
  • gitlab.com
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node /home/REDACTED/work/_temp/ghcca-node/node/bin/corepack npm install babel-preset-php@gitlab:kornelski/babel-preset-php#5fbc24ccc37bd72052ce71ceae5b4934feb3ac19 --force --ignore-scripts --package-lock-only om/Graffino/Browcorepack m/_cacache/tmp/gnpm /usr/bin/basenaminstall git conf�� --global credential.helpe--package-lock-only ub.com/.insteadOf .com/ crt dabot-core/depennpm git (dns block)
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node node /home/REDACTED/work/_temp/ghcca-node/node/bin/corepack npm install babel-preset-php@gitlab:kornelski/babel-preset-php#5fbc24ccc37bd72052ce71ceae5b4934feb3ac19 --force --ignore-scripts --package-lock-only k/dependabot-corgit /usr/share/ca-ceconfig e --file /home/r--global sh -c e/common sed k/_temp/ghcca-node/node/bin/corepack elper &#39;!/home/rugit /usr/share/ca-cerev-parse dabot-core/depen--show-prefix git (dns block)
  • ports.ubuntu.com
    • Triggering command: /usr/lib/apt/methods/http /usr/lib/apt/methods/http (dns block)

If you need me to access, download, or install something from one of these locations, you can either:


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI and others added 2 commits March 18, 2026 09:52
…irement unchanged

When Dependabot updates a dependency in a workspace package (e.g., cli/package.json),
it runs `npm install dep@newversion --workspace=cli --package-lock-only`. This causes
npm to update `packages["cli"].dependencies.dep` in the lockfile to the exact installed
version, even when the workspace's package.json requirement hasn't changed (e.g.,
versioning strategy is increase-if-necessary).

Extend `restore_locked_package_dependencies` to also restore workspace package entries
(packages["<workspace>"]) so they mirror the workspace package.json requirement rather
than reflecting the exact npm-installed version.

Add helper methods `workspace_package_files` and `workspace_lockfile_key`, and add a
test assertion for the behavior.

Co-authored-by: thavaahariharangit <164553783+thavaahariharangit@users.noreply.github.com>
…ace content

Co-authored-by: thavaahariharangit <164553783+thavaahariharangit@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix Dependabot PR that alters package lock incorrectly Fix npm workspace package-lock.json entries incorrectly updated when requirement unchanged Mar 18, 2026
@thavaahariharangit thavaahariharangit force-pushed the copilot/fix-package-lock-issue branch from ba0aaa9 to f8cc697 Compare March 18, 2026 14:31
@thavaahariharangit thavaahariharangit marked this pull request as ready for review March 18, 2026 15:34
@thavaahariharangit thavaahariharangit requested a review from a team as a code owner March 18, 2026 15:34
Copilot AI review requested due to automatic review settings March 18, 2026 15:34
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 prevents npm workspace package-lock.json metadata from being rewritten to a new range/version when the workspace package.json requirement was intentionally left unchanged (e.g., increase-if-necessary updates), aligning workspace lockfile entries back to the manifest requirements.

Changes:

  • Extend lockfile “requirement restoration” logic from the root packages[""] entry to workspace packages["<workspace>"] entries.
  • Add targeted string-replacement helpers to scope replacements to a specific packages section (instead of global gsub).
  • Expand NpmLockfileUpdater specs and add new fixture projects covering multiple workspace/range scenarios.

Reviewed changes

Copilot reviewed 12 out of 16 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater.rb Adds workspace-aware restoration of packages["<workspace>"].(dev)Dependencies requirements and introduces helper methods to scope lockfile substitutions.
npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater_spec.rb Extends coverage to ensure workspace requirement ranges are preserved (and not restored when requirements were intentionally updated).
npm_and_yarn/spec/fixtures/projects/npm8/workspace_outdated_deps_requirement_changed/package.json New workspace fixture root manifest for “requirement changed” scenario.
npm_and_yarn/spec/fixtures/projects/npm8/workspace_outdated_deps_requirement_changed/package-lock.json New fixture lockfile simulating npm overwriting workspace metadata.
npm_and_yarn/spec/fixtures/projects/npm8/workspace_outdated_deps_requirement_changed/app/package.json New fixture workspace manifest for “requirement changed” scenario.
npm_and_yarn/spec/fixtures/projects/npm8/workspace_multiple_packages_same_dep/package.json New fixture root manifest for multi-workspace shared dependency scenario.
npm_and_yarn/spec/fixtures/projects/npm8/workspace_multiple_packages_same_dep/package-lock.json New fixture lockfile for shared dependency across workspaces.
npm_and_yarn/spec/fixtures/projects/npm8/workspace_multiple_packages_same_dep/app-a/package.json New fixture workspace manifest (app-a).
npm_and_yarn/spec/fixtures/projects/npm8/workspace_multiple_packages_same_dep/app-b/package.json New fixture workspace manifest (app-b).
npm_and_yarn/spec/fixtures/projects/npm8/workspace_multiple_packages_different_ranges/package.json New fixture root manifest for different ranges per workspace scenario.
npm_and_yarn/spec/fixtures/projects/npm8/workspace_multiple_packages_different_ranges/package-lock.json New fixture lockfile for different ranges per workspace.
npm_and_yarn/spec/fixtures/projects/npm8/workspace_multiple_packages_different_ranges/app-a/package.json New fixture workspace manifest (app-a).
npm_and_yarn/spec/fixtures/projects/npm8/workspace_multiple_packages_different_ranges/app-b/package.json New fixture workspace manifest (app-b).
npm_and_yarn/spec/fixtures/projects/npm8/workspace_dep_in_root_and_workspace/package.json New fixture root manifest where dep exists in root + workspace with different ranges.
npm_and_yarn/spec/fixtures/projects/npm8/workspace_dep_in_root_and_workspace/package-lock.json New fixture lockfile for dep existing in both root + workspace.
npm_and_yarn/spec/fixtures/projects/npm8/workspace_dep_in_root_and_workspace/app/package.json New fixture workspace manifest for root+workspace dep scenario.
Files not reviewed (4)
  • npm_and_yarn/spec/fixtures/projects/npm8/workspace_dep_in_root_and_workspace/package-lock.json: Language not supported
  • npm_and_yarn/spec/fixtures/projects/npm8/workspace_multiple_packages_different_ranges/package-lock.json: Language not supported
  • npm_and_yarn/spec/fixtures/projects/npm8/workspace_multiple_packages_same_dep/package-lock.json: Language not supported
  • npm_and_yarn/spec/fixtures/projects/npm8/workspace_outdated_deps_requirement_changed/package-lock.json: Language not supported

@thavaahariharangit
Copy link
Copy Markdown
Contributor

thavaahariharangit commented Mar 20, 2026

@robaiken As discussed during standup, I’ve recreated the issue here.

PR: https://github.com/thavaahariharangit/errorneous-lock-update-recreation/pull/2/changes
Workflow run: https://github.com/thavaahariharangit/errorneous-lock-update-recreation/actions/runs/23341454944/job/67895829530

Running the Dependabot CLI before and after this fix, I got below output (comparison given below), This confirms this fix resolves the issue: #14460

image image

@thavaahariharangit
Copy link
Copy Markdown
Contributor

covered here: #14515

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

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants