Handle whole workspace in dependency_services (#4294)
diff --git a/lib/src/command/dependency_services.dart b/lib/src/command/dependency_services.dart
index baffc2b..5f3eb72 100644
--- a/lib/src/command/dependency_services.dart
+++ b/lib/src/command/dependency_services.dart
@@ -25,6 +25,7 @@
import '../package_name.dart';
import '../pubspec.dart';
import '../pubspec_utils.dart';
+import '../sdk.dart';
import '../solver.dart';
import '../solver/version_solver.dart';
import '../source/git.dart';
@@ -56,6 +57,7 @@
@override
Future<void> runProtected() async {
+ _checkAtRoot(entrypoint);
final stdinString = await utf8.decodeStream(stdin);
final input = json.decode(stdinString.isEmpty ? '{}' : stdinString)
as Map<String, Object?>;
@@ -65,27 +67,21 @@
throw FormatException('"target" should be a String.');
}
- final compatiblePubspec =
- stripDependencyOverrides(entrypoint.workspaceRoot.pubspec);
+ final compatibleWorkspace = entrypoint.workspaceRoot
+ .transformWorkspace((p) => stripDependencyOverrides(p.pubspec));
- final breakingPubspec = stripVersionBounds(compatiblePubspec);
+ final breakingWorkspace = compatibleWorkspace.transformWorkspace(
+ (p) => stripVersionBounds(p.pubspec),
+ );
final compatiblePackagesResult = await _tryResolve(
- Package(
- compatiblePubspec,
- entrypoint.workspaceRoot.dir,
- entrypoint.workspaceRoot.workspaceChildren,
- ),
+ compatibleWorkspace,
cache,
additionalConstraints: additionalConstraints,
);
final breakingPackagesResult = await _tryResolve(
- Package(
- breakingPubspec,
- entrypoint.workspaceRoot.dir,
- entrypoint.workspaceRoot.workspaceChildren,
- ),
+ breakingWorkspace,
cache,
additionalConstraints: additionalConstraints,
);
@@ -105,22 +101,19 @@
?.firstWhereOrNull((element) => element.name == package.name);
final multiBreakingVersion = breakingPackagesResult
?.firstWhereOrNull((element) => element.name == package.name);
- final singleBreakingPubspec = compatiblePubspec.copyWith();
- final dependencySet =
- _dependencySetOfPackage(singleBreakingPubspec, package);
- final kind = _kindString(compatiblePubspec, package.name);
+ final kind = _kindString(compatibleWorkspace, package.name);
PackageId? singleBreakingVersion;
- if (dependencySet != null) {
- dependencySet[package.name] = package
- .toRef()
- .withConstraint(stripUpperBound(package.toRange().constraint));
+
+ if (kind != 'transitive') {
+ final singleBreakingWorkspace = compatibleWorkspace.transformWorkspace(
+ (p) {
+ final r = stripVersionBounds(p.pubspec, stripOnly: [package.name]);
+ return r;
+ },
+ );
final singleBreakingPackagesResult = await _tryResolve(
- Package(
- singleBreakingPubspec,
- entrypoint.workspaceRoot.dir,
- entrypoint.workspaceRoot.workspaceChildren,
- ),
+ singleBreakingWorkspace,
cache,
);
singleBreakingVersion = singleBreakingPackagesResult
@@ -131,17 +124,15 @@
(c) => c.range.toRef() == package.toRef() && !c.range.allows(package),
)) {
// Current version disallowed by restrictions.
- final atLeastCurrentPubspec = atLeastCurrent(
- compatiblePubspec,
- entrypoint.lockFile.packages.values.toList(),
+ final atLeastCurrentWorkspace = compatibleWorkspace.transformWorkspace(
+ (p) => atLeastCurrent(
+ p.pubspec,
+ entrypoint.lockFile.packages.values.toList(),
+ ),
);
final smallestUpgradeResult = await _tryResolve(
- Package(
- atLeastCurrentPubspec,
- entrypoint.workspaceRoot.dir,
- entrypoint.workspaceRoot.workspaceChildren,
- ),
+ atLeastCurrentWorkspace,
cache,
solveType: SolveType.downgrade,
additionalConstraints: additionalConstraints,
@@ -156,7 +147,7 @@
_UpgradeType upgradeType,
) async {
return await _computeUpgradeSet(
- compatiblePubspec,
+ compatibleWorkspace,
package,
entrypoint,
cache,
@@ -174,8 +165,8 @@
'latest':
(await cache.getLatest(package.toRef(), version: package.version))
?.versionOrHash(),
- 'constraint':
- _constraintOf(compatiblePubspec, package.name)?.toString(),
+ 'constraint': _constraintIntersection(compatibleWorkspace, package.name)
+ ?.toString(),
'compatible': await computeUpgradeSet(
compatibleVersion,
_UpgradeType.compatible,
@@ -225,16 +216,11 @@
@override
Future<void> runProtected() async {
- final pubspec = entrypoint.workspaceRoot.pubspec;
-
+ _checkAtRoot(entrypoint);
final currentPackages = fileExists(entrypoint.lockFilePath)
? entrypoint.lockFile.packages.values.toList()
: (await _tryResolve(
- Package(
- pubspec,
- entrypoint.workspaceRoot.dir,
- entrypoint.workspaceRoot.workspaceChildren,
- ),
+ entrypoint.workspaceRoot,
cache,
) ??
<PackageId>[]);
@@ -246,8 +232,10 @@
dependencies.add({
'name': package.name,
'version': package.versionOrHash(),
- 'kind': _kindString(pubspec, package.name),
- 'constraint': _constraintOf(pubspec, package.name)?.toString(),
+ 'kind': _kindString(entrypoint.workspaceRoot, package.name),
+ 'constraint':
+ _constraintIntersection(entrypoint.workspaceRoot, package.name)
+ ?.toString(),
'source': _source(package, containingDir: directory),
});
}
@@ -303,7 +291,6 @@
@override
Future<void> runProtected() async {
- YamlEditor(readTextFile(entrypoint.workspaceRoot.pubspecPath));
final toApply = <_PackageVersion>[];
final input = json.decode(await utf8.decodeStream(stdin));
for (final change in input['dependencyChanges'] as Iterable) {
@@ -317,10 +304,46 @@
),
);
}
-
- final pubspec = entrypoint.workspaceRoot.pubspec;
- final pubspecEditor =
- YamlEditor(readTextFile(entrypoint.workspaceRoot.pubspecPath));
+ final updatedPubspecs = <String, YamlEditor>{};
+ _checkAtRoot(entrypoint);
+ for (final package in entrypoint.workspaceRoot.transitiveWorkspace) {
+ final pubspec = package.pubspec;
+ final pubspecEditor = YamlEditor(readTextFile(package.pubspecPath));
+ for (final p in toApply) {
+ final targetConstraint = p.constraint;
+ final targetPackage = p.name;
+ final targetVersion = p.version;
+ late final section = pubspec.dependencies[targetPackage] != null
+ ? 'dependencies'
+ : 'dev_dependencies';
+ if (targetConstraint != null) {
+ final packageConfig =
+ pubspecEditor.parseAt([section, targetPackage]).value;
+ if (packageConfig == null || packageConfig is String) {
+ pubspecEditor
+ .update([section, targetPackage], targetConstraint.toString());
+ } else if (packageConfig is Map) {
+ pubspecEditor.update(
+ [section, targetPackage, 'version'],
+ targetConstraint.toString(),
+ );
+ } else {
+ fail(
+ 'The dependency $targetPackage does not have a map or string as a description',
+ );
+ }
+ } else if (targetVersion != null) {
+ final constraint = _constraintOf(pubspec, targetPackage);
+ if (constraint != null && !constraint.allows(targetVersion)) {
+ pubspecEditor.update(
+ [section, targetPackage],
+ VersionConstraint.compatibleWith(targetVersion).toString(),
+ );
+ }
+ }
+ updatedPubspecs[package.dir] = pubspecEditor;
+ }
+ }
final lockFile = fileExists(entrypoint.lockFilePath)
? readTextFile(entrypoint.lockFilePath)
: null;
@@ -331,40 +354,8 @@
for (final p in toApply) {
final targetPackage = p.name;
final targetVersion = p.version;
- final targetConstraint = p.constraint;
final targetRevision = p.gitRevision;
- if (targetConstraint != null) {
- final section = pubspec.dependencies[targetPackage] != null
- ? 'dependencies'
- : 'dev_dependencies';
- final packageConfig =
- pubspecEditor.parseAt([section, targetPackage]).value;
- if (packageConfig == null || packageConfig is String) {
- pubspecEditor
- .update([section, targetPackage], targetConstraint.toString());
- } else if (packageConfig is Map) {
- pubspecEditor.update(
- [section, targetPackage, 'version'],
- targetConstraint.toString(),
- );
- } else {
- fail(
- 'The dependency $targetPackage does not have a map or string as a description',
- );
- }
- } else if (targetVersion != null) {
- final constraint = _constraintOf(pubspec, targetPackage);
- if (constraint != null && !constraint.allows(targetVersion)) {
- final section = pubspec.dependencies[targetPackage] != null
- ? 'dependencies'
- : 'dev_dependencies';
- pubspecEditor.update(
- [section, targetPackage],
- VersionConstraint.compatibleWith(targetVersion).toString(),
- );
- }
- }
if (lockFileEditor != null) {
if (targetVersion != null &&
(lockFileYaml['packages'] as Map).containsKey(targetPackage)) {
@@ -431,7 +422,14 @@
);
await log.errorsOnlyUnlessTerminal(
() async {
- final updatedPubspec = pubspecEditor.toString();
+ final updatedWorkspace = entrypoint.workspaceRoot.transformWorkspace(
+ (package) => Pubspec.parse(
+ updatedPubspecs[package.dir].toString(),
+ cache.sources,
+ location: toUri(package.pubspecPath),
+ containingDescription: RootDescription(package.dir),
+ ),
+ );
// Resolve versions, this will update transitive dependencies that were
// not passed in the input. And also counts as a validation of the input
// by ensuring the resolution is valid.
@@ -442,21 +440,17 @@
final solveResult = await resolveVersions(
SolveType.get,
cache,
- Package(
- Pubspec.parse(
- updatedPubspec,
- cache.sources,
- location: toUri(entrypoint.workspaceRoot.pubspecPath),
- containingDescription:
- RootDescription(entrypoint.workspaceRoot.dir),
- ),
- entrypoint.workspaceRoot.dir,
- entrypoint.workspaceRoot.workspaceChildren,
- ),
+ updatedWorkspace,
lockFile: updatedLockfile,
);
- if (pubspecEditor.edits.isNotEmpty) {
- writeTextFile(entrypoint.workspaceRoot.pubspecPath, updatedPubspec);
+ for (final package in entrypoint.workspaceRoot.transitiveWorkspace) {
+ final updatedPubspec = updatedPubspecs[package.dir]!;
+ if (updatedPubspec.edits.isNotEmpty) {
+ writeTextFile(
+ package.pubspecPath,
+ updatedPubspec.toString(),
+ );
+ }
}
// Only if we originally had a lock-file we write the resulting lockfile back.
if (updatedLockfile != null) {
@@ -521,9 +515,9 @@
final newLockFile = LockFile(
updatedPackages,
sdkConstraints: updatedLockfile.sdkConstraints,
- mainDependencies: pubspec.dependencies.keys.toSet(),
- devDependencies: pubspec.devDependencies.keys.toSet(),
- overriddenDependencies: pubspec.dependencyOverrides.keys.toSet(),
+ mainDependencies: entrypoint.lockFile.mainDependencies,
+ devDependencies: entrypoint.lockFile.devDependencies,
+ overriddenDependencies: entrypoint.lockFile.overriddenDependencies,
);
newLockFile.writeToFile(entrypoint.lockFilePath, cache);
@@ -535,6 +529,12 @@
}
}
+void _checkAtRoot(Entrypoint entrypoint) {
+ if (entrypoint.workspaceRoot != entrypoint.workPackage) {
+ fail('Only apply dependency_services to the root of the workspace.');
+ }
+}
+
class _PackageVersion {
String name;
Version? version;
@@ -681,16 +681,33 @@
return solveResult?.packages;
}
+VersionConstraint? _constraintIntersection(
+ Package workspace,
+ String packageName,
+) {
+ final constraints = workspace.transitiveWorkspace
+ .map((p) => _constraintOf(p.pubspec, packageName))
+ .whereNotNull();
+ if (constraints.isEmpty) {
+ return null;
+ }
+ return constraints
+ .reduce((a, b) => a.intersect(b))
+ .asCompatibleWithIfPossible();
+}
+
VersionConstraint? _constraintOf(Pubspec pubspec, String packageName) {
return (pubspec.dependencies[packageName] ??
pubspec.devDependencies[packageName])
?.constraint;
}
-String _kindString(Pubspec pubspec, String packageName) {
- return pubspec.dependencies.containsKey(packageName)
+String _kindString(Package workspace, String packageName) {
+ return workspace.transitiveWorkspace
+ .any((p) => p.dependencies.containsKey(packageName))
? 'direct'
- : pubspec.devDependencies.containsKey(packageName)
+ : workspace.transitiveWorkspace
+ .any((p) => p.devDependencies.containsKey(packageName))
? 'dev'
: 'transitive';
}
@@ -721,12 +738,14 @@
key: (e) => (e as PackageId).name,
);
}
- currentPackages.remove(entrypoint.workspaceRoot.name);
+ for (final p in entrypoint.workspaceRoot.transitiveWorkspace) {
+ currentPackages.remove(p.name);
+ }
return currentPackages;
}
Future<List<Object>> _computeUpgradeSet(
- Pubspec rootPubspec,
+ Package workspace,
PackageId? package,
Entrypoint entrypoint,
SystemCache cache, {
@@ -736,16 +755,18 @@
}) async {
if (package == null) return [];
final lockFile = entrypoint.lockFile;
- final pubspec = (upgradeType == _UpgradeType.multiBreaking ||
+ final upgradedWorkspace = (upgradeType == _UpgradeType.multiBreaking ||
upgradeType == _UpgradeType.smallestUpdate)
- ? stripVersionBounds(rootPubspec)
- : rootPubspec.copyWith();
+ ? workspace.transformWorkspace((p) => stripVersionBounds(p.pubspec))
+ : workspace.transformWorkspace((p) => p.pubspec.copyWith());
- final dependencySet = _dependencySetOfPackage(pubspec, package);
- if (dependencySet != null) {
- // Force the version to be the new version.
- dependencySet[package.name] =
- package.toRef().withConstraint(package.toRange().constraint);
+ for (final p in upgradedWorkspace.transitiveWorkspace) {
+ final dependencySet = _dependencySetOfPackage(p.pubspec, package);
+ if (dependencySet != null) {
+ // Force the version to be the new version.
+ dependencySet[package.name] =
+ package.toRef().withConstraint(package.toRange().constraint);
+ }
}
final resolution = await tryResolveVersions(
@@ -753,11 +774,7 @@
? SolveType.downgrade
: SolveType.get,
cache,
- Package(
- pubspec,
- entrypoint.workspaceRoot.dir,
- entrypoint.workspaceRoot.workspaceChildren,
- ),
+ upgradedWorkspace,
lockFile: lockFile,
additionalConstraints: additionalConstraints,
);
@@ -766,40 +783,43 @@
if (resolution == null) {
return [];
}
-
+ final workspaceNames = {
+ ...workspace.transitiveWorkspace.map((p) => p.name),
+ };
return [
...resolution.packages.where((r) {
- if (r.name == rootPubspec.name) return false;
+ if (workspaceNames.contains(r.name)) return false;
final originalVersion = currentPackages[r.name];
return originalVersion == null || r != originalVersion;
}).map((p) {
- final depset = _dependencySetOfPackage(rootPubspec, p);
- final originalConstraint = depset?[p.name]?.constraint;
+ final constraintIntersection = _constraintIntersection(workspace, p.name);
final currentPackage = currentPackages[p.name];
return {
'name': p.name,
'version': p.versionOrHash(),
- 'kind': _kindString(pubspec, p.name),
+ 'kind': _kindString(workspace, p.name),
'source': _source(p, containingDir: entrypoint.workspaceRoot.dir),
- 'constraintBumped': originalConstraint == null
+ 'constraintBumped': constraintIntersection == null
? null
: upgradeType == _UpgradeType.compatible
- ? originalConstraint.toString()
- : _bumpConstraint(originalConstraint, p.version).toString(),
- 'constraintWidened': originalConstraint == null
+ ? constraintIntersection.toString()
+ : _bumpConstraint(constraintIntersection, p.version).toString(),
+ 'constraintWidened': constraintIntersection == null
? null
: upgradeType == _UpgradeType.compatible
- ? originalConstraint.toString()
- : _widenConstraint(originalConstraint, p.version).toString(),
- 'constraintBumpedIfNeeded': originalConstraint == null
+ ? constraintIntersection.toString()
+ : _widenConstraint(constraintIntersection, p.version)
+ .toString(),
+ 'constraintBumpedIfNeeded': constraintIntersection == null
? null
: upgradeType == _UpgradeType.compatible
- ? originalConstraint.toString()
- : originalConstraint.allows(p.version)
- ? originalConstraint.toString()
- : _bumpConstraint(originalConstraint, p.version).toString(),
+ ? constraintIntersection.toString()
+ : constraintIntersection.allows(p.version)
+ ? constraintIntersection.toString()
+ : _bumpConstraint(constraintIntersection, p.version)
+ .toString(),
'previousVersion': currentPackage?.versionOrHash(),
- 'previousConstraint': originalConstraint?.toString(),
+ 'previousConstraint': constraintIntersection?.toString(),
'previousSource': currentPackage == null
? null
: _source(
diff --git a/test/dependency_services/dependency_services_test.dart b/test/dependency_services/dependency_services_test.dart
index 11ccb68..ab3516c 100644
--- a/test/dependency_services/dependency_services_test.dart
+++ b/test/dependency_services/dependency_services_test.dart
@@ -18,24 +18,25 @@
import '../golden_file.dart';
import '../test_pub.dart';
-void manifestAndLockfile(GoldenTestContext context) {
+void manifestAndLockfile(GoldenTestContext context, List<String> workspace) {
String catFile(String filename) {
final path = p.join(d.sandbox, appPath, filename);
+ final normalizedFilename = p.posix.joinAll(p.split(p.normalize(filename)));
if (File(path).existsSync()) {
final contents = File(path).readAsLinesSync().map(filterUnstableText);
return '''
-\$ cat $filename
+\$ cat $normalizedFilename
${contents.join('\n')}''';
} else {
return '''
-\$ cat $filename
-No such file $filename.''';
+\$ cat $normalizedFilename
+No such file $normalizedFilename.''';
}
}
context.expectNextSection('''
-${catFile('pubspec.yaml')}
+${workspace.map((path) => catFile(p.join(path, 'pubspec.yaml'))).join('\n')}
${catFile('pubspec.lock')}
''');
}
@@ -47,6 +48,7 @@
Future<String> runDependencyServices(
List<String> args, {
String stdin = '',
+ Map<String, String>? environment,
}) async {
final buffer = StringBuffer();
buffer.writeln('## Section ${args.join(' ')}');
@@ -61,6 +63,7 @@
environment: {
...getPubTestEnvironment(),
'_PUB_TEST_DEFAULT_HOSTED_URL': globalServer.url,
+ ...?environment,
},
workingDirectory: p.join(d.sandbox, appPath),
);
@@ -96,11 +99,14 @@
Future<void> _listReportApply(
GoldenTestContext context,
List<_PackageVersion> upgrades, {
+ List<String> workspace = const ['.'],
void Function(Map)? reportAssertions,
+ Map<String, String>? environment,
}) async {
- manifestAndLockfile(context);
- await context.runDependencyServices(['list']);
- final report = await context.runDependencyServices(['report']);
+ manifestAndLockfile(context, workspace);
+ await context.runDependencyServices(['list'], environment: environment);
+ final report =
+ await context.runDependencyServices(['report'], environment: environment);
if (reportAssertions != null) {
reportAssertions(json.decode(report) as Map);
}
@@ -108,8 +114,12 @@
'dependencyChanges': upgrades,
});
- await context.runDependencyServices(['apply'], stdin: input);
- manifestAndLockfile(context);
+ await context.runDependencyServices(
+ ['apply'],
+ stdin: input,
+ environment: environment,
+ );
+ manifestAndLockfile(context, workspace);
}
Future<void> _reportWithForbidden(
@@ -118,7 +128,7 @@
void Function(Map)? resultAssertions,
String? targetPackage,
}) async {
- manifestAndLockfile(context);
+ manifestAndLockfile(context, ['.']);
final input = json.encode({
'target': targetPackage,
'disallowed': [
@@ -136,7 +146,7 @@
}
// await context.runDependencyServices(['apply'], stdin: input);
- manifestAndLockfile(context);
+ manifestAndLockfile(context, ['.']);
}
Future<void> main() async {
@@ -628,6 +638,83 @@
},
);
});
+
+ testWithGolden('can upgrade workspaces', (context) async {
+ (await servePackages())
+ ..serve('foo', '1.2.3')
+ ..serve('foo', '2.2.3', deps: {'transitive': '^1.0.0'})
+ ..serve('bar', '1.2.3')
+ ..serve('bar', '2.2.3')
+ ..serve('dev', '1.0.0')
+ ..serve('dev', '2.0.0')
+ ..serve('transitive', '1.0.0')
+ ..serveContentHashes = true;
+
+ await dir(appPath, [
+ libPubspec(
+ 'myapp',
+ '1.2.3',
+ extras: {
+ 'workspace': ['pkgs/a'],
+ },
+ deps: {
+ 'foo': '^1.0.0',
+ 'bar': '^1.0.0',
+ },
+ sdk: '^3.5.0',
+ ),
+ dir('pkgs', [
+ dir('a', [
+ libPubspec(
+ 'a',
+ '1.1.1',
+ deps: {'bar': '>=1.2.0 <1.5.0'},
+ devDeps: {
+ 'foo': '^1.2.0',
+ 'dev': '^1.0.0',
+ },
+ resolutionWorkspace: true,
+ ),
+ ]),
+ ]),
+ ]).create();
+ await pubGet(environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'});
+
+ final result = await Process.run(
+ Platform.resolvedExecutable,
+ [snapshot, 'list'],
+ environment: {
+ ...getPubTestEnvironment(),
+ '_PUB_TEST_SDK_VERSION': '3.5.0',
+ },
+ workingDirectory: p.join(d.sandbox, appPath, 'pkgs', 'a'),
+ );
+
+ expect(
+ result.stderr,
+ contains(
+ 'Only apply dependency_services to the root of the workspace.',
+ ),
+ );
+ expect(result.exitCode, 1);
+
+ await _listReportApply(
+ context,
+ [_PackageVersion('foo', '2.2.3'), _PackageVersion('transitive', '1.0.0')],
+ workspace: ['.', p.join('pkgs', 'a')],
+ reportAssertions: (report) {
+ expect(
+ findChangeVersion(report, 'singleBreaking', 'foo'),
+ '2.2.3',
+ );
+ expect(
+ findChangeVersion(report, 'singleBreaking', 'transitive'),
+ '1.0.0',
+ );
+ },
+ environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
+ );
+ });
}
dynamic findChangeVersion(dynamic json, String updateType, String name) {
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/can upgrade workspaces.txt b/test/testdata/goldens/dependency_services/dependency_services_test/can upgrade workspaces.txt
new file mode 100644
index 0000000..cf9e847
--- /dev/null
+++ b/test/testdata/goldens/dependency_services/dependency_services_test/can upgrade workspaces.txt
@@ -0,0 +1,400 @@
+# GENERATED BY: test/dependency_services/dependency_services_test.dart
+
+$ cat pubspec.yaml
+{"name":"myapp","version":"1.2.3","homepage":"https://pub.dev","description":"A package, I guess.","dependencies":{"foo":"^1.0.0","bar":"^1.0.0"},"environment":{"sdk":"^3.5.0"},"workspace":["pkgs/a"]}
+$ cat pkgs/a/pubspec.yaml
+{"name":"a","version":"1.1.1","homepage":"https://pub.dev","description":"A package, I guess.","dependencies":{"bar":">=1.2.0 <1.5.0"},"dev_dependencies":{"foo":"^1.2.0","dev":"^1.0.0"},"environment":{"sdk":"^3.5.0-0"},"resolution":"workspace"}
+$ cat pubspec.lock
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ bar:
+ dependency: "direct main"
+ description:
+ name: bar
+ sha256: $SHA256
+ url: "http://localhost:$PORT"
+ source: hosted
+ version: "1.2.3"
+ dev:
+ dependency: transitive
+ description:
+ name: dev
+ sha256: $SHA256
+ url: "http://localhost:$PORT"
+ source: hosted
+ version: "1.0.0"
+ foo:
+ dependency: "direct main"
+ description:
+ name: foo
+ sha256: $SHA256
+ url: "http://localhost:$PORT"
+ source: hosted
+ version: "1.2.3"
+sdks:
+ dart: ">=3.5.0 <4.0.0"
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section list
+$ echo '' | dependency_services list
+{
+ "dependencies": [
+ {
+ "name": "bar",
+ "version": "1.2.3",
+ "kind": "direct",
+ "constraint": ">=1.2.0 <1.5.0",
+ "source": {
+ "type": "hosted",
+ "description": {
+ "name": "bar",
+ "url": "http://localhost:$PORT",
+ "sha256": "0b119406be305b6e65d33551008b5b72fdd810965f0df914478c940d5fe28e53"
+ }
+ }
+ },
+ {
+ "name": "dev",
+ "version": "1.0.0",
+ "kind": "dev",
+ "constraint": "^1.0.0",
+ "source": {
+ "type": "hosted",
+ "description": {
+ "name": "dev",
+ "url": "http://localhost:$PORT",
+ "sha256": "fb990b7b071a76286080ee183e9ed4cd6d5538f8e1eccce8c1f2caf50c51c1bc"
+ }
+ }
+ },
+ {
+ "name": "foo",
+ "version": "1.2.3",
+ "kind": "direct",
+ "constraint": "^1.2.0",
+ "source": {
+ "type": "hosted",
+ "description": {
+ "name": "foo",
+ "url": "http://localhost:$PORT",
+ "sha256": "b2b7fc405959806aa1f31ac7e68752534f66f66a11a280d9878ecb6cd835f01c"
+ }
+ }
+ }
+ ]
+}
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section report
+$ echo '' | dependency_services report
+{
+ "dependencies": [
+ {
+ "name": "bar",
+ "version": "1.2.3",
+ "kind": "direct",
+ "source": {
+ "type": "hosted",
+ "description": {
+ "name": "bar",
+ "url": "http://localhost:$PORT",
+ "sha256": "0b119406be305b6e65d33551008b5b72fdd810965f0df914478c940d5fe28e53"
+ }
+ },
+ "latest": "2.2.3",
+ "constraint": ">=1.2.0 <1.5.0",
+ "compatible": [],
+ "singleBreaking": [
+ {
+ "name": "bar",
+ "version": "2.2.3",
+ "kind": "direct",
+ "source": {
+ "type": "hosted",
+ "description": {
+ "name": "bar",
+ "url": "http://localhost:$PORT",
+ "sha256": "18169c0899ff5f0551a80839dc1618e597a6ee4508a5065f9318dd2a2fda6455"
+ }
+ },
+ "constraintBumped": "^2.2.3",
+ "constraintWidened": ">=1.2.0 <3.0.0",
+ "constraintBumpedIfNeeded": "^2.2.3",
+ "previousVersion": "1.2.3",
+ "previousConstraint": ">=1.2.0 <1.5.0",
+ "previousSource": {
+ "type": "hosted",
+ "description": {
+ "name": "bar",
+ "url": "http://localhost:$PORT",
+ "sha256": "0b119406be305b6e65d33551008b5b72fdd810965f0df914478c940d5fe28e53"
+ }
+ }
+ }
+ ],
+ "multiBreaking": [
+ {
+ "name": "bar",
+ "version": "2.2.3",
+ "kind": "direct",
+ "source": {
+ "type": "hosted",
+ "description": {
+ "name": "bar",
+ "url": "http://localhost:$PORT",
+ "sha256": "18169c0899ff5f0551a80839dc1618e597a6ee4508a5065f9318dd2a2fda6455"
+ }
+ },
+ "constraintBumped": "^2.2.3",
+ "constraintWidened": ">=1.2.0 <3.0.0",
+ "constraintBumpedIfNeeded": "^2.2.3",
+ "previousVersion": "1.2.3",
+ "previousConstraint": ">=1.2.0 <1.5.0",
+ "previousSource": {
+ "type": "hosted",
+ "description": {
+ "name": "bar",
+ "url": "http://localhost:$PORT",
+ "sha256": "0b119406be305b6e65d33551008b5b72fdd810965f0df914478c940d5fe28e53"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "dev",
+ "version": "1.0.0",
+ "kind": "dev",
+ "source": {
+ "type": "hosted",
+ "description": {
+ "name": "dev",
+ "url": "http://localhost:$PORT",
+ "sha256": "fb990b7b071a76286080ee183e9ed4cd6d5538f8e1eccce8c1f2caf50c51c1bc"
+ }
+ },
+ "latest": "2.0.0",
+ "constraint": "^1.0.0",
+ "compatible": [],
+ "singleBreaking": [
+ {
+ "name": "dev",
+ "version": "2.0.0",
+ "kind": "dev",
+ "source": {
+ "type": "hosted",
+ "description": {
+ "name": "dev",
+ "url": "http://localhost:$PORT",
+ "sha256": "e3496752d80b78354cd745f8d1e381c42022b14624233c9e5306711171418d09"
+ }
+ },
+ "constraintBumped": "^2.0.0",
+ "constraintWidened": ">=1.0.0 <3.0.0",
+ "constraintBumpedIfNeeded": "^2.0.0",
+ "previousVersion": "1.0.0",
+ "previousConstraint": "^1.0.0",
+ "previousSource": {
+ "type": "hosted",
+ "description": {
+ "name": "dev",
+ "url": "http://localhost:$PORT",
+ "sha256": "fb990b7b071a76286080ee183e9ed4cd6d5538f8e1eccce8c1f2caf50c51c1bc"
+ }
+ }
+ }
+ ],
+ "multiBreaking": [
+ {
+ "name": "dev",
+ "version": "2.0.0",
+ "kind": "dev",
+ "source": {
+ "type": "hosted",
+ "description": {
+ "name": "dev",
+ "url": "http://localhost:$PORT",
+ "sha256": "e3496752d80b78354cd745f8d1e381c42022b14624233c9e5306711171418d09"
+ }
+ },
+ "constraintBumped": "^2.0.0",
+ "constraintWidened": ">=1.0.0 <3.0.0",
+ "constraintBumpedIfNeeded": "^2.0.0",
+ "previousVersion": "1.0.0",
+ "previousConstraint": "^1.0.0",
+ "previousSource": {
+ "type": "hosted",
+ "description": {
+ "name": "dev",
+ "url": "http://localhost:$PORT",
+ "sha256": "fb990b7b071a76286080ee183e9ed4cd6d5538f8e1eccce8c1f2caf50c51c1bc"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "foo",
+ "version": "1.2.3",
+ "kind": "direct",
+ "source": {
+ "type": "hosted",
+ "description": {
+ "name": "foo",
+ "url": "http://localhost:$PORT",
+ "sha256": "b2b7fc405959806aa1f31ac7e68752534f66f66a11a280d9878ecb6cd835f01c"
+ }
+ },
+ "latest": "2.2.3",
+ "constraint": "^1.2.0",
+ "compatible": [],
+ "singleBreaking": [
+ {
+ "name": "foo",
+ "version": "2.2.3",
+ "kind": "direct",
+ "source": {
+ "type": "hosted",
+ "description": {
+ "name": "foo",
+ "url": "http://localhost:$PORT",
+ "sha256": "a7e0832c069301a6e6ba78d792a08156fbf5bbfc04e766393ad7c98e7a27f648"
+ }
+ },
+ "constraintBumped": "^2.2.3",
+ "constraintWidened": ">=1.2.0 <3.0.0",
+ "constraintBumpedIfNeeded": "^2.2.3",
+ "previousVersion": "1.2.3",
+ "previousConstraint": "^1.2.0",
+ "previousSource": {
+ "type": "hosted",
+ "description": {
+ "name": "foo",
+ "url": "http://localhost:$PORT",
+ "sha256": "b2b7fc405959806aa1f31ac7e68752534f66f66a11a280d9878ecb6cd835f01c"
+ }
+ }
+ },
+ {
+ "name": "transitive",
+ "version": "1.0.0",
+ "kind": "transitive",
+ "source": {
+ "type": "hosted",
+ "description": {
+ "name": "transitive",
+ "url": "http://localhost:$PORT",
+ "sha256": "d705923b5a6b4c7053e6d86a35799ede6467245151ff15dfdd5719986a61d49c"
+ }
+ },
+ "constraintBumped": null,
+ "constraintWidened": null,
+ "constraintBumpedIfNeeded": null,
+ "previousVersion": null,
+ "previousConstraint": null,
+ "previousSource": null
+ }
+ ],
+ "multiBreaking": [
+ {
+ "name": "foo",
+ "version": "2.2.3",
+ "kind": "direct",
+ "source": {
+ "type": "hosted",
+ "description": {
+ "name": "foo",
+ "url": "http://localhost:$PORT",
+ "sha256": "a7e0832c069301a6e6ba78d792a08156fbf5bbfc04e766393ad7c98e7a27f648"
+ }
+ },
+ "constraintBumped": "^2.2.3",
+ "constraintWidened": ">=1.2.0 <3.0.0",
+ "constraintBumpedIfNeeded": "^2.2.3",
+ "previousVersion": "1.2.3",
+ "previousConstraint": "^1.2.0",
+ "previousSource": {
+ "type": "hosted",
+ "description": {
+ "name": "foo",
+ "url": "http://localhost:$PORT",
+ "sha256": "b2b7fc405959806aa1f31ac7e68752534f66f66a11a280d9878ecb6cd835f01c"
+ }
+ }
+ },
+ {
+ "name": "transitive",
+ "version": "1.0.0",
+ "kind": "transitive",
+ "source": {
+ "type": "hosted",
+ "description": {
+ "name": "transitive",
+ "url": "http://localhost:$PORT",
+ "sha256": "d705923b5a6b4c7053e6d86a35799ede6467245151ff15dfdd5719986a61d49c"
+ }
+ },
+ "constraintBumped": null,
+ "constraintWidened": null,
+ "constraintBumpedIfNeeded": null,
+ "previousVersion": null,
+ "previousConstraint": null,
+ "previousSource": null
+ }
+ ]
+ }
+ ]
+}
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section apply
+$ echo '{"dependencyChanges":[{"name":"foo","version":"2.2.3"},{"name":"transitive","version":"1.0.0"}]}' | dependency_services apply
+{"dependencies":[]}
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+$ cat pubspec.yaml
+{"name":"myapp","version":"1.2.3","homepage":"https://pub.dev","description":"A package, I guess.","dependencies":{"foo":^2.2.3,"bar":"^1.0.0"},"environment":{"sdk":"^3.5.0"},"workspace":["pkgs/a"]}
+$ cat pkgs/a/pubspec.yaml
+{"name":"a","version":"1.1.1","homepage":"https://pub.dev","description":"A package, I guess.","dependencies":{"bar":">=1.2.0 <1.5.0"},"dev_dependencies":{"foo":^2.2.3,"dev":"^1.0.0"},"environment":{"sdk":"^3.5.0-0"},"resolution":"workspace"}
+$ cat pubspec.lock
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ bar:
+ dependency: "direct main"
+ description:
+ name: bar
+ sha256: $SHA256
+ url: "http://localhost:$PORT"
+ source: hosted
+ version: "1.2.3"
+ dev:
+ dependency: transitive
+ description:
+ name: dev
+ sha256: $SHA256
+ url: "http://localhost:$PORT"
+ source: hosted
+ version: "1.0.0"
+ foo:
+ dependency: "direct main"
+ description:
+ name: foo
+ sha256: $SHA256
+ url: "http://localhost:$PORT"
+ source: hosted
+ version: "2.2.3"
+ transitive:
+ dependency: transitive
+ description:
+ name: transitive
+ sha256: $SHA256
+ url: "http://localhost:$PORT"
+ source: hosted
+ version: "1.0.0"
+sdks:
+ dart: ">=3.5.0 <4.0.0"