diff --git a/.github/dependabot.yml b/.github/dependabot.yml index fdb170b..2199393 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml
@@ -4,7 +4,7 @@ - package-ecosystem: "pub" directory: "/" schedule: - interval: "monthly" + interval: "weekly" - package-ecosystem: github-actions directory: /
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 7f30b9a..f45c8e4 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml
@@ -24,7 +24,7 @@ matrix: sdk: [dev] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }} @@ -52,7 +52,7 @@ sdk: [dev] shard: [0, 1, 2, 3, 4, 5, 6] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }}
diff --git a/lib/src/command/upgrade.dart b/lib/src/command/upgrade.dart index 0f2abec..edd7eb6 100644 --- a/lib/src/command/upgrade.dart +++ b/lib/src/command/upgrade.dart
@@ -71,6 +71,13 @@ ); argParser.addFlag( + 'unlock-transitive', + help: 'Also upgrades the transitive dependencies ' + 'of the listed [dependencies]', + negatable: false, + ); + + argParser.addFlag( 'major-versions', help: 'Upgrades packages to their latest resolvable versions, ' 'and updates pubspec.yaml.', @@ -101,11 +108,27 @@ bool get _precompile => argResults.flag('precompile'); + late final Future<List<String>> _packagesToUpgrade = + _computePackagesToUpgrade(); + /// List of package names to upgrade, if empty then upgrade all packages. /// /// This allows the user to specify list of names that they want the /// upgrade command to affect. - List<String> get _packagesToUpgrade => argResults.rest; + Future<List<String>> _computePackagesToUpgrade() async { + if (argResults.flag('unlock-transitive')) { + final graph = await entrypoint.packageGraph; + return argResults.rest + .expand( + (package) => + graph.transitiveDependencies(package).map((p) => p.name), + ) + .toSet() + .toList(); + } else { + return argResults.rest; + } + } bool get _upgradeNullSafety => argResults.flag('nullsafety') || argResults.flag('null-safety'); @@ -142,7 +165,7 @@ ); } final changes = - entrypoint.tighten(packagesToUpgrade: _packagesToUpgrade); + entrypoint.tighten(packagesToUpgrade: await _packagesToUpgrade); entrypoint.applyChanges(changes, _dryRun); } } @@ -157,7 +180,7 @@ Future<void> _runUpgrade(Entrypoint e, {bool onlySummary = false}) async { await e.acquireDependencies( SolveType.upgrade, - unlock: _packagesToUpgrade, + unlock: await _packagesToUpgrade, dryRun: _dryRun, precompile: _precompile, summaryOnly: onlySummary, @@ -171,7 +194,7 @@ /// given. /// /// This assumes that `--major-versions` was passed. - List<String> _directDependenciesToUpgrade() { + Future<List<String>> _directDependenciesToUpgrade() async { assert(_upgradeMajorVersions); final directDeps = { @@ -180,12 +203,13 @@ ...package.devDependencies.keys, ], }.toList(); + final packagesToUpgrade = await _packagesToUpgrade; final toUpgrade = - _packagesToUpgrade.isEmpty ? directDeps : _packagesToUpgrade; + packagesToUpgrade.isEmpty ? directDeps : packagesToUpgrade; // Check that all package names in upgradeOnly are direct-dependencies final notInDeps = toUpgrade.where((n) => !directDeps.contains(n)); - if (toUpgrade.any(notInDeps.contains)) { + if (argResults.rest.any(notInDeps.contains)) { usageException(''' Dependencies specified in `$topLevelProgram pub upgrade --major-versions <dependencies>` must be direct 'dependencies' or 'dev_dependencies', following packages are not: @@ -198,7 +222,7 @@ } Future<void> _runUpgradeMajorVersions() async { - final toUpgrade = _directDependenciesToUpgrade(); + final toUpgrade = await _directDependenciesToUpgrade(); // Solve [resolvablePubspec] in-memory and consolidate the resolved // versions of the packages into a map for quick searching. final resolvedPackages = <String, PackageId>{}; @@ -267,7 +291,7 @@ }), ); changes = entrypoint.tighten( - packagesToUpgrade: _packagesToUpgrade, + packagesToUpgrade: await _packagesToUpgrade, existingChanges: changes, packageVersions: solveResult.packages, ); @@ -279,7 +303,7 @@ // But without a specific package we want to get as many non-major updates // as possible (SolveType.upgrade). final solveType = - _packagesToUpgrade.isEmpty ? SolveType.upgrade : SolveType.get; + (await _packagesToUpgrade).isEmpty ? SolveType.upgrade : SolveType.get; entrypoint.applyChanges(changes, _dryRun); await entrypoint.withUpdatedRootPubspecs({ @@ -290,6 +314,7 @@ solveType, dryRun: _dryRun, precompile: !_dryRun && _precompile, + unlock: await _packagesToUpgrade, ); // If any of the packages to upgrade are dependency overrides, then we
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart index f57aeb9..693c9a2 100644 --- a/lib/src/entrypoint.dart +++ b/lib/src/entrypoint.dart
@@ -515,7 +515,7 @@ /// pubspec.yaml and all dependencies downloaded. Future<void> acquireDependencies( SolveType type, { - Iterable<String>? unlock, + Iterable<String> unlock = const [], bool dryRun = false, bool precompile = false, bool summaryOnly = false, @@ -547,7 +547,7 @@ cache, workspaceRoot, lockFile: lockFile, - unlock: unlock ?? [], + unlock: unlock, ); }); } on SolveFailure catch (e) { @@ -557,7 +557,7 @@ this, type, e.incompatibility, - unlock ?? [], + unlock, cache, ), );
diff --git a/lib/src/pubspec.dart b/lib/src/pubspec.dart index fcd9259..1542768 100644 --- a/lib/src/pubspec.dart +++ b/lib/src/pubspec.dart
@@ -72,6 +72,12 @@ '`workspace` and `resolution` requires at least language version ' '${LanguageVersion.firstVersionWithWorkspaces}', r.span, + hint: ''' +Consider updating the SDK constraint to: + +environment: + sdk: '^${sdk.version}' +''', ); } if (r == null || r.value == null) return <String>[]; @@ -103,6 +109,12 @@ '`workspace` and `resolution` requires at least language version ' '${LanguageVersion.firstVersionWithWorkspaces}', r.span, + hint: ''' +Consider updating the SDK constraint to: + +environment: + sdk: '^${sdk.version}' +''', ); } return switch (r?.value) { @@ -720,8 +732,8 @@ } /// Throws a [SourceSpanApplicationException] with the given message. -Never _error(String message, SourceSpan? span) { - throw SourceSpanApplicationException(message, span); +Never _error(String message, SourceSpan? span, {String? hint}) { + throw SourceSpanApplicationException(message, span, hint: hint); } enum _FileType {
diff --git a/pubspec.lock b/pubspec.lock index 5051dfb..e20fe4d 100644 --- a/pubspec.lock +++ b/pubspec.lock
@@ -5,23 +5,23 @@ dependency: transitive description: name: _fe_analyzer_shared - sha256: "45cfa8471b89fb6643fe9bf51bd7931a76b8f5ec2d65de4fb176dba8d4f22c77" + sha256: f6dbf021f4b214d85c79822912c5fcd142a2c4869f01222ad371bc51f9f1c356 url: "https://pub.dev" source: hosted - version: "73.0.0" + version: "74.0.0" _macros: dependency: transitive description: dart source: sdk - version: "0.3.2" + version: "0.3.3" analyzer: dependency: "direct main" description: name: analyzer - sha256: "4959fec185fe70cce007c57e9ab6983101dbe593d2bf8bbfb4453aaec0cf470a" + sha256: f7e8caf82f2d3190881d81012606effdf8a38e6c1ab9e30947149733065f817c url: "https://pub.dev" source: hosted - version: "6.8.0" + version: "6.9.0" args: dependency: "direct main" description: @@ -194,10 +194,10 @@ dependency: transitive description: name: macros - sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" url: "https://pub.dev" source: hosted - version: "0.1.2-main.4" + version: "0.1.3-main.0" matcher: dependency: transitive description:
diff --git a/pubspec.yaml b/pubspec.yaml index 423b53d..5ff42ae 100644 --- a/pubspec.yaml +++ b/pubspec.yaml
@@ -26,7 +26,7 @@ tar: ^2.0.0 typed_data: ^1.3.2 yaml: ^3.1.2 - yaml_edit: ^2.1.1 + yaml_edit: ^2.2.1 dev_dependencies: checks: ^0.3.0
diff --git a/test/pubspec_test.dart b/test/pubspec_test.dart index 5db5bf3..8dbff18 100644 --- a/test/pubspec_test.dart +++ b/test/pubspec_test.dart
@@ -24,6 +24,7 @@ String contents, void Function(Pubspec) fn, { String? expectedContains, + String? hintContains, Description? containingDescription, }) { var expectation = const TypeMatcher<SourceSpanApplicationException>(); @@ -34,6 +35,13 @@ contains(expectedContains), ); } + if (hintContains != null) { + expectation = expectation.having( + (error) => error.hint, + 'hint', + contains(hintContains), + ); + } final pubspec = Pubspec.parse( contents, @@ -384,6 +392,14 @@ workspace: ['a', 'b', 'c'] ''', (p) => p.workspace, + expectedContains: '`workspace` and `resolution` ' + 'requires at least language version 3.5', + hintContains: ''' +Consider updating the SDK constraint to: + +environment: + sdk: '^${Platform.version.split(' ').first}' +''', ); // but no error if you don't look at it. expect( @@ -406,9 +422,17 @@ ''' environment: sdk: ^1.2.3 -resolution: local +resolution: workspace ''', (p) => p.resolution, + expectedContains: '`workspace` and `resolution` ' + 'requires at least language version 3.5', + hintContains: ''' +Consider updating the SDK constraint to: + +environment: + sdk: '^${Platform.version.split(' ').first}' +''', ); });
diff --git a/test/testdata/goldens/help_test/pub upgrade --help.txt b/test/testdata/goldens/help_test/pub upgrade --help.txt index 2721d13..ade5a8d 100644 --- a/test/testdata/goldens/help_test/pub upgrade --help.txt +++ b/test/testdata/goldens/help_test/pub upgrade --help.txt
@@ -5,13 +5,14 @@ Upgrade the current package's dependencies to latest versions. Usage: pub upgrade [dependencies...] --h, --help Print this usage information. - --[no-]offline Use cached packages instead of accessing the network. --n, --dry-run Report what dependencies would change but don't change any. - --[no-]precompile Precompile executables in immediate dependencies. - --tighten Updates lower bounds in pubspec.yaml to match the resolved version. - --major-versions Upgrades packages to their latest resolvable versions, and updates pubspec.yaml. --C, --directory=<dir> Run this in the directory <dir>. +-h, --help Print this usage information. + --[no-]offline Use cached packages instead of accessing the network. +-n, --dry-run Report what dependencies would change but don't change any. + --[no-]precompile Precompile executables in immediate dependencies. + --tighten Updates lower bounds in pubspec.yaml to match the resolved version. + --unlock-transitive Also upgrades the transitive dependencies of the listed [dependencies] + --major-versions Upgrades packages to their latest resolvable versions, and updates pubspec.yaml. +-C, --directory=<dir> Run this in the directory <dir>. Run "pub help" to see global options. See https://dart.dev/tools/pub/cmd/pub-upgrade for detailed documentation.
diff --git a/test/upgrade/upgrade_transitive_test.dart b/test/upgrade/upgrade_transitive_test.dart new file mode 100644 index 0000000..864972b --- /dev/null +++ b/test/upgrade/upgrade_transitive_test.dart
@@ -0,0 +1,126 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// 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:test/test.dart'; + +import '../descriptor.dart' as d; +import '../test_pub.dart'; + +void main() { + test('without --unlock-transitive, the transitive dependencies stay locked', + () async { + final server = await servePackages(); + server.serve('foo', '1.0.0', deps: {'bar': '^1.0.0'}); + server.serve('bar', '1.0.0'); + + await d.appDir( + dependencies: { + 'foo': '^1.0.0', + }, + ).create(); + + await pubGet(output: contains('+ foo 1.0.0')); + + server.serve('foo', '1.5.0', deps: {'bar': '^1.0.0'}); + server.serve('bar', '1.5.0'); + + await pubUpgrade( + args: ['foo'], + output: allOf( + contains('> foo 1.5.0'), + isNot(contains('> bar')), + ), + ); + }); + + test('`--unlock-transitive` dependencies gets unlocked', () async { + final server = await servePackages(); + server.serve('foo', '1.0.0', deps: {'bar': '^1.0.0'}); + server.serve('bar', '1.0.0'); + server.serve('baz', '1.0.0'); + + await d.appDir( + dependencies: { + 'foo': '^1.0.0', + 'baz': '^1.0.0', + }, + ).create(); + + await pubGet(output: contains('+ foo 1.0.0')); + + server.serve('foo', '1.5.0', deps: {'bar': '^1.0.0'}); + server.serve('bar', '1.5.0'); + server.serve('baz', '1.5.0'); + + await pubUpgrade( + args: ['--unlock-transitive', 'foo'], + output: allOf( + contains('> foo 1.5.0'), + contains('> bar 1.5.0'), + isNot( + contains('baz 1.5.0'), + ), // Baz is not a transitive dependency of bar + ), + ); + }); + + test( + '`--major-versions` without `--unlock-transitive` does not allow ' + 'transitive dependencies to be upgraded along with the named packages', + () async { + final server = await servePackages(); + server.serve('foo', '1.0.0', deps: {'bar': '^1.0.0'}); + server.serve('bar', '1.0.0'); + + await d.appDir( + dependencies: { + 'foo': '^1.0.0', + }, + ).create(); + + await pubGet(output: contains('+ foo 1.0.0')); + + server.serve('foo', '2.0.0', deps: {'bar': '^1.0.0'}); + server.serve('bar', '1.5.0'); + + await pubUpgrade( + args: ['--major-versions', 'foo'], + output: allOf( + contains('> foo 2.0.0'), + isNot(contains('bar 1.5.0')), + ), + ); + }); + + test( + '`--unlock-transitive --major-versions` allows transitive dependencies ' + 'be upgraded along with the named packages', () async { + final server = await servePackages(); + server.serve('foo', '1.0.0', deps: {'bar': '^1.0.0'}); + server.serve('bar', '1.0.0'); + server.serve('baz', '1.0.0'); + + await d.appDir( + dependencies: { + 'foo': '^1.0.0', + 'baz': '^1.0.0', + }, + ).create(); + + await pubGet(output: contains('+ foo 1.0.0')); + + server.serve('foo', '2.0.0', deps: {'bar': '^1.0.0'}); + server.serve('bar', '1.5.0'); + server.serve('baz', '1.5.0'); + + await pubUpgrade( + args: ['--major-versions', '--unlock-transitive', 'foo'], + output: allOf( + contains('> foo 2.0.0'), + contains('> bar 1.5.0'), + isNot(contains('> baz 1.5.0')), + ), + ); + }); +}