blob: 2d72c101fc87f3899b1fd041a1060f4958db0768 [file] [log] [blame] [edit]
// Copyright (c) 2014, 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 'dart:async';
import 'package:pub_semver/pub_semver.dart';
import '../command.dart';
import '../exit_codes.dart' as exit_codes;
import '../io.dart';
import '../log.dart' as log;
import '../package_name.dart';
import '../source/git.dart';
import '../source/hosted.dart';
import '../utils.dart';
/// Handles the `cache repair` pub command.
class CacheRepairCommand extends PubCommand {
@override
String get name => 'repair';
@override
String get description => 'Reinstall cached packages.';
@override
String get docUrl => 'https://dart.dev/tools/pub/cmd/pub-cache';
@override
bool get takesArguments => false;
CacheRepairCommand() {
argParser.addFlag(
'all',
help:
'Repair all cached packages instead of only packages in the '
'current pubspec.lock.',
negatable: false,
);
}
@override
Future<void> runProtected() async {
final repairAll = argResults.flag('all');
// Get the filters for packages to repair (from lockfile if not --all).
bool Function(String, Version)? hostedPackageFilter;
bool Function(String, Version)? gitPackageFilter;
if (!repairAll) {
if (!entrypoint.canFindWorkspaceRoot) {
log.message(
'No pubspec.yaml found. '
'Run from a Dart project or use --all to repair all cached packages.',
);
return;
}
if (!fileExists(entrypoint.lockFilePath)) {
log.message(
'No pubspec.lock found. '
'Run "pub get" first or use --all to repair all cached packages.',
);
return;
}
final lockFile = entrypoint.lockFile;
final packages = lockFile.packages.values.toList();
if (packages.isEmpty) {
log.message('No packages found in pubspec.lock.');
return;
}
(hostedPackageFilter, gitPackageFilter) = _buildPackageFilters(packages);
}
// Delete any eventual temp-files left in the cache.
cache.deleteTempDir();
// Repair every cached source.
final repairResults = [
...await cache.hosted.repairCachedPackages(
cache,
packageFilter: hostedPackageFilter,
),
...await cache.git.repairCachedPackages(
cache,
packageFilter: gitPackageFilter,
),
];
final successes = repairResults.where((result) => result.success);
final failures = repairResults.where((result) => !result.success);
if (successes.isNotEmpty) {
final packages = pluralize('package', successes.length);
log.message(
'Reinstalled ${log.green(successes.length.toString())} $packages.',
);
}
if (failures.isNotEmpty) {
final packages = pluralize('package', failures.length);
final buffer = StringBuffer(
'Failed to reinstall '
'${log.red(failures.length.toString())} $packages:\n',
);
for (var failure in failures) {
buffer.write('- ${log.bold(failure.packageName)} ${failure.version}');
if (failure.source != cache.defaultSource) {
buffer.write(' from ${failure.source}');
}
buffer.writeln();
}
log.message(buffer.toString());
}
final (repairSuccesses, repairFailures) =
await globals.repairActivatedPackages();
if (repairSuccesses.isNotEmpty) {
final packages = pluralize('package', repairSuccesses.length);
log.message(
'Reactivated '
'${log.green(repairSuccesses.length.toString())} $packages.',
);
}
if (repairFailures.isNotEmpty) {
final packages = pluralize('package', repairFailures.length);
log.message(
'Failed to reactivate '
'${log.red(repairFailures.length.toString())} $packages:',
);
log.message(
repairFailures.map((name) => '- ${log.bold(name)}').join('\n'),
);
}
if (successes.isEmpty && failures.isEmpty) {
if (repairAll) {
log.message('No packages in cache, so nothing to repair.');
} else {
log.message('No packages from pubspec.lock found in cache.');
}
}
if (failures.isNotEmpty || repairFailures.isNotEmpty) {
overrideExitCode(exit_codes.UNAVAILABLE);
}
}
/// Builds source-specific package filters from the lockfile packages.
///
/// Returns a tuple of (hostedFilter, gitFilter).
/// - Hosted filter: matches by name AND version.
/// - Git filter: matches by name only (version isn't reliably derivable from
/// cache directory names).
(bool Function(String, Version), bool Function(String, Version))
_buildPackageFilters(List<PackageId> packages) {
final hostedPackages =
packages.where((p) => p.source is HostedSource).toList();
final gitPackages = packages.where((p) => p.source is GitSource).toList();
return (
(name, version) =>
hostedPackages.any((p) => p.name == name && p.version == version),
(name, version) => gitPackages.any((p) => p.name == name),
);
}
}