Smallest update in `dependency-services report` (#3761)
diff --git a/lib/src/command/dependency_services.dart b/lib/src/command/dependency_services.dart index 44ae700..f1d8143 100644 --- a/lib/src/command/dependency_services.dart +++ b/lib/src/command/dependency_services.dart
@@ -14,6 +14,7 @@ import 'package:yaml_edit/yaml_edit.dart'; import '../command.dart'; +import '../entrypoint.dart'; import '../exceptions.dart'; import '../io.dart'; import '../lock_file.dart'; @@ -23,6 +24,7 @@ import '../pubspec.dart'; import '../pubspec_utils.dart'; import '../solver.dart'; +import '../solver/version_solver.dart'; import '../source/git.dart'; import '../source/hosted.dart'; import '../system_cache.dart'; @@ -51,133 +53,42 @@ @override Future<void> runProtected() async { + final stdinString = await utf8.decodeStream(stdin); + final input = json.decode(stdinString.isEmpty ? '{}' : stdinString) + as Map<String, Object?>; + final additionalConstraints = _parseDisallowed(input, cache); + final targetPackageName = input['target']; + if (targetPackageName is! String?) { + throw FormatException('"target" should be a String.'); + } + final compatiblePubspec = stripDependencyOverrides(entrypoint.root.pubspec); final breakingPubspec = stripVersionBounds(compatiblePubspec); - final compatiblePackagesResult = - await _tryResolve(compatiblePubspec, cache); + final compatiblePackagesResult = await _tryResolve( + compatiblePubspec, + cache, + additionalConstraints: additionalConstraints, + ); - final breakingPackagesResult = await _tryResolve(breakingPubspec, cache); + final breakingPackagesResult = await _tryResolve( + breakingPubspec, + cache, + additionalConstraints: additionalConstraints, + ); - // The packages in the current lockfile or resolved from current pubspec.yaml. - late Map<String, PackageId> currentPackages; - - if (fileExists(entrypoint.lockFilePath)) { - currentPackages = - Map<String, PackageId>.from(entrypoint.lockFile.packages); - } else { - final resolution = await _tryResolve(entrypoint.root.pubspec, cache) ?? - (throw DataException('Failed to resolve pubspec')); - currentPackages = Map<String, PackageId>.fromIterable( - resolution, - key: (e) => (e as PackageId).name, - ); - } - currentPackages.remove(entrypoint.root.name); + final currentPackages = await _computeCurrentPackages(entrypoint, cache); final dependencies = <Object>[]; final result = <String, Object>{'dependencies': dependencies}; - Future<List<Object>> computeUpgradeSet( - Pubspec rootPubspec, - PackageId? package, { - required _UpgradeType upgradeType, - }) async { - if (package == null) return []; - final lockFile = entrypoint.lockFile; - final pubspec = upgradeType == _UpgradeType.multiBreaking - ? stripVersionBounds(rootPubspec) - : Pubspec( - rootPubspec.name, - dependencies: rootPubspec.dependencies.values, - devDependencies: rootPubspec.devDependencies.values, - sdkConstraints: rootPubspec.sdkConstraints, - ); + final targetPackage = + targetPackageName == null ? null : currentPackages[targetPackageName]; - 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); - } - - final resolution = await tryResolveVersions( - SolveType.get, - cache, - Package.inMemory(pubspec), - lockFile: lockFile, - ); - - // TODO(sigurdm): improve error messages. - if (resolution == null) { - throw DataException('Failed resolving'); - } - - return [ - ...resolution.packages.where((r) { - if (r.name == rootPubspec.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 currentPackage = currentPackages[p.name]; - return { - 'name': p.name, - 'version': p.versionOrHash(), - 'kind': _kindString(pubspec, p.name), - 'source': _source(p, containingDir: directory), - 'constraintBumped': originalConstraint == null - ? null - : upgradeType == _UpgradeType.compatible - ? originalConstraint.toString() - : _bumpConstraint(originalConstraint, p.version).toString(), - 'constraintWidened': originalConstraint == null - ? null - : upgradeType == _UpgradeType.compatible - ? originalConstraint.toString() - : _widenConstraint(originalConstraint, p.version) - .toString(), - 'constraintBumpedIfNeeded': originalConstraint == null - ? null - : upgradeType == _UpgradeType.compatible - ? originalConstraint.toString() - : originalConstraint.allows(p.version) - ? originalConstraint.toString() - : _bumpConstraint(originalConstraint, p.version) - .toString(), - 'previousVersion': currentPackage?.versionOrHash(), - 'previousConstraint': originalConstraint?.toString(), - 'previousSource': currentPackage == null - ? null - : _source(currentPackage, containingDir: directory), - }; - }), - // Find packages that were removed by the resolution - for (final oldPackageName in lockFile.packages.keys) - if (!resolution.packages - .any((newPackage) => newPackage.name == oldPackageName)) - { - 'name': oldPackageName, - 'version': null, - 'kind': - 'transitive', // Only transitive constraints can be removed. - 'constraintBumped': null, - 'constraintWidened': null, - 'constraintBumpedIfNeeded': null, - 'previousVersion': - currentPackages[oldPackageName]?.versionOrHash(), - 'previousConstraint': null, - 'previous': _source( - currentPackages[oldPackageName]!, - containingDir: directory, - ) - }, - ]; - } - - for (final package in currentPackages.values) { + for (final package in targetPackage == null + ? currentPackages.values + : <PackageId>[targetPackage]) { final compatibleVersion = compatiblePackagesResult ?.firstWhereOrNull((element) => element.name == package.name); final multiBreakingVersion = breakingPackagesResult @@ -202,6 +113,42 @@ singleBreakingVersion = singleBreakingPackagesResult ?.firstWhereOrNull((element) => element.name == package.name); } + PackageId? smallestUpgrade; + if (additionalConstraints.any( + (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 smallestUpgradeResult = await _tryResolve( + atLeastCurrentPubspec, + cache, + solveType: SolveType.downgrade, + additionalConstraints: additionalConstraints, + ); + + smallestUpgrade = smallestUpgradeResult + ?.firstWhereOrNull((element) => element.name == package.name); + } + + Future<List<Object>> computeUpgradeSet( + PackageId? package, + _UpgradeType upgradeType, + ) async { + return await _computeUpgradeSet( + compatiblePubspec, + package, + entrypoint, + cache, + currentPackages: currentPackages, + upgradeType: upgradeType, + additionalConstraints: additionalConstraints, + ); + } + dependencies.add({ 'name': package.name, 'version': package.versionOrHash(), @@ -213,64 +160,32 @@ 'constraint': _constraintOf(compatiblePubspec, package.name)?.toString(), 'compatible': await computeUpgradeSet( - compatiblePubspec, compatibleVersion, - upgradeType: _UpgradeType.compatible, + _UpgradeType.compatible, ), 'singleBreaking': kind != 'transitive' && singleBreakingVersion == null ? [] : await computeUpgradeSet( - compatiblePubspec, singleBreakingVersion, - upgradeType: _UpgradeType.singleBreaking, + _UpgradeType.singleBreaking, ), 'multiBreaking': kind != 'transitive' && multiBreakingVersion != null ? await computeUpgradeSet( - compatiblePubspec, multiBreakingVersion, - upgradeType: _UpgradeType.multiBreaking, + _UpgradeType.multiBreaking, ) : [], + if (smallestUpgrade != null) + 'smallestUpdate': await computeUpgradeSet( + smallestUpgrade, + _UpgradeType.smallestUpdate, + ), }); } log.message(JsonEncoder.withIndent(' ').convert(result)); } } -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) - ? 'direct' - : pubspec.devDependencies.containsKey(packageName) - ? 'dev' - : 'transitive'; -} - -Map<String, Object?> _source(PackageId id, {required String containingDir}) { - return { - 'type': id.source.name, - 'description': - id.description.serializeForLockfile(containingDir: containingDir), - }; -} - -/// Try to solve [pubspec] return [PackageId]s in the resolution or `null` if no -/// resolution was found. -Future<List<PackageId>?> _tryResolve(Pubspec pubspec, SystemCache cache) async { - final solveResult = await tryResolveVersions( - SolveType.upgrade, - cache, - Package.inMemory(pubspec), - ); - - return solveResult?.packages; -} - class DependencyServicesListCommand extends PubCommand { @override String get name => 'list'; @@ -336,6 +251,9 @@ /// Unlock any dependencies in pubspec.yaml needed for getting the /// latest resolvable version. multiBreaking, + + /// Try to upgrade as little as possible. + smallestUpdate, } class DependencyServicesApplyCommand extends PubCommand { @@ -715,6 +633,231 @@ return false; } +/// Try to solve [pubspec] return [PackageId]s in the resolution or `null` if no +/// resolution was found. +Future<List<PackageId>?> _tryResolve( + Pubspec pubspec, + SystemCache cache, { + SolveType solveType = SolveType.upgrade, + Iterable<ConstraintAndCause>? additionalConstraints, +}) async { + final solveResult = await tryResolveVersions( + solveType, + cache, + Package.inMemory(pubspec), + additionalConstraints: additionalConstraints, + ); + + return solveResult?.packages; +} + +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) + ? 'direct' + : pubspec.devDependencies.containsKey(packageName) + ? 'dev' + : 'transitive'; +} + +Map<String, Object?> _source(PackageId id, {required String containingDir}) { + return { + 'type': id.source.name, + 'description': + id.description.serializeForLockfile(containingDir: containingDir), + }; +} + +/// The packages in the current lockfile or resolved from current pubspec.yaml. +/// Does not include the root package. +Future<Map<String, PackageId>> _computeCurrentPackages( + Entrypoint entrypoint, + SystemCache cache, +) async { + late Map<String, PackageId> currentPackages; + + if (fileExists(entrypoint.lockFilePath)) { + currentPackages = Map<String, PackageId>.from(entrypoint.lockFile.packages); + } else { + final resolution = await _tryResolve(entrypoint.root.pubspec, cache) ?? + (throw DataException('Failed to resolve pubspec')); + currentPackages = Map<String, PackageId>.fromIterable( + resolution, + key: (e) => (e as PackageId).name, + ); + } + currentPackages.remove(entrypoint.root.name); + return currentPackages; +} + +Future<List<Object>> _computeUpgradeSet( + Pubspec rootPubspec, + PackageId? package, + Entrypoint entrypoint, + SystemCache cache, { + required Map<String, PackageId> currentPackages, + required _UpgradeType upgradeType, + required List<ConstraintAndCause> additionalConstraints, +}) async { + if (package == null) return []; + final lockFile = entrypoint.lockFile; + final pubspec = (upgradeType == _UpgradeType.multiBreaking || + upgradeType == _UpgradeType.smallestUpdate) + ? stripVersionBounds(rootPubspec) + : Pubspec( + rootPubspec.name, + dependencies: rootPubspec.dependencies.values, + devDependencies: rootPubspec.devDependencies.values, + sdkConstraints: rootPubspec.sdkConstraints, + ); + + 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); + } + + final resolution = await tryResolveVersions( + upgradeType == _UpgradeType.smallestUpdate + ? SolveType.downgrade + : SolveType.get, + cache, + Package.inMemory(pubspec), + lockFile: lockFile, + additionalConstraints: additionalConstraints, + ); + + // TODO(sigurdm): improve error messages. + if (resolution == null) { + return []; + } + + return [ + ...resolution.packages.where((r) { + if (r.name == rootPubspec.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 currentPackage = currentPackages[p.name]; + return { + 'name': p.name, + 'version': p.versionOrHash(), + 'kind': _kindString(pubspec, p.name), + 'source': _source(p, containingDir: entrypoint.root.dir), + 'constraintBumped': originalConstraint == null + ? null + : upgradeType == _UpgradeType.compatible + ? originalConstraint.toString() + : _bumpConstraint(originalConstraint, p.version).toString(), + 'constraintWidened': originalConstraint == null + ? null + : upgradeType == _UpgradeType.compatible + ? originalConstraint.toString() + : _widenConstraint(originalConstraint, p.version).toString(), + 'constraintBumpedIfNeeded': originalConstraint == null + ? null + : upgradeType == _UpgradeType.compatible + ? originalConstraint.toString() + : originalConstraint.allows(p.version) + ? originalConstraint.toString() + : _bumpConstraint(originalConstraint, p.version).toString(), + 'previousVersion': currentPackage?.versionOrHash(), + 'previousConstraint': originalConstraint?.toString(), + 'previousSource': currentPackage == null + ? null + : _source(currentPackage, containingDir: entrypoint.root.dir), + }; + }), + // Find packages that were removed by the resolution + for (final oldPackageName in lockFile.packages.keys) + if (!resolution.packages + .any((newPackage) => newPackage.name == oldPackageName)) + { + 'name': oldPackageName, + 'version': null, + 'kind': 'transitive', // Only transitive constraints can be removed. + 'constraintBumped': null, + 'constraintWidened': null, + 'constraintBumpedIfNeeded': null, + 'previousVersion': currentPackages[oldPackageName]?.versionOrHash(), + 'previousConstraint': null, + 'previous': _source( + currentPackages[oldPackageName]!, + containingDir: entrypoint.root.dir, + ) + }, + ]; +} + +List<ConstraintAndCause> _parseDisallowed( + Map<String, Object?> input, + SystemCache cache, +) { + final disallowedList = input['disallowed']; + if (disallowedList == null) { + return []; + } + if (disallowedList is! List<Object?>) { + throw FormatException('Disallowed should be a list of maps'); + } + final result = <ConstraintAndCause>[]; + for (final disallowed in disallowedList) { + if (disallowed is! Map) { + throw FormatException('Disallowed should be a list of maps'); + } + final name = disallowed['name']; + if (name is! String) { + throw FormatException('"name" should be a string.'); + } + final url = disallowed['url'] ?? cache.hosted.defaultUrl; + if (url is! String) { + throw FormatException('"url" should be a string.'); + } + final ref = PackageRef( + name, + HostedDescription( + name, + url, + ), + ); + final constraints = disallowed['versions']; + if (constraints is! List) { + throw FormatException('"versions" should be a list.'); + } + final reason = disallowed['reason']; + if (reason is! String?) { + throw FormatException('"reason", if present, should be a string.'); + } + for (final entry in constraints) { + if (entry is! Map) { + throw FormatException( + 'Each element of "versions" should be an object.', + ); + } + final rangeString = entry['range']; + if (rangeString is! String) { + throw FormatException('"range" should be a string'); + } + final range = VersionConstraint.parse(rangeString); + result.add( + ConstraintAndCause( + PackageRange(ref, VersionConstraint.any.difference(range)), + reason, + ), + ); + } + } + return result; +} + /// `true` iff any of the packages described by the [lockfile] uses /// `https://pub.dev` as url. ///
diff --git a/lib/src/pubspec_utils.dart b/lib/src/pubspec_utils.dart index 3f57976..5edf74a 100644 --- a/lib/src/pubspec_utils.dart +++ b/lib/src/pubspec_utils.dart
@@ -2,6 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'package:collection/collection.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package_name.dart'; @@ -84,6 +85,42 @@ ); } +/// Returns a pubspec with the same dependencies as [original] but with all +/// version constraints replaced by `>=c` where `c`, is the member of `current` +/// that has same name as the dependency. +Pubspec atLeastCurrent(Pubspec original, List<PackageId> current) { + List<PackageRange> fixBounds( + Map<String, PackageRange> constrained, + ) { + final result = <PackageRange>[]; + + for (final name in constrained.keys) { + final packageRange = constrained[name]!; + final currentVersion = current.firstWhereOrNull((id) => id.name == name); + if (currentVersion == null) { + result.add(packageRange); + } else { + result.add( + packageRange.toRef().withConstraint( + VersionRange(min: currentVersion.version, includeMin: true), + ), + ); + } + } + + return result; + } + + return Pubspec( + original.name, + version: original.version, + sdkConstraints: original.sdkConstraints, + dependencies: fixBounds(original.dependencies), + devDependencies: fixBounds(original.devDependencies), + dependencyOverrides: original.dependencyOverrides.values, + ); +} + /// Removes the upper bound of [constraint]. If [constraint] is the /// empty version constraint, [VersionConstraint.empty] will be returned. VersionConstraint stripUpperBound(VersionConstraint constraint) {
diff --git a/lib/src/solver.dart b/lib/src/solver.dart index b8f68e7..71e2fcd 100644 --- a/lib/src/solver.dart +++ b/lib/src/solver.dart
@@ -29,6 +29,9 @@ /// If [unlock] is empty [SolveType.get] interprets this as lock everything, /// while [SolveType.upgrade] and [SolveType.downgrade] interprets an empty /// [unlock] as unlock everything. +/// +/// [additionalConstraints] can contain a list of extra constraints for this +/// resolution. Future<SolveResult> resolveVersions( SolveType type, SystemCache cache, @@ -36,16 +39,21 @@ LockFile? lockFile, Iterable<String> unlock = const [], Map<String, Version> sdkOverrides = const {}, + Iterable<ConstraintAndCause>? additionalConstraints, }) { lockFile ??= LockFile.empty(); - return VersionSolver( + final solver = VersionSolver( type, cache, root, lockFile, unlock, sdkOverrides: sdkOverrides, - ).solve(); + ); + if (additionalConstraints != null) { + solver.addConstraints(additionalConstraints); + } + return solver.solve(); } /// Attempts to select the best concrete versions for all of the transitive @@ -68,6 +76,7 @@ Package root, { LockFile? lockFile, Iterable<String>? unlock, + Iterable<ConstraintAndCause>? additionalConstraints, }) async { try { return await resolveVersions( @@ -76,6 +85,7 @@ root, lockFile: lockFile, unlock: unlock ?? [], + additionalConstraints: additionalConstraints, ); } on SolveFailure { return null;
diff --git a/lib/src/solver/incompatibility_cause.dart b/lib/src/solver/incompatibility_cause.dart index 13047df..01b9dcf 100644 --- a/lib/src/solver/incompatibility_cause.dart +++ b/lib/src/solver/incompatibility_cause.dart
@@ -147,3 +147,15 @@ @override String? get hint => exception.hint; } + +/// The incompatibility represents a package-version that is not allowed to be +/// used in the solve for some external reason. +class PackageVersionForbiddenCause extends IncompatibilityCause { + /// The reason this package version was forbidden. + final String? reason; + + PackageVersionForbiddenCause({this.reason}); + + @override + String? get hint => reason; +}
diff --git a/lib/src/solver/version_solver.dart b/lib/src/solver/version_solver.dart index afef413..5194e59 100644 --- a/lib/src/solver/version_solver.dart +++ b/lib/src/solver/version_solver.dart
@@ -92,6 +92,18 @@ _dependencyOverrides = _root.dependencyOverrides, _unlock = {...unlock}; + /// Prime the solver with [constraints]. + void addConstraints(Iterable<ConstraintAndCause> constraints) { + for (final constraint in constraints) { + _addIncompatibility( + Incompatibility( + [Term(constraint.range, false)], + PackageVersionForbiddenCause(reason: constraint.cause), + ), + ); + } + } + /// Finds a set of dependencies that match the root package's constraints, or /// throws an error if no such set is available. Future<SolveResult> solve() async { @@ -581,3 +593,20 @@ log.solver(prefixLines(message, prefix: ' ' * _solution.decisionLevel)); } } + +// An additional constraint to a version resolution. +class ConstraintAndCause { + /// Stated like constraints in the pubspec. (The constraint specifies those + /// versions that are allowed). + /// + /// Meaning that to forbid a version you must do + /// `VersionConstraint.any.difference(version)`. + /// + /// Example: + /// `ConstraintAndCause(packageRef, VersionConstraint.parse('> 1.0.0'))` + /// To require `packageRef` be greater than `1.0.0`. + final PackageRange range; + final String? cause; + + ConstraintAndCause(this.range, this.cause); +}
diff --git a/test/dependency_services/dependency_services_test.dart b/test/dependency_services/dependency_services_test.dart index 4dbef36..f372701 100644 --- a/test/dependency_services/dependency_services_test.dart +++ b/test/dependency_services/dependency_services_test.dart
@@ -46,7 +46,7 @@ /// Returns the stdout. Future<String> runDependencyServices( List<String> args, { - String? stdin, + String stdin = '', }) async { final buffer = StringBuffer(); buffer.writeln('## Section ${args.join(' ')}'); @@ -64,16 +64,15 @@ }, workingDirectory: p.join(d.sandbox, appPath), ); - if (stdin != null) { - process.stdin.write(stdin); - await process.stdin.flush(); - await process.stdin.close(); - } + process.stdin.write(stdin); + await process.stdin.flush(); + await process.stdin.close(); + final outLines = outputLines(process.stdout); final errLines = outputLines(process.stderr); final exitCode = await process.exitCode; - final pipe = stdin == null ? '' : ' echo ${escapeShellArgument(stdin)} |'; + final pipe = ' echo ${filterUnstableText(escapeShellArgument(stdin))} |'; buffer.writeln( [ '\$$pipe dependency_services ${args.map(escapeShellArgument).join(' ')}', @@ -113,6 +112,33 @@ manifestAndLockfile(context); } +Future<void> _reportWithForbidden( + GoldenTestContext context, + Map<String, List<String>> disallowedVersions, { + void Function(Map)? resultAssertions, + String? targetPackage, +}) async { + manifestAndLockfile(context); + final input = json.encode({ + 'target': targetPackage, + 'disallowed': [ + for (final e in disallowedVersions.entries) + { + 'name': e.key, + 'url': globalServer.url, + 'versions': e.value.map((d) => {'range': d}).toList() + } + ] + }); + final report = await context.runDependencyServices(['report'], stdin: input); + if (resultAssertions != null) { + resultAssertions(json.decode(report) as Map); + } + + // await context.runDependencyServices(['apply'], stdin: input); + manifestAndLockfile(context); +} + Future<void> main() async { setUpAll(() async { final tempDir = Directory.systemTemp.createTempSync(); @@ -161,6 +187,39 @@ ); }); + testWithGolden('Ignoring version', (context) async { + final server = await servePackages(); + server.serve('foo', '1.0.0'); + + await d.dir(appPath, [ + d.pubspec({ + 'name': 'app', + 'dependencies': { + 'foo': '^1.0.0', + }, + }) + ]).create(); + await pubGet(); + + server.serve('foo', '1.0.1'); // should get this. + server.serve('foo', '1.0.2'); // ignored + server.serve('foo', '1.0.3', deps: {'transitive': '1.0.0'}); + server.serve('transitive', '1.0.0'); // ignored + await _reportWithForbidden( + context, + { + 'foo': ['1.0.2'], + 'transitive': ['1.0.0'], + }, + resultAssertions: (report) { + expect( + findChangeVersion(report, 'compatible', 'foo'), + '1.0.1', + ); + }, + ); + }); + testWithGolden('No pubspec.lock', (context) async { final server = (await servePackages()) ..serve('foo', '1.2.3', deps: {'transitive': '^1.0.0'}) @@ -465,6 +524,110 @@ }, ); }); + + testWithGolden('Finds smallest possible upgrade', (context) async { + final server = await servePackages(); + server.serve('foo', '1.1.1'); // This version will be disallowed. + + await d.appDir(dependencies: {'foo': '^1.0.0'}).create(); + await pubGet(); + server.serve( + 'foo', + '1.0.0', + ); // We don't want the downgrade to go below the current. + + server.serve( + 'foo', + '1.1.2', + ); // This will also be disallowed, a minimal update should not find this. + server.serve('foo', '1.1.3'); // We would like this to be the new version. + server.serve('foo', '1.1.4'); // This version would not be a minimal update. + + await _reportWithForbidden( + context, + { + 'foo': ['1.1.1', '1.1.2'] + }, + targetPackage: 'foo', + resultAssertions: (r) { + expect(findChangeVersion(r, 'smallestUpdate', 'foo'), '1.1.3'); + }, + ); + }); + + testWithGolden('Smallest possible upgrade can upgrade beyond breaking', + (context) async { + final server = await servePackages(); + server.serve('foo', '1.1.1'); // This version will be disallowed. + + await d.appDir(dependencies: {'foo': '^1.0.0'}).create(); + await pubGet(); + + server.serve( + 'foo', + '2.0.0', + ); // This will also be disallowed, a minimal update should not find this. + server.serve('foo', '2.0.1'); // We would like this to be the new version. + server.serve('foo', '2.0.2'); // This version would not be a minimal update. + + await _reportWithForbidden( + context, + { + 'foo': ['1.1.1', '2.0.0'] + }, + targetPackage: 'foo', + resultAssertions: (r) { + expect(findChangeVersion(r, 'smallestUpdate', 'foo'), '2.0.1'); + }, + ); + }); + + testWithGolden( + 'Smallest possible upgrade can upgrade other packages if needed', + (context) async { + final server = await servePackages(); + server.serve('bar', '1.0.0'); + server.serve('bar', '2.0.0'); + server.serve('bar', '2.2.0'); + + server.serve( + 'foo', + '1.1.1', + deps: {'bar': '^1.0.0'}, + ); // This version will be disallowed. + + await d.appDir(dependencies: {'foo': '^1.0.0', 'bar': '^1.0.0'}).create(); + await pubGet(); + + server.serve( + 'foo', + '2.0.0', + deps: {'bar': '^2.0.0'}, + ); // This will also be disallowed, a minimal update should not find this. + server.serve( + 'foo', + '2.0.1', + deps: {'bar': '^2.0.0'}, + ); // We would like this to be the new version. + server.serve( + 'foo', + '2.0.2', + deps: {'bar': '^2.0.0'}, + ); // This version would not be a minimal update. + + await _reportWithForbidden( + context, + { + 'foo': ['1.1.1', '2.0.0'], + 'bar': ['2.0.0'] + }, + targetPackage: 'foo', + resultAssertions: (r) { + expect(findChangeVersion(r, 'smallestUpdate', 'foo'), '2.0.1'); + expect(findChangeVersion(r, 'smallestUpdate', 'bar'), '2.2.0'); + }, + ); + }); } dynamic findChangeVersion(dynamic json, String updateType, String name) {
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/Adding transitive.txt b/test/testdata/goldens/dependency_services/dependency_services_test/Adding transitive.txt index 9c04c2f..e0533ae 100644 --- a/test/testdata/goldens/dependency_services/dependency_services_test/Adding transitive.txt +++ b/test/testdata/goldens/dependency_services/dependency_services_test/Adding transitive.txt
@@ -19,7 +19,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section list -$ dependency_services list +$ echo '' | dependency_services list { "dependencies": [ { @@ -42,7 +42,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section report -$ dependency_services report +$ echo '' | dependency_services report { "dependencies": [ {
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/Can update a git package.txt b/test/testdata/goldens/dependency_services/dependency_services_test/Can update a git package.txt index c34c46f..da10be4 100644 --- a/test/testdata/goldens/dependency_services/dependency_services_test/Can update a git package.txt +++ b/test/testdata/goldens/dependency_services/dependency_services_test/Can update a git package.txt
@@ -29,7 +29,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section list -$ dependency_services list +$ echo '' | dependency_services list { "dependencies": [ { @@ -68,7 +68,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section report -$ dependency_services report +$ echo '' | dependency_services report { "dependencies": [ {
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/Compatible.txt b/test/testdata/goldens/dependency_services/dependency_services_test/Compatible.txt index 86e82b9..c4e816c 100644 --- a/test/testdata/goldens/dependency_services/dependency_services_test/Compatible.txt +++ b/test/testdata/goldens/dependency_services/dependency_services_test/Compatible.txt
@@ -35,7 +35,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section list -$ dependency_services list +$ echo '' | dependency_services list { "dependencies": [ { @@ -86,7 +86,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section report -$ dependency_services report +$ echo '' | dependency_services report { "dependencies": [ {
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/Finds smallest possible upgrade.txt b/test/testdata/goldens/dependency_services/dependency_services_test/Finds smallest possible upgrade.txt new file mode 100644 index 0000000..7fcfaf4 --- /dev/null +++ b/test/testdata/goldens/dependency_services/dependency_services_test/Finds smallest possible upgrade.txt
@@ -0,0 +1,172 @@ +# GENERATED BY: test/dependency_services/dependency_services_test.dart + +$ cat pubspec.yaml +{"name":"myapp","dependencies":{"foo":"^1.0.0"},"environment":{"sdk":"^3.0.2"}} +$ cat pubspec.lock +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + foo: + dependency: "direct main" + description: + name: foo + sha256: ee9afb23699244da9e40f5001a3600e529c3696f2b8696906fd43ea8e54e0457 + url: "http://localhost:$PORT" + source: hosted + version: "1.1.1" +sdks: + dart: ">=3.0.2 <4.0.0" +-------------------------------- END OF OUTPUT --------------------------------- + +## Section report +$ echo '{"target":"foo","disallowed":[{"name":"foo","url":"http://localhost:$PORT","versions":[{"range":"1.1.1"},{"range":"1.1.2"}]}]}' | dependency_services report +{ + "dependencies": [ + { + "name": "foo", + "version": "1.1.1", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "ee9afb23699244da9e40f5001a3600e529c3696f2b8696906fd43ea8e54e0457" + } + }, + "latest": "1.1.4", + "constraint": "^1.0.0", + "compatible": [ + { + "name": "foo", + "version": "1.1.4", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "4e29148fe4f00dc3e48082c074a538bc10030ed519622ee350815d5b2fd96a6d" + } + }, + "constraintBumped": "^1.0.0", + "constraintWidened": "^1.0.0", + "constraintBumpedIfNeeded": "^1.0.0", + "previousVersion": "1.1.1", + "previousConstraint": "^1.0.0", + "previousSource": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "ee9afb23699244da9e40f5001a3600e529c3696f2b8696906fd43ea8e54e0457" + } + } + } + ], + "singleBreaking": [ + { + "name": "foo", + "version": "1.1.4", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "4e29148fe4f00dc3e48082c074a538bc10030ed519622ee350815d5b2fd96a6d" + } + }, + "constraintBumped": "^1.1.4", + "constraintWidened": "^1.0.0", + "constraintBumpedIfNeeded": "^1.0.0", + "previousVersion": "1.1.1", + "previousConstraint": "^1.0.0", + "previousSource": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "ee9afb23699244da9e40f5001a3600e529c3696f2b8696906fd43ea8e54e0457" + } + } + } + ], + "multiBreaking": [ + { + "name": "foo", + "version": "1.1.4", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "4e29148fe4f00dc3e48082c074a538bc10030ed519622ee350815d5b2fd96a6d" + } + }, + "constraintBumped": "^1.1.4", + "constraintWidened": "^1.0.0", + "constraintBumpedIfNeeded": "^1.0.0", + "previousVersion": "1.1.1", + "previousConstraint": "^1.0.0", + "previousSource": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "ee9afb23699244da9e40f5001a3600e529c3696f2b8696906fd43ea8e54e0457" + } + } + } + ], + "smallestUpdate": [ + { + "name": "foo", + "version": "1.1.3", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "5efda06be6bc23558d40eeab6f92729c53e5018621e7bda9868d97dd613693b9" + } + }, + "constraintBumped": "^1.1.3", + "constraintWidened": "^1.0.0", + "constraintBumpedIfNeeded": "^1.0.0", + "previousVersion": "1.1.1", + "previousConstraint": "^1.0.0", + "previousSource": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "ee9afb23699244da9e40f5001a3600e529c3696f2b8696906fd43ea8e54e0457" + } + } + } + ] + } + ] +} + +-------------------------------- END OF OUTPUT --------------------------------- + +$ cat pubspec.yaml +{"name":"myapp","dependencies":{"foo":"^1.0.0"},"environment":{"sdk":"^3.0.2"}} +$ cat pubspec.lock +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + foo: + dependency: "direct main" + description: + name: foo + sha256: ee9afb23699244da9e40f5001a3600e529c3696f2b8696906fd43ea8e54e0457 + url: "http://localhost:$PORT" + source: hosted + version: "1.1.1" +sdks: + dart: ">=3.0.2 <4.0.0"
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/Ignoring version.txt b/test/testdata/goldens/dependency_services/dependency_services_test/Ignoring version.txt new file mode 100644 index 0000000..1636fda --- /dev/null +++ b/test/testdata/goldens/dependency_services/dependency_services_test/Ignoring version.txt
@@ -0,0 +1,117 @@ +# GENERATED BY: test/dependency_services/dependency_services_test.dart + +$ cat pubspec.yaml +{"name":"app","dependencies":{"foo":"^1.0.0"},"environment":{"sdk":"^3.0.2"}} +$ cat pubspec.lock +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + foo: + dependency: "direct main" + description: + name: foo + sha256: "4a8df8c695623e81d90f663801ead4a5269b406599b43b90ad558561a6f09c59" + url: "http://localhost:$PORT" + source: hosted + version: "1.0.0" +sdks: + dart: ">=3.0.2 <4.0.0" +-------------------------------- END OF OUTPUT --------------------------------- + +## Section report +$ echo '{"target":null,"disallowed":[{"name":"foo","url":"http://localhost:$PORT","versions":[{"range":"1.0.2"}]},{"name":"transitive","url":"http://localhost:$PORT","versions":[{"range":"1.0.0"}]}]}' | dependency_services report +{ + "dependencies": [ + { + "name": "foo", + "version": "1.0.0", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "4a8df8c695623e81d90f663801ead4a5269b406599b43b90ad558561a6f09c59" + } + }, + "latest": "1.0.3", + "constraint": "^1.0.0", + "compatible": [ + { + "name": "foo", + "version": "1.0.1", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "7e5c9a856d9aaa086b9724210ae9017edd53d704d630fe0cae743abc5074a997" + } + }, + "constraintBumped": "^1.0.0", + "constraintWidened": "^1.0.0", + "constraintBumpedIfNeeded": "^1.0.0", + "previousVersion": "1.0.0", + "previousConstraint": "^1.0.0", + "previousSource": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "4a8df8c695623e81d90f663801ead4a5269b406599b43b90ad558561a6f09c59" + } + } + } + ], + "singleBreaking": [], + "multiBreaking": [ + { + "name": "foo", + "version": "1.0.1", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "7e5c9a856d9aaa086b9724210ae9017edd53d704d630fe0cae743abc5074a997" + } + }, + "constraintBumped": "^1.0.1", + "constraintWidened": "^1.0.0", + "constraintBumpedIfNeeded": "^1.0.0", + "previousVersion": "1.0.0", + "previousConstraint": "^1.0.0", + "previousSource": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "4a8df8c695623e81d90f663801ead4a5269b406599b43b90ad558561a6f09c59" + } + } + } + ] + } + ] +} + +-------------------------------- END OF OUTPUT --------------------------------- + +$ cat pubspec.yaml +{"name":"app","dependencies":{"foo":"^1.0.0"},"environment":{"sdk":"^3.0.2"}} +$ cat pubspec.lock +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + foo: + dependency: "direct main" + description: + name: foo + sha256: "4a8df8c695623e81d90f663801ead4a5269b406599b43b90ad558561a6f09c59" + url: "http://localhost:$PORT" + source: hosted + version: "1.0.0" +sdks: + dart: ">=3.0.2 <4.0.0"
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/No pubspec.lock.txt b/test/testdata/goldens/dependency_services/dependency_services_test/No pubspec.lock.txt index 46d1a77..0d46baa 100644 --- a/test/testdata/goldens/dependency_services/dependency_services_test/No pubspec.lock.txt +++ b/test/testdata/goldens/dependency_services/dependency_services_test/No pubspec.lock.txt
@@ -7,7 +7,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section list -$ dependency_services list +$ echo '' | dependency_services list { "dependencies": [ { @@ -59,7 +59,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section report -$ dependency_services report +$ echo '' | dependency_services report { "dependencies": [ {
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/Preserves no content-hashes.txt b/test/testdata/goldens/dependency_services/dependency_services_test/Preserves no content-hashes.txt index a0ffae6..61c69a2 100644 --- a/test/testdata/goldens/dependency_services/dependency_services_test/Preserves no content-hashes.txt +++ b/test/testdata/goldens/dependency_services/dependency_services_test/Preserves no content-hashes.txt
@@ -32,7 +32,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section list -$ dependency_services list +$ echo '' | dependency_services list { "dependencies": [ { @@ -80,7 +80,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section report -$ dependency_services report +$ echo '' | dependency_services report { "dependencies": [ {
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/Preserves pub.dartlang.org as hosted url.txt b/test/testdata/goldens/dependency_services/dependency_services_test/Preserves pub.dartlang.org as hosted url.txt index 5a95570..2483450 100644 --- a/test/testdata/goldens/dependency_services/dependency_services_test/Preserves pub.dartlang.org as hosted url.txt +++ b/test/testdata/goldens/dependency_services/dependency_services_test/Preserves pub.dartlang.org as hosted url.txt
@@ -27,7 +27,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section list -$ dependency_services list +$ echo '' | dependency_services list { "dependencies": [ { @@ -64,7 +64,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section report -$ dependency_services report +$ echo '' | dependency_services report { "dependencies": [ {
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/Relative paths are allowed.txt b/test/testdata/goldens/dependency_services/dependency_services_test/Relative paths are allowed.txt index 6ac254b..7560bdd 100644 --- a/test/testdata/goldens/dependency_services/dependency_services_test/Relative paths are allowed.txt +++ b/test/testdata/goldens/dependency_services/dependency_services_test/Relative paths are allowed.txt
@@ -26,7 +26,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section list -$ dependency_services list +$ echo '' | dependency_services list { "dependencies": [ { @@ -62,7 +62,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section report -$ dependency_services report +$ echo '' | dependency_services report { "dependencies": [ {
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/Removing transitive.txt b/test/testdata/goldens/dependency_services/dependency_services_test/Removing transitive.txt index 097456a..3f3a887 100644 --- a/test/testdata/goldens/dependency_services/dependency_services_test/Removing transitive.txt +++ b/test/testdata/goldens/dependency_services/dependency_services_test/Removing transitive.txt
@@ -27,7 +27,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section list -$ dependency_services list +$ echo '' | dependency_services list { "dependencies": [ { @@ -64,7 +64,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section report -$ dependency_services report +$ echo '' | dependency_services report { "dependencies": [ {
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/Smallest possible upgrade can upgrade beyond breaking.txt b/test/testdata/goldens/dependency_services/dependency_services_test/Smallest possible upgrade can upgrade beyond breaking.txt new file mode 100644 index 0000000..fdf4b1a --- /dev/null +++ b/test/testdata/goldens/dependency_services/dependency_services_test/Smallest possible upgrade can upgrade beyond breaking.txt
@@ -0,0 +1,145 @@ +# GENERATED BY: test/dependency_services/dependency_services_test.dart + +$ cat pubspec.yaml +{"name":"myapp","dependencies":{"foo":"^1.0.0"},"environment":{"sdk":"^3.0.2"}} +$ cat pubspec.lock +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + foo: + dependency: "direct main" + description: + name: foo + sha256: ee9afb23699244da9e40f5001a3600e529c3696f2b8696906fd43ea8e54e0457 + url: "http://localhost:$PORT" + source: hosted + version: "1.1.1" +sdks: + dart: ">=3.0.2 <4.0.0" +-------------------------------- END OF OUTPUT --------------------------------- + +## Section report +$ echo '{"target":"foo","disallowed":[{"name":"foo","url":"http://localhost:$PORT","versions":[{"range":"1.1.1"},{"range":"2.0.0"}]}]}' | dependency_services report +{ + "dependencies": [ + { + "name": "foo", + "version": "1.1.1", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "ee9afb23699244da9e40f5001a3600e529c3696f2b8696906fd43ea8e54e0457" + } + }, + "latest": "2.0.2", + "constraint": "^1.0.0", + "compatible": [], + "singleBreaking": [ + { + "name": "foo", + "version": "2.0.2", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "3db0dc36e17a64a3f2ba5c2aa5871c056621886c9cc8464a0123642d3d68d272" + } + }, + "constraintBumped": "^2.0.2", + "constraintWidened": ">=1.0.0 <3.0.0", + "constraintBumpedIfNeeded": "^2.0.2", + "previousVersion": "1.1.1", + "previousConstraint": "^1.0.0", + "previousSource": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "ee9afb23699244da9e40f5001a3600e529c3696f2b8696906fd43ea8e54e0457" + } + } + } + ], + "multiBreaking": [ + { + "name": "foo", + "version": "2.0.2", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "3db0dc36e17a64a3f2ba5c2aa5871c056621886c9cc8464a0123642d3d68d272" + } + }, + "constraintBumped": "^2.0.2", + "constraintWidened": ">=1.0.0 <3.0.0", + "constraintBumpedIfNeeded": "^2.0.2", + "previousVersion": "1.1.1", + "previousConstraint": "^1.0.0", + "previousSource": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "ee9afb23699244da9e40f5001a3600e529c3696f2b8696906fd43ea8e54e0457" + } + } + } + ], + "smallestUpdate": [ + { + "name": "foo", + "version": "2.0.1", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "bcaaad4d1c92a89ff8dfb1ce146e11635fbce6219dc8992d5a5d8eaca0658fdd" + } + }, + "constraintBumped": "^2.0.1", + "constraintWidened": ">=1.0.0 <3.0.0", + "constraintBumpedIfNeeded": "^2.0.1", + "previousVersion": "1.1.1", + "previousConstraint": "^1.0.0", + "previousSource": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "ee9afb23699244da9e40f5001a3600e529c3696f2b8696906fd43ea8e54e0457" + } + } + } + ] + } + ] +} + +-------------------------------- END OF OUTPUT --------------------------------- + +$ cat pubspec.yaml +{"name":"myapp","dependencies":{"foo":"^1.0.0"},"environment":{"sdk":"^3.0.2"}} +$ cat pubspec.lock +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + foo: + dependency: "direct main" + description: + name: foo + sha256: ee9afb23699244da9e40f5001a3600e529c3696f2b8696906fd43ea8e54e0457 + url: "http://localhost:$PORT" + source: hosted + version: "1.1.1" +sdks: + dart: ">=3.0.2 <4.0.0"
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/Smallest possible upgrade can upgrade other packages if needed.txt b/test/testdata/goldens/dependency_services/dependency_services_test/Smallest possible upgrade can upgrade other packages if needed.txt new file mode 100644 index 0000000..745d857 --- /dev/null +++ b/test/testdata/goldens/dependency_services/dependency_services_test/Smallest possible upgrade can upgrade other packages if needed.txt
@@ -0,0 +1,186 @@ +# GENERATED BY: test/dependency_services/dependency_services_test.dart + +$ cat pubspec.yaml +{"name":"myapp","dependencies":{"foo":"^1.0.0","bar":"^1.0.0"},"environment":{"sdk":"^3.0.2"}} +$ cat pubspec.lock +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + bar: + dependency: "direct main" + description: + name: bar + sha256: "8cd4b5a00de63aa592f4240249affd87abf49de4281233870f22b30919f87d42" + url: "http://localhost:$PORT" + source: hosted + version: "1.0.0" + foo: + dependency: "direct main" + description: + name: foo + sha256: "6f20056a28b780546e364af618f5c357863660259f8df4431c7fd12ccb8b4372" + url: "http://localhost:$PORT" + source: hosted + version: "1.1.1" +sdks: + dart: ">=3.0.2 <4.0.0" +-------------------------------- END OF OUTPUT --------------------------------- + +## Section report +$ echo '{"target":"foo","disallowed":[{"name":"foo","url":"http://localhost:$PORT","versions":[{"range":"1.1.1"},{"range":"2.0.0"}]},{"name":"bar","url":"http://localhost:$PORT","versions":[{"range":"2.0.0"}]}]}' | dependency_services report +{ + "dependencies": [ + { + "name": "foo", + "version": "1.1.1", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "6f20056a28b780546e364af618f5c357863660259f8df4431c7fd12ccb8b4372" + } + }, + "latest": "2.0.2", + "constraint": "^1.0.0", + "compatible": [], + "singleBreaking": [], + "multiBreaking": [ + { + "name": "foo", + "version": "2.0.2", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "2fe8aa833d5a1c720c47cde975d63ff8cd1436da5a13b9ff413eb2b489bdf850" + } + }, + "constraintBumped": "^2.0.2", + "constraintWidened": ">=1.0.0 <3.0.0", + "constraintBumpedIfNeeded": "^2.0.2", + "previousVersion": "1.1.1", + "previousConstraint": "^1.0.0", + "previousSource": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "6f20056a28b780546e364af618f5c357863660259f8df4431c7fd12ccb8b4372" + } + } + }, + { + "name": "bar", + "version": "2.2.0", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "bar", + "url": "http://localhost:$PORT", + "sha256": "3c51e3f45f9b993f18c64ab5c55e5f8149c98bac06b0c3e9100fef98385afe4f" + } + }, + "constraintBumped": "^2.2.0", + "constraintWidened": ">=1.0.0 <3.0.0", + "constraintBumpedIfNeeded": "^2.2.0", + "previousVersion": "1.0.0", + "previousConstraint": "^1.0.0", + "previousSource": { + "type": "hosted", + "description": { + "name": "bar", + "url": "http://localhost:$PORT", + "sha256": "8cd4b5a00de63aa592f4240249affd87abf49de4281233870f22b30919f87d42" + } + } + } + ], + "smallestUpdate": [ + { + "name": "foo", + "version": "2.0.1", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "3fc65bb8d960eb60a8ff969ba138df09de120b4e05dbde7a45b7f77aba179237" + } + }, + "constraintBumped": "^2.0.1", + "constraintWidened": ">=1.0.0 <3.0.0", + "constraintBumpedIfNeeded": "^2.0.1", + "previousVersion": "1.1.1", + "previousConstraint": "^1.0.0", + "previousSource": { + "type": "hosted", + "description": { + "name": "foo", + "url": "http://localhost:$PORT", + "sha256": "6f20056a28b780546e364af618f5c357863660259f8df4431c7fd12ccb8b4372" + } + } + }, + { + "name": "bar", + "version": "2.2.0", + "kind": "direct", + "source": { + "type": "hosted", + "description": { + "name": "bar", + "url": "http://localhost:$PORT", + "sha256": "3c51e3f45f9b993f18c64ab5c55e5f8149c98bac06b0c3e9100fef98385afe4f" + } + }, + "constraintBumped": "^2.2.0", + "constraintWidened": ">=1.0.0 <3.0.0", + "constraintBumpedIfNeeded": "^2.2.0", + "previousVersion": "1.0.0", + "previousConstraint": "^1.0.0", + "previousSource": { + "type": "hosted", + "description": { + "name": "bar", + "url": "http://localhost:$PORT", + "sha256": "8cd4b5a00de63aa592f4240249affd87abf49de4281233870f22b30919f87d42" + } + } + } + ] + } + ] +} + +-------------------------------- END OF OUTPUT --------------------------------- + +$ cat pubspec.yaml +{"name":"myapp","dependencies":{"foo":"^1.0.0","bar":"^1.0.0"},"environment":{"sdk":"^3.0.2"}} +$ cat pubspec.lock +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + bar: + dependency: "direct main" + description: + name: bar + sha256: "8cd4b5a00de63aa592f4240249affd87abf49de4281233870f22b30919f87d42" + url: "http://localhost:$PORT" + source: hosted + version: "1.0.0" + foo: + dependency: "direct main" + description: + name: foo + sha256: "6f20056a28b780546e364af618f5c357863660259f8df4431c7fd12ccb8b4372" + url: "http://localhost:$PORT" + source: hosted + version: "1.1.1" +sdks: + dart: ">=3.0.2 <4.0.0"
diff --git a/test/testdata/goldens/dependency_services/dependency_services_test/multibreaking.txt b/test/testdata/goldens/dependency_services/dependency_services_test/multibreaking.txt index a65da00..1e63cb2 100644 --- a/test/testdata/goldens/dependency_services/dependency_services_test/multibreaking.txt +++ b/test/testdata/goldens/dependency_services/dependency_services_test/multibreaking.txt
@@ -35,7 +35,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section list -$ dependency_services list +$ echo '' | dependency_services list { "dependencies": [ { @@ -86,7 +86,7 @@ -------------------------------- END OF OUTPUT --------------------------------- ## Section report -$ dependency_services report +$ echo '' | dependency_services report { "dependencies": [ {