Skip to content

Comments

JOB-148890 Fix dependency duplication#30

Merged
timoteialbu merged 1 commit intomasterfrom
fix/workspace-filter-object-aliasing-bug
Feb 20, 2026
Merged

JOB-148890 Fix dependency duplication#30
timoteialbu merged 1 commit intomasterfrom
fix/workspace-filter-object-aliasing-bug

Conversation

@timoteialbu
Copy link
Contributor

@timoteialbu timoteialbu commented Feb 20, 2026

Summary

Three bugs fixed:

  • Workspace filter was a no-op due to object aliasing: parse_libyear() returns the same hash object it receives as all_libraries, so parsed_results and all_libraries are the same reference. When add_dependabot_findings injects cross-workspace packages into parsed_results, they simultaneously appear in all_libraries, making the filter compare the hash against itself. Fix: snapshot known package names into an immutable Set before Dependabot mutates the hash.

  • Dependency graph empty for devDependency-only workspaces: pnpm list --json puts devDependencies under a separate "devDependencies" key, not under "dependencies". Workspace packages that only have devDependencies (like library packages with peerDependencies + devDependencies, e.g. packages/visualizations) produced an empty dependency graph, so transitive ownership propagation never ran and all transitive deps were uploaded as owner: unknown. Fix: process both "dependencies" and "devDependencies" from the JSON output.

  • Transitive dependency inflation: add_all_libraries used --depth=Infinity, pulling the entire transitive dependency tree into parsed_results as top-level library records. This caused hundreds of transitive deps to be uploaded as individual libraries (e.g. 588 for packages/visualizations which only has 23 direct deps). Transitive deps should only exist in the dependency graph for ownership propagation, not as standalone library records. Fix: use --depth=0 to only collect direct dependencies.

Changes

File Change
lib/library_version_analysis/pnpm.rb Snapshot parsed_results.keys.to_set before Dependabot; update filter_to_workspace_packages to check against the Set; process package["devDependencies"] in add_dependency_graph; change add_all_libraries from --depth=Infinity to --depth=0
spec/pnpm_spec.rb Update 5 existing filter tests to pass a Set; add 1 regression test for aliasing bug; add 2 tests for devDependencies graph building

Test plan

  • All 109 tests pass
  • Regression test covers the exact production aliasing scenario
  • New tests cover devDependencies-only and mixed dependency graph building
  • Deploy and verify packages/visualizations uploads ~23 libraries (direct deps) instead of ~588
  • Verify transitive ownership propagation works for devDependency-only workspace packages
  • Verify no duplicate Slack notifications from cross-workspace Dependabot alerts

@timoteialbu timoteialbu changed the title Fix workspace filter being a no-op due to object aliasing JOB-148890 Fix workspace filter being a no-op due to object aliasing Feb 20, 2026
@timoteialbu timoteialbu requested a review from Copilot February 20, 2026 15:43
@timoteialbu timoteialbu force-pushed the fix/workspace-filter-object-aliasing-bug branch from 8fee61a to 717dd0b Compare February 20, 2026 15:46
Copy link

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 fixes a critical bug where the workspace package filtering introduced in PR #29 was ineffective due to Ruby object aliasing. The parse_libyear() method returns the same hash object it receives as all_libraries, making it identical to parsed_results. When add_dependabot_findings() injects cross-workspace packages into parsed_results, they simultaneously appear in the comparison hash, causing the filter to compare against itself and become a no-op.

Changes:

  • Snapshot package names into an immutable Set before Dependabot mutations to break the object aliasing chain
  • Update the filter method to compare against the pre-mutation Set instead of the aliased hash
  • Add regression test that reproduces the exact production scenario with shared object mutation

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
lib/library_version_analysis/pnpm.rb Captures parsed_results.keys.to_set snapshot after parse_libyear but before add_dependabot_findings in both workspace analysis methods; updates filter signature to accept Set instead of hash
spec/pnpm_spec.rb Updates 5 existing filter tests to use Set literals; adds regression test simulating production aliasing scenario with shared hash mutation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


puts("\tPNPM [#{source}] parsing libyear") if LibraryVersionAnalysis.dev_output?
parsed_results, meta_data = parse_libyear(libyear_results, all_libraries)
# Snapshot before Dependabot: parse_libyear returns all_libraries as parsed_results (same object),
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

Missing require 'set' statement at the top of this file. In Ruby 3.0+, the Set class is not automatically loaded and must be explicitly required. Since this codebase uses Ruby 3.3.6 (as specified in .tool-versions) and the code now uses .to_set, you need to add require 'set' at the top of the file (around line 1-3) alongside the other require statements.

Copilot uses AI. Check for mistakes.
"react" => LibraryVersionAnalysis::Versionline.new(owner: ":unknown", current_version: "18.2.0"),
"lodash" => LibraryVersionAnalysis::Versionline.new(owner: ":unknown", current_version: "4.17.21")
}
workspace_package_names = Set["react", "lodash"]
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

Missing require 'set' statement. In Ruby 3.0+, the Set class is not automatically loaded and must be explicitly required. Since this codebase uses Ruby 3.3.6 and the tests now use Set["react", "lodash"], you need to add require 'set' at the top of this spec file.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why do I need this if the tests pass? Presumably they would fail if this was really needed.

Copy link
Contributor

Choose a reason for hiding this comment

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

I was thinking the same thing

@timoteialbu timoteialbu force-pushed the fix/workspace-filter-object-aliasing-bug branch from 717dd0b to 3ad46f9 Compare February 20, 2026 17:34
…tive dep inflation

Three bugs fixed:

1. parse_libyear() returns the same hash object it receives as
all_libraries, so parsed_results and all_libraries are the same
reference. When add_dependabot_findings injects cross-workspace
packages into parsed_results, they simultaneously appear in
all_libraries. The filter then compares the hash against itself
and finds nothing to remove. Fix by snapshotting known package
names into an immutable Set before Dependabot can mutate the hash.

2. pnpm list --json puts devDependencies under a separate
"devDependencies" key, not under "dependencies". Workspace packages
that only have devDependencies (like library packages with
peerDependencies + devDependencies) produced an empty dependency
graph, so transitive ownership propagation never ran and all
transitive deps were uploaded as owner:unknown. Fix by processing
both "dependencies" and "devDependencies" from the JSON output.

3. add_all_libraries used --depth=Infinity, pulling the entire
transitive dependency tree into parsed_results as top-level library
records. This caused hundreds of transitive deps to be uploaded as
individual libraries (e.g. 588 for packages/visualizations which
only has 23 direct deps). These transitive deps should only exist
in the dependency graph for ownership propagation, not as standalone
library records. Fix by using --depth=0 to only collect direct
dependencies.

Co-authored-by: Cursor <cursoragent@cursor.com>
@timoteialbu timoteialbu force-pushed the fix/workspace-filter-object-aliasing-bug branch from 3ad46f9 to dd44e30 Compare February 20, 2026 18:29
@timoteialbu timoteialbu changed the title JOB-148890 Fix workspace filter being a no-op due to object aliasing JOB-148890 Fix dependency duplication Feb 20, 2026
@timoteialbu timoteialbu requested a review from naarok February 20, 2026 20:36
"react" => LibraryVersionAnalysis::Versionline.new(owner: ":unknown", current_version: "18.2.0"),
"lodash" => LibraryVersionAnalysis::Versionline.new(owner: ":unknown", current_version: "4.17.21")
}
workspace_package_names = Set["react", "lodash"]
Copy link
Contributor

Choose a reason for hiding this comment

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

I was thinking the same thing

@timoteialbu timoteialbu merged commit 4629268 into master Feb 20, 2026
1 check passed
@timoteialbu timoteialbu deleted the fix/workspace-filter-object-aliasing-bug branch February 20, 2026 21:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

2 participants