Skip to content

Commit 0264dd0

Browse files
authored
Fix dependency_overrides validator in workspaces (#4564)
1 parent 21fc678 commit 0264dd0

File tree

6 files changed

+132
-76
lines changed

6 files changed

+132
-76
lines changed

lib/src/package_graph.dart

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,14 @@ class PackageGraph {
3737
SolveResult result,
3838
) {
3939
final packages = {
40-
for (final id in result.packages)
41-
id.name:
42-
id.isRoot
43-
? entrypoint.workspaceRoot
44-
: Package(
45-
result.pubspecs[id.name]!,
46-
entrypoint.cache.getDirectory(id),
47-
[],
48-
),
40+
for (final package in entrypoint.workspaceRoot.transitiveWorkspace)
41+
package.name: package,
42+
for (final id in result.packages.where((p) => !p.isRoot))
43+
id.name: Package(
44+
result.pubspecs[id.name]!,
45+
entrypoint.cache.getDirectory(id),
46+
[],
47+
),
4948
};
5049

5150
return PackageGraph(entrypoint, packages);

lib/src/validator/dependency_override.dart

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,33 @@
44

55
import 'dart:async';
66

7-
import 'package:collection/collection.dart';
8-
97
import '../validator.dart';
108

11-
/// A validator that validates a package's dependencies overrides (or the
12-
/// absence thereof).
9+
/// Complains (with a hint) if any of the transitive dependencies of a package's
10+
/// non-dev dependencies are overridden anywhere in the workspace.
1311
class DependencyOverrideValidator extends Validator {
1412
@override
1513
Future<void> validate() async {
16-
final overridden = MapKeySet(
17-
context.entrypoint.workspaceRoot.allOverridesInWorkspace,
18-
);
19-
final dev = MapKeySet(package.devDependencies);
20-
if (overridden.difference(dev).isNotEmpty) {
21-
final overridesFile =
22-
package.pubspec.dependencyOverridesFromOverridesFile
23-
? package.pubspecOverridesPath
24-
: package.pubspecPath;
14+
final graph = await context.entrypoint.packageGraph;
15+
final transitiveNonDevDependencies = <String>{};
16+
final toVisit = [package.name];
17+
while (toVisit.isNotEmpty) {
18+
final next = toVisit.removeLast();
19+
if (transitiveNonDevDependencies.add(next)) {
20+
toVisit.addAll(graph.packages[next]!.dependencies.keys);
21+
}
22+
}
2523

26-
hints.add('''
24+
for (final workspacePackage
25+
in context.entrypoint.workspaceRoot.transitiveWorkspace) {
26+
for (final override
27+
in workspacePackage.pubspec.dependencyOverrides.keys) {
28+
if (transitiveNonDevDependencies.contains(override)) {
29+
final overridesFile =
30+
workspacePackage.pubspec.dependencyOverridesFromOverridesFile
31+
? workspacePackage.pubspecOverridesPath
32+
: workspacePackage.pubspecPath;
33+
hints.add('''
2734
Non-dev dependencies are overridden in $overridesFile.
2835
2936
This indicates you are not testing your package against the same versions of its
@@ -32,6 +39,8 @@ dependencies that users will have when they use it.
3239
This might be necessary for packages with cyclic dependencies.
3340
3441
Please be extra careful when publishing.''');
42+
}
43+
}
3544
}
3645
}
3746
}

pubspec.lock

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,18 @@ packages:
55
dependency: transitive
66
description:
77
name: _fe_analyzer_shared
8-
sha256: "88399e291da5f7e889359681a8f64b18c5123e03576b01f32a6a276611e511c3"
8+
sha256: dc27559385e905ad30838356c5f5d574014ba39872d732111cd07ac0beff4c57
99
url: "https://pub.dev"
1010
source: hosted
11-
version: "78.0.0"
12-
_macros:
13-
dependency: transitive
14-
description: dart
15-
source: sdk
16-
version: "0.3.3"
11+
version: "80.0.0"
1712
analyzer:
1813
dependency: "direct main"
1914
description:
2015
name: analyzer
21-
sha256: "62899ef43d0b962b056ed2ebac6b47ec76ffd003d5f7c4e4dc870afe63188e33"
16+
sha256: "192d1c5b944e7e53b24b5586db760db934b177d4147c42fbca8c8c5f1eb8d11e"
2217
url: "https://pub.dev"
2318
source: hosted
24-
version: "7.1.0"
19+
version: "7.3.0"
2520
args:
2621
dependency: "direct main"
2722
description:
@@ -190,14 +185,6 @@ packages:
190185
url: "https://pub.dev"
191186
source: hosted
192187
version: "1.3.0"
193-
macros:
194-
dependency: transitive
195-
description:
196-
name: macros
197-
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
198-
url: "https://pub.dev"
199-
source: hosted
200-
version: "0.1.3-main.0"
201188
matcher:
202189
dependency: transitive
203190
description:

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ environment:
44
sdk: ^3.7.0
55

66
dependencies:
7-
analyzer: 7.1.0
7+
analyzer: 7.3.0
88
args: ^2.7.0
99
async: ^2.11.0
1010
cli_util: ^0.4.1

test/validator/dependency_override_test.dart

Lines changed: 94 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:path/path.dart' as p;
56
import 'package:test/test.dart';
67

78
import '../descriptor.dart' as d;
89
import '../test_pub.dart';
910
import 'utils.dart';
1011

1112
void main() {
12-
test('should consider a package valid if it has dev dependency '
13+
test('should consider a package valid if it overrides dev dependency '
1314
'overrides', () async {
1415
final server = await servePackages();
1516
server.serve('foo', '3.0.0');
@@ -27,8 +28,28 @@ void main() {
2728
await expectValidation();
2829
});
2930

30-
group('should consider a package invalid if', () {
31-
test('it has only non-dev dependency overrides', () async {
31+
test('should consider a package valid '
32+
'if it has any dependency overrides on non-dependency', () async {
33+
final server = await servePackages();
34+
server.serve('foo', '3.0.0');
35+
server.serve('bar', '3.0.0');
36+
37+
await d.validPackage().create();
38+
await d.dir(appPath, [
39+
d.validPubspec(
40+
extras: {
41+
'dev_dependencies': {'foo': '^1.0.0'},
42+
'dependency_overrides': {'foo': '^3.0.0', 'bar': '^3.0.0'},
43+
},
44+
),
45+
]).create();
46+
47+
await expectValidation();
48+
});
49+
50+
test(
51+
'should consider a package invalid if it has override of direct dependency',
52+
() async {
3253
final server = await servePackages();
3354
server.serve('foo', '3.0.0');
3455
await d.validPackage().create();
@@ -45,46 +66,84 @@ void main() {
4566
await expectValidationHint(
4667
'Non-dev dependencies are overridden in pubspec.yaml.',
4768
);
48-
});
49-
test('it has a pubspec_overrides.yaml', () async {
50-
final server = await servePackages();
51-
server.serve('foo', '3.0.0');
52-
await d.validPackage().create();
69+
},
70+
);
5371

54-
await d.dir(appPath, [
55-
d.validPubspec(
56-
extras: {
57-
'dependencies': {'foo': '^1.0.0'},
58-
},
59-
),
60-
d.pubspecOverrides({
61-
'dependency_overrides': {'foo': '3.0.0'},
62-
}),
63-
]).create();
72+
test('should consider a package invalid if it '
73+
'has override of transitive dependency', () async {
74+
final server = await servePackages();
75+
server.serve('foo', '1.0.0', deps: {'bar': '^3.0.0'});
76+
server.serve('bar', '3.0.0');
6477

65-
await expectValidationHint(
66-
'Non-dev dependencies are overridden in pubspec_overrides.yaml.',
67-
);
68-
});
78+
await d.validPackage().create();
6979

70-
test('it has any non-dev dependency overrides', () async {
71-
final server = await servePackages();
72-
server.serve('foo', '3.0.0');
73-
server.serve('bar', '3.0.0');
80+
await d.dir(appPath, [
81+
d.validPubspec(
82+
extras: {
83+
'dependencies': {'foo': '^1.0.0'},
84+
'dependency_overrides': {'bar': '^3.0.0'},
85+
},
86+
),
87+
]).create();
7488

75-
await d.validPackage().create();
76-
await d.dir(appPath, [
89+
await expectValidationHint(
90+
'Non-dev dependencies are overridden in pubspec.yaml.',
91+
);
92+
});
93+
94+
test('reports correctly about a pubspec_overrides.yaml', () async {
95+
final server = await servePackages();
96+
server.serve('foo', '3.0.0');
97+
await d.validPackage().create();
98+
99+
await d.dir(appPath, [
100+
d.validPubspec(
101+
extras: {
102+
'dependencies': {'foo': '^1.0.0'},
103+
},
104+
),
105+
d.pubspecOverrides({
106+
'dependency_overrides': {'foo': '3.0.0'},
107+
}),
108+
]).create();
109+
110+
await expectValidationHint(
111+
'Non-dev dependencies are overridden in pubspec_overrides.yaml.',
112+
);
113+
});
114+
115+
test('Detects overrides from outside work-package', () async {
116+
final server = await servePackages();
117+
server.serve('foo', '3.0.0');
118+
await d.validPackage().create();
119+
120+
await d.dir(appPath, [
121+
d.libPubspec(
122+
'workspace',
123+
'1.2.3',
124+
extras: {
125+
'workspace': ['a'],
126+
'dependency_overrides': {'foo': '^3.0.0'},
127+
},
128+
sdk: '^3.5.0',
129+
),
130+
d.dir('a', [
131+
...d.validPackage().contents,
77132
d.validPubspec(
78133
extras: {
79-
'dev_dependencies': {'foo': '^1.0.0'},
80-
'dependency_overrides': {'foo': '^3.0.0', 'bar': '^3.0.0'},
134+
'environment': {'sdk': '^3.5.0'},
135+
'resolution': 'workspace',
136+
'dependencies': {'foo': '^1.0.0'},
81137
},
82138
),
83-
]).create();
139+
]),
140+
]).create();
84141

85-
await expectValidationHint(
86-
'Non-dev dependencies are overridden in pubspec.yaml.',
87-
);
88-
});
142+
final s = p.separator;
143+
await expectValidationHint(
144+
'Non-dev dependencies are overridden in ..${s}pubspec.yaml.',
145+
workingDirectory: p.join(d.sandbox, appPath, 'a'),
146+
environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
147+
);
89148
});
90149
}

test/validator/utils.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,13 @@ Future<void> expectValidationHint(
5858
String hint, {
5959
int count = 1,
6060
Map<String, String> environment = const {},
61+
String? workingDirectory,
6162
}) async {
6263
final s = count == 1 ? '' : 's';
6364
await expectValidation(
6465
message: allOf([contains(hint), contains('and $count hint$s')]),
6566
environment: environment,
67+
workingDirectory: workingDirectory,
6668
);
6769
}
6870

0 commit comments

Comments
 (0)