feat: add workspace support for packages check licenses#1540
feat: add workspace support for packages check licenses#1540realmeylisdev wants to merge 23 commits into
Conversation
When a pubspec.yaml declares a workspace property, the command now recursively collects dependencies from all workspace members and checks their licenses using the root pubspec.lock. This enables license checking in monorepo projects that use Dart's pub workspace feature. Closes VeryGoodOpenSource#1273
|
@realmeylisdev seems like there are some conflicts that needs to be resolved |
Keep both workspace license support functions and ReporterOutputFormat enum from main.
|
now done. |
|
@marcossevilla could we get this PR reviewed which was de-prioritized last time ? #1444 (comment) |
hi @samitsv, I'll take a look during this week, thanks for pushing this! also mentioning @brianegan as he was having this issue too, a review or test to check if it's fixing your issue is also appreciated 👍 |
|
any update on this @marcossevilla |
|
any update on this @marcossevilla . Would be great to have this merged soon. |
|
hey @samitsv, we're currently trying the branch against internal repos to verify the changes, I'll leave an update once we finish testing |
|
@marcossevilla Functional testing done on my end! PR worked well for a workspace monorepo. Sorry for the delay here -- deadlines. |
Address review feedback on PR VeryGoodOpenSource#1540: - Move `pubspecBasename` constant from licenses.dart to pubspec library - Replace private `_tryParsePubspec` helper with `Pubspec.tryParse` static - Simplify `Pubspec.fromString` by replacing the throwing `as` cast with an `is!` check, removing the `TypeError` catch and lint suppression - Pass class types directly to `group()` in pubspec_test.dart
|
@realmeylisdev CI is failing, also found we could just use https://pub.dev/packages/pubspec_parse instead of creating a pubspec library |
CI Failure AnalysisThe Root CauseThe docs_site e2e test generates a Docusaurus project and runs This is an upstream Docusaurus/webpack incompatibility in the PR Code Verification
|
|
hey @realmeylisdev, we need to use |
|
hey @realmeylisdev, are you able to work on the changes? otherwise we could close the PR and the team will tackle this issue separately |
Replaces the hand-rolled Pubspec parser with `package:pubspec_parse`, which is already a direct dependency. Keeps only the workspace helpers (`tryParsePubspec`, `PubspecWorkspace` extension, `resolveWorkspaceMembers`) that pubspec_parse does not provide. Addresses review feedback on VeryGoodOpenSource#1540.
done. |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
|
@vgvbot perform a code review focused on verifying nits and code readability |
There was a problem hiding this comment.
This PR adds workspace support to packages check licenses, which is a useful feature with reasonable test coverage. Overall the approach (detect a workspace root, walk member pubspecs, build a Set of declared direct deps to filter pubspec.lock by name) is workable and backwards-compatible. A few concerns worth addressing:
Concerns
_collectWorkspaceDependenciesrecurses into nested workspaces with no cycle protection. A pubspec that lists its own directory (or a parent) inworkspace:would cause unbounded recursion.Pubspec.parse(..., lenient: true)accepts many malformed pubspecs without throwing. The malformed-pubspec test only passes because the YAML itself is unparsable; a structurally invalid but YAML-valid pubspec would silently parse with empty/default dep maps and contribute nothing — i.e. workspace members would be silently dropped.PubspecWorkspace.isWorkspaceMemberis dead production code (only referenced by tests). Either drop it or use it to validate that resolved members declareresolution: workspace.- The new workspace-aware filter and the helper that drives it (
_collectWorkspaceDependencies) live inlicenses.darteven though the logic is generic workspace-walking; moving it intolib/src/pubspec/pubspec.dart(or a sibling) would keep the command file focused and let other commands reuse it. - The branch around
dependencyTypes.contains('direct-main') || dependencyTypes.contains('direct-dev')is redundant onceworkspaceDependencieswas already filtered by those types — the outer if can be removed for clarity, or the membership Sets should be tracked per-type to actually enforce the distinction. - Test gaps: no coverage for (a) a dep declared as
direct-mainin one member anddirect-devin another (relevant since the merged Set loses that distinction), (b)_isGlobPattern's?/[branches, (c) cycle protection, and (d) a workspace member that also declaresresolution: workspacemismatch. _isGlobPatternis a hand-rolled heuristic; pkg:glob can be queried more directly, and patterns like!packages/foo(negation) won't be classified as glob.
Positives
- Clean separation between
pubspec.darthelpers and the command layer. - Backwards-compatibility is preserved: when no
pubspec.yamlis present or it isn't a workspace root, the original filtering path is used unchanged. - Good integration test coverage for the common workspace shapes (direct-main/dev, transitive, overridden, nested, and non-workspace).
- Move resolveWorkspaceMembers and dependency collection into the PubspecWorkspace extension on Pubspec, keeping pubspec helpers in the pubspec library. - Add cycle protection to collectWorkspaceDependencies via a visited set of canonical paths to prevent unbounded recursion when a member references a parent. - Drop the redundant outer dependency-type guard in licenses.dart now that workspaceDependencies is already filtered at collection time. - Use targetDirectory.path consistently when joining the pubspec and pubspec.lock paths. - Cover the merged-membership case (dep listed as direct-main in one member and direct-dev in another) and circular workspace references with new tests; rename groups to track the renamed APIs.
|
hey @realmeylisdev, will you be able to apply the comments on this PR? thanks! |
- Drop `lenient: true` from `tryParsePubspec` so structurally invalid
pubspecs (missing `name`, malformed dep maps) fall through to the
exception handler instead of silently parsing with empty dep maps.
- Remove the unused `PubspecWorkspace.isWorkspaceMember` getter and
`_workspaceResolution` constant; only tests referenced it.
- Drop the hand-rolled `_isGlobPattern` heuristic and route every
workspace pattern through `Glob.listSync`. Literal paths match as
exact entries, so the previous direct-path branch (which skipped
the `pubspec.yaml` check) becomes unnecessary.
- Cover the new behavior: structurally invalid pubspecs, `?`/`[...]`/
`{...}` glob shapes, literal paths with non-metachar symbols, and
workspace members that omit `resolution: workspace`.
|
@marcossevilla applied the remaining bot review nits in d5668ab:
Ready for another look. |
|
@vgvbot check the changes from your last review, resolve the comments that have been addressed and leave any comments if necessary |
There was a problem hiding this comment.
Overall
This PR adds workspace support to packages check licenses, which fills a real gap for monorepo users. The approach — walk member pubspecs to build a union of direct deps, then intersect with the workspace's pubspec.lock — is reasonable. Test coverage is broad, covering globs (*, ?, [...], {...}), nested workspaces, malformed members, and backwards compatibility.
Main concerns
- Duplicated filter logic in
licenses.dart. The new workspace branch (lines 212–227) re-implements thetransitive/direct-overriddenchecks that already exist in the non-workspace branch (lines 234–238). The two paths now have to stay in sync. - Dual signal/data return in
collectWorkspaceDependencies.nullmeans "not a workspace," a non-nullSetmeans "workspace deps." SinceisWorkspaceRootalready exists as a separate getter, this overloading is unnecessary and makes the call site read awkwardly (workspaceDependencies != nullis really asking "are we in workspace mode?"). - Redundant traversal on nested workspaces. When a member is itself a workspace root, its
dependencies/devDependenciesare added by the outer loop and by the recursive call's prologue.Setdedupes, but the data flow is harder to follow than it should be. dependency_overridesis silently ignored when collecting workspace deps. Members that override deps will rely on the lock file'sdirect-overriddenmarker — which is fine in practice but worth either documenting in the dartdoc or handling explicitly.- Unnecessary churn at line 183: changing
targetPathtotargetDirectory.pathdoesn't change behavior (they're the same string) and bloats the diff.
Smaller notes
- Several test fixtures collide on the pubspec
namefield (e.g._workspaceMemberSharedPubspecContentis used forappDir, where it producesname: shared), which makes the test scenarios harder to parse. tryParsePubspeccatchesExceptiononly;Pubspec.parsepaths go throughParsedYamlException/YamlExceptionwhich subclassFormatException, so this is correct — but the doc comment could state that explicitly.- Cross-platform:
Glob(pattern).listSync(root: root.path)will use the platformpath.Contextby default. Workspace globs in real pubspecs always use/, which works, but worth a quick sanity check on Windows CI before shipping.
|
hey @realmeylisdev! let us know if you will be able to apply the changes, otherwise we can prioritize this change for the next CLI release (not #1605, but the following one) |
Move tryParse into PubspecWorkspace, return a non-null Set from collectWorkspaceDependencies, unify the licenses filter, de-duplicate nested-workspace traversal, and document the dependency_overrides and silent-skip behavior. Tightens workspace license test fixtures.
|
Thanks @marcossevilla — I've applied the changes (pushed in d2b106d). Summary of this round:
Tests
100% coverage is maintained, and |
|
Heads up on CI: the failing
Re-merging |
|
Thanks @marcossevilla and @unicoderbot for the thorough review. The feedback has been addressed in the latest revision — a summary of each point and how it was resolved: Architecture / API
Correctness / robustness
Tests
One intentional divergence — Windows globsThe suggestion to pin CI is green and the full suite passes locally (analyzer clean with |
|
Rebased onto the latest @marcossevilla, when you have a moment, could you take another look? Happy to address anything further. Thanks! |
Summary
This PR adds workspace support to the
packages check licensescommand, enabling license checking in monorepo projects that use Dart's pub workspace feature.Reopens #1444 (branch was restored per request).
Closes #1273
Changes
New Files
lib/src/pubspec/pubspec.dart- Pubspec parser for detecting workspace configurationstest/src/pubspec/pubspec_test.dart- 19 tests for the Pubspec parserModified Files
lib/src/commands/packages/commands/check/commands/licenses.dart- Added workspace detection and dependency collectiontest/src/commands/packages/commands/check/commands/licenses_test.dart- Added 3 workspace support testsHow It Works
pubspec.yamlcontains aworkspaceproperty, it's detected as a workspace rootpubspec.yamlfilespackages/*are supported (Dart 3.11+)Test plan