Report failure if package is included twice in same workspace (#4211)
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart index 861c9df..a9315fb 100644 --- a/lib/src/entrypoint.dart +++ b/lib/src/entrypoint.dart
@@ -123,6 +123,7 @@ ); for (final package in root.transitiveWorkspace) { if (identical(pubspecsMet.entries.first.value, package.pubspec)) { + validateWorkspaceGraph(root); return (root: root, work: package); } }
diff --git a/lib/src/package.dart b/lib/src/package.dart index ca6b3f6..e1c5208 100644 --- a/lib/src/package.dart +++ b/lib/src/package.dart
@@ -359,3 +359,27 @@ ).map(resolve).toList(); } } + +/// Reports an error if the graph of the workspace rooted at [root] is not a +/// tree. +void validateWorkspaceGraph(Package root) { + final includedFrom = <String, String>{}; + final stack = [root]; + + while (stack.isNotEmpty) { + final current = stack.removeLast(); + for (final child in current.workspaceChildren) { + final previous = includedFrom[p.canonicalize(child.dir)]; + if (previous != null) { + fail(''' +Packages can only be included in the workspace once. + +`${p.join(child.dir, 'pubspec.yaml')}` is included in the workspace, both from: +* `${p.join(current.dir, 'pubspec.yaml')}` and +* ${p.join(previous, 'pubspec.yaml')}.'''); + } + includedFrom[p.canonicalize(child.dir)] = current.dir; + } + stack.addAll(current.workspaceChildren); + } +}
diff --git a/test/workspace_test.dart b/test/workspace_test.dart index 967c737..f78c514 100644 --- a/test/workspace_test.dart +++ b/test/workspace_test.dart
@@ -719,6 +719,45 @@ expect(unmanagedPackageConfig.statSync().type, FileSystemEntityType.file); }); + test('Reports error if workspace doesn\'t form a tree.', () async { + await dir(appPath, [ + libPubspec( + 'myapp', + '1.2.3', + sdk: '^3.7.0', + extras: { + 'workspace': ['pkgs/a', 'pkgs'], + }, + ), + dir('pkgs', [ + libPubspec( + 'a', + '1.1.1', + resolutionWorkspace: true, + extras: { + 'workspace': ['a'], + }, + ), + dir( + 'a', + [ + libPubspec('a', '1.1.1', resolutionWorkspace: true), + ], + ), + ]), + ]).create(); + final s = p.separator; + await pubGet( + error: ''' +Packages can only be included in the workspace once. + +`.${s}pkgs${s}a${s}pubspec.yaml` is included in the workspace, both from: +* `.${s}pkgs${s}pubspec.yaml` and +* .${s}pubspec.yaml.''', + environment: {'_PUB_TEST_SDK_VERSION': '3.7.0'}, + ); + }); + test( 'Reports a failure if a workspace pubspec is not nested inside the parent dir', () async {