`outdated` support for workspaces (#4251)
diff --git a/lib/src/command/outdated.dart b/lib/src/command/outdated.dart
index 23cfeb7..7ce16fe 100644
--- a/lib/src/command/outdated.dart
+++ b/lib/src/command/outdated.dart
@@ -127,16 +127,24 @@
'The json report always includes transitive dependencies.');
}
- final rootPubspec = includeDependencyOverrides
- ? entrypoint.workspaceRoot.pubspec
- : stripDependencyOverrides(entrypoint.workspaceRoot.pubspec);
+ /// The workspace root with dependency overrides removed if requested.
+ final baseWorkspace = includeDependencyOverrides
+ ? entrypoint.workspaceRoot
+ : entrypoint.workspaceRoot.transformWorkspace(
+ (package) => stripDependencyOverrides(package.pubspec),
+ );
- final upgradablePubspec = includeDevDependencies
- ? rootPubspec
- : stripDevDependencies(rootPubspec);
+ /// [baseWorkspace] with dev-dependencies removed if requested.
+ final upgradableWorkspace = includeDevDependencies
+ ? baseWorkspace
+ : baseWorkspace.transformWorkspace(
+ (package) => stripDevDependencies(package.pubspec),
+ );
- final resolvablePubspec = await mode.resolvablePubspec(upgradablePubspec);
-
+ /// [upgradableWorkspace] with upper bounds removed.
+ final resolvableWorkspace = upgradableWorkspace.transformWorkspace(
+ (package) => mode.resolvablePubspec(package.pubspec),
+ );
late List<PackageId> upgradablePackages;
late List<PackageId> resolvablePackages;
late bool hasUpgradableResolution;
@@ -146,11 +154,7 @@
'Resolving',
() async {
final upgradablePackagesResult = await _tryResolve(
- Package(
- upgradablePubspec,
- entrypoint.workspaceRoot.dir,
- entrypoint.workspaceRoot.workspaceChildren,
- ),
+ upgradableWorkspace,
cache,
lockFile: entrypoint.lockFile,
);
@@ -158,11 +162,7 @@
upgradablePackages = upgradablePackagesResult ?? [];
final resolvablePackagesResult = await _tryResolve(
- Package(
- resolvablePubspec,
- entrypoint.workspaceRoot.dir,
- entrypoint.workspaceRoot.workspaceChildren,
- ),
+ resolvableWorkspace,
cache,
lockFile: entrypoint.lockFile,
);
@@ -206,8 +206,7 @@
var latestIsOverridden = false;
PackageId? latest;
// If not overridden in current resolution we can use this
- if (!entrypoint.workspaceRoot.pubspec.dependencyOverrides
- .containsKey(name)) {
+ if (!hasOverride(entrypoint.workspaceRoot, name)) {
latest ??= await cache.getLatest(
current?.toRef(),
version: current?.version,
@@ -216,23 +215,27 @@
}
// If present as a dependency or dev_dependency we use this
latest ??= await cache.getLatest(
- rootPubspec.dependencies[name]?.toRef(),
+ allDependencies(baseWorkspace)
+ .firstWhereOrNull((r) => r.name == name)
+ ?.toRef(),
allowPrereleases: prereleases,
);
latest ??= await cache.getLatest(
- rootPubspec.devDependencies[name]?.toRef(),
+ allDevDependencies(baseWorkspace)
+ .firstWhereOrNull((r) => r.name == name)
+ ?.toRef(),
allowPrereleases: prereleases,
);
// If not overridden and present in either upgradable or resolvable we
// use this reference to find the latest
- if (!upgradablePubspec.dependencyOverrides.containsKey(name)) {
+ if (!hasOverride(upgradableWorkspace, name)) {
latest ??= await cache.getLatest(
upgradable?.toRef(),
version: upgradable?.version,
allowPrereleases: prereleases,
);
}
- if (!resolvablePubspec.dependencyOverrides.containsKey(name)) {
+ if (!hasOverride(resolvableWorkspace, name)) {
latest ??= await cache.getLatest(
resolvable?.toRef(),
version: resolvable?.version,
@@ -278,12 +281,12 @@
final upgradableVersionDetails = await _describeVersion(
upgradable,
- upgradablePubspec.dependencyOverrides.containsKey(name),
+ hasOverride(upgradableWorkspace, name),
);
final resolvableVersionDetails = await _describeVersion(
resolvable,
- resolvablePubspec.dependencyOverrides.containsKey(name),
+ hasOverride(resolvableWorkspace, name),
);
final latestVersionDetails = await _describeVersion(
@@ -332,8 +335,9 @@
final rows = <_PackageDetails>[];
- final visited = <String>{
- entrypoint.workspaceRoot.name,
+ final visited = {
+ ...entrypoint.workspaceRoot.transitiveWorkspace
+ .map((package) => package.name),
};
// Add all dependencies from the lockfile.
for (final id in [
@@ -361,6 +365,7 @@
includeDevDependencies: includeDevDependencies,
);
} else {
+ bool isNotFromSdk(PackageRange range) => range.source is! SdkSource;
await _outputHuman(
rows,
mode,
@@ -368,13 +373,13 @@
showAll: showAll,
includeDevDependencies: includeDevDependencies,
lockFileExists: fileExists(entrypoint.lockFilePath),
- hasDirectDependencies: rootPubspec.dependencies.values.any(
+ hasDirectDependencies: allDependencies(baseWorkspace).any(
// Test if it contains non-SDK dependencies
- (c) => c.source is! SdkSource,
+ isNotFromSdk,
),
- hasDevDependencies: rootPubspec.devDependencies.values.any(
+ hasDevDependencies: allDevDependencies(baseWorkspace).any(
// Test if it contains non-SDK dependencies
- (c) => c.source is! SdkSource,
+ isNotFromSdk,
),
showTransitiveDependencies: showTransitiveDependencies,
hasUpgradableResolution: hasUpgradableResolution,
@@ -420,23 +425,27 @@
}
/// Computes the closure of the graph of dependencies (not including
- /// `dev_dependencies` from [root], given the package versions
- /// in [resolution].
+ /// `dev_dependencies`) from all workspace packages in [workspaceRoot], given
+ /// the package versions in [resolution].
///
/// The [resolution] is allowed to be a partial (or empty) resolution not
- /// satisfying all the dependencies of [root].
+ /// satisfying all the dependencies of [workspaceRoot].
Future<Set<String>> _nonDevDependencyClosure(
- Package root,
+ Package workspaceRoot,
Iterable<PackageId> resolution,
) async {
final nameToId = {for (final id in resolution) id.name: id};
- final nonDevDependencies = <String>{root.name};
- final queue = [...root.dependencies.keys];
+ final result = <String>{
+ for (final p in workspaceRoot.transitiveWorkspace) p.name,
+ };
+ final queue = [
+ for (final p in workspaceRoot.transitiveWorkspace) ...p.dependencies.keys,
+ ];
while (queue.isNotEmpty) {
final name = queue.removeLast();
- if (!nonDevDependencies.add(name)) {
+ if (!result.add(name)) {
continue;
}
@@ -448,7 +457,7 @@
queue.addAll(pubspec.dependencies.keys);
}
- return nonDevDependencies;
+ return result;
}
}
@@ -784,7 +793,7 @@
String get upgradeConstrained;
String get allSafe;
- Future<Pubspec> resolvablePubspec(Pubspec pubspec);
+ Pubspec resolvablePubspec(Pubspec pubspec);
}
class _OutdatedMode implements _Mode {
@@ -873,8 +882,8 @@
}
@override
- Future<Pubspec> resolvablePubspec(Pubspec? pubspec) async {
- return stripVersionBounds(pubspec!);
+ Pubspec resolvablePubspec(Pubspec pubspec) {
+ return stripVersionBounds(pubspec);
}
}
@@ -972,9 +981,9 @@
Entrypoint entrypoint,
Set<String> nonDevTransitive,
) {
- if (entrypoint.workspaceRoot.dependencies.containsKey(name)) {
+ if (hasDependency(entrypoint.workspaceRoot, name)) {
return _DependencyKind.direct;
- } else if (entrypoint.workspaceRoot.devDependencies.containsKey(name)) {
+ } else if (hasDevDependency(entrypoint.workspaceRoot, name)) {
return _DependencyKind.dev;
} else {
if (nonDevTransitive.contains(name)) {
@@ -1093,3 +1102,29 @@
static String _noFormat(String x) => x;
}
+
+/// Whether the package [name] is overridden anywhere in the workspace rooted at
+/// [workspaceRoot].
+bool hasOverride(Package workspaceRoot, String name) {
+ return workspaceRoot.allOverridesInWorkspace.containsKey(name);
+}
+
+/// Whether the package [name] is depended on directly anywhere in the workspace
+/// rooted at [workspaceRoot].
+bool hasDependency(Package workspaceRoot, String name) {
+ return workspaceRoot.transitiveWorkspace
+ .any((p) => p.dependencies.containsKey(name));
+}
+
+/// Whether the package [name] is dev-depended on directly anywhere in the workspace
+/// rooted at [workspaceRoot].
+bool hasDevDependency(Package workspaceRoot, String name) {
+ return workspaceRoot.transitiveWorkspace
+ .any((p) => p.devDependencies.containsKey(name));
+}
+
+Iterable<PackageRange> allDependencies(Package workspaceRoot) =>
+ workspaceRoot.transitiveWorkspace.expand((p) => p.dependencies.values);
+
+Iterable<PackageRange> allDevDependencies(Package workspaceRoot) =>
+ workspaceRoot.transitiveWorkspace.expand((p) => p.devDependencies.values);
diff --git a/test/outdated/outdated_test.dart b/test/outdated/outdated_test.dart
index 084c8dc..ff9ed28 100644
--- a/test/outdated/outdated_test.dart
+++ b/test/outdated/outdated_test.dart
@@ -778,6 +778,56 @@
await ctx.runOutdatedTests();
});
+ testWithGolden('reports dependencies from all of workspace', (ctx) async {
+ final server = await servePackages();
+ server.serve('myapp', '1.2.4');
+ server.serve('dep', '0.9.0', deps: {'myapp': '^1.2.3'});
+ server.serve('dep', '0.8.0', deps: {'myapp': '^1.2.3'});
+ server.serve('dep', '1.0.0');
+ server.serve('dep_a', '0.9.0');
+ server.serve('dep_a', '1.0.0');
+ server.serve('dev_dep_a', '0.9.0');
+ server.serve('dev_dep_a', '1.0.0');
+
+ await d.dir(appPath, [
+ d.libPubspec(
+ 'myapp',
+ '1.2.3',
+ deps: {'dep': '^0.9.0'},
+ extras: {
+ 'workspace': ['pkgs/a'],
+ },
+ sdk: '^3.5.0',
+ ),
+ d.dir('pkgs', [
+ d.dir('a', [
+ d.libPubspec(
+ 'a',
+ '1.1.1',
+ deps: {'myapp': '^1.0.0', 'dep_a': '^0.9.0'},
+ devDeps: {'dev_dep_a': '^0.9.0'},
+ extras: {
+ 'dependency_overrides': {'dep': '0.8.0'},
+ },
+ resolutionWorkspace: true,
+ ),
+ ]),
+ ]),
+ ]).create();
+
+ await pubGet(
+ environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
+ );
+
+ server.serve('dep', '0.9.5');
+ server.serve('dep_a', '0.9.5');
+ server.serve('dev_dep_a', '0.9.5');
+
+ await ctx.runOutdatedTests(
+ environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
+ );
+ });
+
testWithGolden('Handles SDK dependencies', (ctx) async {
await servePackages()
..serve(
diff --git a/test/testdata/goldens/outdated/outdated_test/reports dependencies from all of workspace.txt b/test/testdata/goldens/outdated/outdated_test/reports dependencies from all of workspace.txt
new file mode 100644
index 0000000..c4e53af
--- /dev/null
+++ b/test/testdata/goldens/outdated/outdated_test/reports dependencies from all of workspace.txt
@@ -0,0 +1,246 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
+$ pub outdated --json
+{
+ "packages": [
+ {
+ "package": "dep",
+ "kind": "direct",
+ "isDiscontinued": false,
+ "isCurrentRetracted": false,
+ "isCurrentAffectedByAdvisory": false,
+ "current": {
+ "version": "0.8.0"
+ },
+ "upgradable": {
+ "version": "0.8.0",
+ "overridden": true
+ },
+ "resolvable": {
+ "version": "0.8.0",
+ "overridden": true
+ },
+ "latest": {
+ "version": "1.0.0"
+ }
+ },
+ {
+ "package": "dep_a",
+ "kind": "direct",
+ "isDiscontinued": false,
+ "isCurrentRetracted": false,
+ "isCurrentAffectedByAdvisory": false,
+ "current": {
+ "version": "0.9.0"
+ },
+ "upgradable": {
+ "version": "0.9.5"
+ },
+ "resolvable": {
+ "version": "1.0.0"
+ },
+ "latest": {
+ "version": "1.0.0"
+ }
+ },
+ {
+ "package": "dev_dep_a",
+ "kind": "dev",
+ "isDiscontinued": false,
+ "isCurrentRetracted": false,
+ "isCurrentAffectedByAdvisory": false,
+ "current": {
+ "version": "0.9.0"
+ },
+ "upgradable": {
+ "version": "0.9.5"
+ },
+ "resolvable": {
+ "version": "1.0.0"
+ },
+ "latest": {
+ "version": "1.0.0"
+ }
+ }
+ ]
+}
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
+$ pub outdated --no-color
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Package Name Current Upgradable Resolvable Latest
+
+direct dependencies:
+dep *0.8.0 *0.8.0 (overridden) *0.8.0 (overridden) 1.0.0
+dep_a *0.9.0 *0.9.5 1.0.0 1.0.0
+
+dev_dependencies:
+dev_dep_a *0.9.0 *0.9.5 1.0.0 1.0.0
+
+2 upgradable dependencies are locked (in pubspec.lock) to older versions.
+To update these dependencies, use `dart pub upgrade`.
+
+2 dependencies are constrained to versions that are older than a resolvable version.
+To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
+$ pub outdated --no-color --no-transitive
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Package Name Current Upgradable Resolvable Latest
+
+direct dependencies:
+dep *0.8.0 *0.8.0 (overridden) *0.8.0 (overridden) 1.0.0
+dep_a *0.9.0 *0.9.5 1.0.0 1.0.0
+
+dev_dependencies:
+dev_dep_a *0.9.0 *0.9.5 1.0.0 1.0.0
+
+2 upgradable dependencies are locked (in pubspec.lock) to older versions.
+To update these dependencies, use `dart pub upgrade`.
+
+2 dependencies are constrained to versions that are older than a resolvable version.
+To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
+$ pub outdated --no-color --up-to-date
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Package Name Current Upgradable Resolvable Latest
+
+direct dependencies:
+dep *0.8.0 *0.8.0 (overridden) *0.8.0 (overridden) 1.0.0
+dep_a *0.9.0 *0.9.5 1.0.0 1.0.0
+
+dev_dependencies:
+dev_dep_a *0.9.0 *0.9.5 1.0.0 1.0.0
+
+2 upgradable dependencies are locked (in pubspec.lock) to older versions.
+To update these dependencies, use `dart pub upgrade`.
+
+2 dependencies are constrained to versions that are older than a resolvable version.
+To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
+$ pub outdated --no-color --prereleases
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Package Name Current Upgradable Resolvable Latest
+
+direct dependencies:
+dep *0.8.0 *0.8.0 (overridden) *0.8.0 (overridden) 1.0.0
+dep_a *0.9.0 *0.9.5 1.0.0 1.0.0
+
+dev_dependencies:
+dev_dep_a *0.9.0 *0.9.5 1.0.0 1.0.0
+
+2 upgradable dependencies are locked (in pubspec.lock) to older versions.
+To update these dependencies, use `dart pub upgrade`.
+
+2 dependencies are constrained to versions that are older than a resolvable version.
+To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
+$ pub outdated --no-color --no-dev-dependencies
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Package Name Current Upgradable Resolvable Latest
+
+direct dependencies:
+dep *0.8.0 *0.8.0 (overridden) *0.8.0 (overridden) 1.0.0
+dep_a *0.9.0 *0.9.5 1.0.0 1.0.0
+
+1 upgradable dependency is locked (in pubspec.lock) to an older version.
+To update it, use `dart pub upgrade`.
+
+1 dependency is constrained to a version that is older than a resolvable version.
+To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
+$ pub outdated --no-color --no-dependency-overrides
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Package Name Current Upgradable Resolvable Latest
+
+direct dependencies:
+dep *0.8.0 *0.9.5 1.0.0 1.0.0
+dep_a *0.9.0 *0.9.5 1.0.0 1.0.0
+
+dev_dependencies:
+dev_dep_a *0.9.0 *0.9.5 1.0.0 1.0.0
+
+3 upgradable dependencies are locked (in pubspec.lock) to older versions.
+To update these dependencies, use `dart pub upgrade`.
+
+3 dependencies are constrained to versions that are older than a resolvable version.
+To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
+$ pub outdated --json --no-dev-dependencies
+{
+ "packages": [
+ {
+ "package": "dep",
+ "kind": "direct",
+ "isDiscontinued": false,
+ "isCurrentRetracted": false,
+ "isCurrentAffectedByAdvisory": false,
+ "current": {
+ "version": "0.8.0"
+ },
+ "upgradable": {
+ "version": "0.8.0",
+ "overridden": true
+ },
+ "resolvable": {
+ "version": "0.8.0",
+ "overridden": true
+ },
+ "latest": {
+ "version": "1.0.0"
+ }
+ },
+ {
+ "package": "dep_a",
+ "kind": "direct",
+ "isDiscontinued": false,
+ "isCurrentRetracted": false,
+ "isCurrentAffectedByAdvisory": false,
+ "current": {
+ "version": "0.9.0"
+ },
+ "upgradable": {
+ "version": "0.9.5"
+ },
+ "resolvable": {
+ "version": "1.0.0"
+ },
+ "latest": {
+ "version": "1.0.0"
+ }
+ }
+ ]
+}
+