diff --git a/lib/src/command/dependency_services.dart b/lib/src/command/dependency_services.dart index baffc2b31e..5f3eb724e3 100644 --- a/lib/src/command/dependency_services.dart +++ b/lib/src/command/dependency_services.dart @@ -25,6 +25,7 @@ import '../package.dart'; 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 @@ class DependencyServicesReportCommand extends PubCommand { @override Future runProtected() async { + _checkAtRoot(entrypoint); final stdinString = await utf8.decodeStream(stdin); final input = json.decode(stdinString.isEmpty ? '{}' : stdinString) as Map; @@ -65,27 +67,21 @@ class DependencyServicesReportCommand extends PubCommand { 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 @@ class DependencyServicesReportCommand extends PubCommand { ?.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 @@ class DependencyServicesReportCommand extends PubCommand { (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 @@ class DependencyServicesReportCommand extends PubCommand { _UpgradeType upgradeType, ) async { return await _computeUpgradeSet( - compatiblePubspec, + compatibleWorkspace, package, entrypoint, cache, @@ -174,8 +165,8 @@ class DependencyServicesReportCommand extends PubCommand { '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 @@ class DependencyServicesListCommand extends PubCommand { @override Future 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, ) ?? []); @@ -246,8 +232,10 @@ class DependencyServicesListCommand extends PubCommand { 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 @@ class DependencyServicesApplyCommand extends PubCommand { @override Future 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 @@ class DependencyServicesApplyCommand extends PubCommand { ), ); } - - final pubspec = entrypoint.workspaceRoot.pubspec; - final pubspecEditor = - YamlEditor(readTextFile(entrypoint.workspaceRoot.pubspecPath)); + final updatedPubspecs = {}; + _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 @@ class DependencyServicesApplyCommand extends PubCommand { 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 @@ class DependencyServicesApplyCommand extends PubCommand { ); 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 @@ class DependencyServicesApplyCommand extends PubCommand { 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 @@ class DependencyServicesApplyCommand extends PubCommand { 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 @@ class DependencyServicesApplyCommand extends PubCommand { } } +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 @@ Future?> _tryResolve( 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 @@ Future> _computeCurrentPackages( 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> _computeUpgradeSet( - Pubspec rootPubspec, + Package workspace, PackageId? package, Entrypoint entrypoint, SystemCache cache, { @@ -736,16 +755,18 @@ Future> _computeUpgradeSet( }) 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(); - - 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); + ? workspace.transformWorkspace((p) => stripVersionBounds(p.pubspec)) + : workspace.transformWorkspace((p) => p.pubspec.copyWith()); + + 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 @@ Future> _computeUpgradeSet( ? SolveType.downgrade : SolveType.get, cache, - Package( - pubspec, - entrypoint.workspaceRoot.dir, - entrypoint.workspaceRoot.workspaceChildren, - ), + upgradedWorkspace, lockFile: lockFile, additionalConstraints: additionalConstraints, ); @@ -766,40 +783,43 @@ Future> _computeUpgradeSet( 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 11ccb68c34..ab3516c6cc 100644 --- a/test/dependency_services/dependency_services_test.dart +++ b/test/dependency_services/dependency_services_test.dart @@ -18,24 +18,25 @@ import '../descriptor.dart'; import '../golden_file.dart'; import '../test_pub.dart'; -void manifestAndLockfile(GoldenTestContext context) { +void manifestAndLockfile(GoldenTestContext context, List 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 @@ extension on GoldenTestContext { Future runDependencyServices( List args, { String stdin = '', + Map? environment, }) async { final buffer = StringBuffer(); buffer.writeln('## Section ${args.join(' ')}'); @@ -61,6 +63,7 @@ extension on GoldenTestContext { environment: { ...getPubTestEnvironment(), '_PUB_TEST_DEFAULT_HOSTED_URL': globalServer.url, + ...?environment, }, workingDirectory: p.join(d.sandbox, appPath), ); @@ -96,11 +99,14 @@ Future> outputLines(Stream> stream) async { Future _listReportApply( GoldenTestContext context, List<_PackageVersion> upgrades, { + List workspace = const ['.'], void Function(Map)? reportAssertions, + Map? 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 @@ Future _listReportApply( 'dependencyChanges': upgrades, }); - await context.runDependencyServices(['apply'], stdin: input); - manifestAndLockfile(context); + await context.runDependencyServices( + ['apply'], + stdin: input, + environment: environment, + ); + manifestAndLockfile(context, workspace); } Future _reportWithForbidden( @@ -118,7 +128,7 @@ Future _reportWithForbidden( void Function(Map)? resultAssertions, String? targetPackage, }) async { - manifestAndLockfile(context); + manifestAndLockfile(context, ['.']); final input = json.encode({ 'target': targetPackage, 'disallowed': [ @@ -136,7 +146,7 @@ Future _reportWithForbidden( } // await context.runDependencyServices(['apply'], stdin: input); - manifestAndLockfile(context); + manifestAndLockfile(context, ['.']); } Future main() async { @@ -628,6 +638,83 @@ Future main() async { }, ); }); + + 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 0000000000..cf9e8476d8 --- /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"