Support pnpm v11 patches format#1650
Conversation
Currently, we parse information about patched dependencies from the pnpm-lock.yaml file. In pnpm v11, the format of patched dependencies has changed, so the pnpm-lock.yaml contains only patch hash, not the actual path that we need for SBOM. Fortunately, this information is already present in pnpm-workspace.yaml, including for pnpm v10 so there is not much we need to change in our code. -- pnpm/pnpm#10911 Signed-off-by: Michal Šoltis <msoltis@redhat.com>
Signed-off-by: Michal Šoltis <msoltis@redhat.com>
There was a problem hiding this comment.
Code Review
This pull request refactors the PNPM resolver to load the pnpm-workspace.yaml file once and pass its data to downstream components, allowing patchedDependencies to be retrieved directly from the workspace configuration. Feedback on these changes highlights the need for defensive type checking when parsing the YAML file to prevent potential AttributeError or type errors if keys like packages or patchedDependencies are null or if the file is empty. Additionally, integration test parameters should be reverted to point to the official upstream repository and branch instead of a personal fork.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| try: | ||
| with pnpm_workspace_path.open() as f: | ||
| pnpm_workspace = yaml.safe_load(f) | ||
| return yaml.safe_load(f) |
There was a problem hiding this comment.
If pnpm-workspace.yaml is empty or contains only comments, yaml.safe_load(f) will return None. This will cause an AttributeError when workspace_data.get() is called later in the execution flow. We should ensure that _load_pnpm_workspace always returns a dictionary by checking the type of the loaded data.
| try: | |
| with pnpm_workspace_path.open() as f: | |
| pnpm_workspace = yaml.safe_load(f) | |
| return yaml.safe_load(f) | |
| try: | |
| with pnpm_workspace_path.open() as f: | |
| data = yaml.safe_load(f) | |
| return data if isinstance(data, dict) else {} |
| proxy_url = config.pnpm.proxy_url | ||
|
|
||
| non_dev_dependencies = _find_non_dev_dependencies(lockfile) | ||
| patched_dependencies = workspace_data.get("patchedDependencies", {}) |
There was a problem hiding this comment.
If patchedDependencies is defined as null or is of an invalid type in pnpm-workspace.yaml, workspace_data.get("patchedDependencies", {}) might return None or a non-dictionary type. This can lead to type errors or crashes in _generate_pedigree_for. We should defensively ensure that patched_dependencies is always a dictionary.
| patched_dependencies = workspace_data.get("patchedDependencies", {}) | |
| patched_dependencies = workspace_data.get("patchedDependencies") | |
| if not isinstance(patched_dependencies, dict): | |
| patched_dependencies = {} |
| components = [] | ||
|
|
||
| workspaces_globs = _read_workspace_globs(project_dir) | ||
| workspaces_globs = workspace_data.get("packages", []) |
There was a problem hiding this comment.
If packages is defined as null or is of an invalid type in pnpm-workspace.yaml, workspace_data.get("packages", []) might return None or a non-list type, which could cause a crash in get_workspace_paths. We should defensively ensure that workspaces_globs is always a list.
| workspaces_globs = workspace_data.get("packages", []) | |
| workspaces_globs = workspace_data.get("packages") | |
| if not isinstance(workspaces_globs, list): | |
| workspaces_globs = [] |
No description provided.