blob: b6d7c75af0dae74dce57f9631995f4ff024a4c3f [file] [log] [blame]
// Copyright (c) 2022, 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:io';
import 'dart:typed_data';
import 'package:cli_util/cli_logging.dart';
import 'package:http/http.dart';
import 'package:http/retry.dart';
import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart' as yaml;
import 'cache.dart';
import 'pub.dart';
import 'utils.dart';
class PackageManager {
final Cache cache = Cache();
late final Client _client;
PackageManager() {
_client = RetryClient(
Client(),
when: (response) => const [502, 503].contains(response.statusCode),
);
}
// "archive_url":"https://pub.dartlang.org/packages/usage/versions/4.0.2.tar.gz",
Future<bool> retrievePackageArchive(
PackageInfo package, {
Logger? logger,
}) async {
var archiveFile = getArchiveFileForPackage(package);
if (archiveFile.existsSync()) {
return true;
}
var progress =
logger?.progress('downloading ${path.basename(archiveFile.path)}');
try {
var data = await _getPackageTarGzArchive(package);
archiveFile.writeAsBytesSync(data);
return true;
} finally {
progress?.finish(showTiming: true);
}
// This is live as _getPackageTarGzArchive() can throw.
// ignore: dead_code
return false;
}
File getArchiveFileForPackage(PackageInfo package) {
var name = '${package.name}-${package.version}.tar.gz';
return File(path.join(cache.archivesDir.path, name));
}
Directory getDirectoryForPackage(PackageInfo package) {
var name = '${package.name}-${package.version}';
return Directory(path.join(cache.packagesDir.path, name));
}
Future<LocalPackage> rehydratePackage(PackageInfo package) async {
var archiveFile = getArchiveFileForPackage(package);
var localPackage = LocalPackage(package, getDirectoryForPackage(package));
if (localPackage.directory.existsSync()) {
return localPackage;
}
localPackage.directory.createSync(recursive: true);
var result = await runProcess(
'tar',
[
'-xf',
'../../archives/${path.basename(archiveFile.path)}',
],
workingDirectory: localPackage.directory.path,
);
if (result.exitCode != 0) {
print(result.stdout);
print(result.stderr);
}
return localPackage;
}
Future<Uint8List> _getPackageTarGzArchive(PackageInfo packageInfo) async {
var response = await _client.get(Uri.parse(packageInfo.archiveUrl));
if (response.statusCode == 404) {
return Future.error(response.reasonPhrase!);
}
return response.bodyBytes;
}
void close() {
_client.close();
}
}
class LocalPackage {
final PackageInfo packageInfo;
final Directory directory;
LocalPackage(this.packageInfo, this.directory);
Future<bool> pubGet({
bool checkUpToDate = false,
Logger? logger,
}) async {
if (checkUpToDate) {
var pubspec = File(path.join(directory.path, 'pubspec.yaml'));
var lock = File(path.join(directory.path, 'pubspec.lock'));
if (pubspec.existsSync() && lock.existsSync()) {
if (!lock.lastModifiedSync().isBefore(pubspec.lastModifiedSync())) {
return true;
}
}
}
var executable = Platform.resolvedExecutable;
// Use 'flutter pub get' for flutter packages; it seems to do a better job
// than 'dart pub get' for some reason.
if (flutterPackage) {
var flutterExecutable = path.join(path.dirname(executable), 'flutter');
if (File(flutterExecutable).existsSync()) {
executable = flutterExecutable;
}
}
final progress = logger?.progress('${path.basename(executable)} pub get');
var result = await runProcess(
executable,
[
'pub',
'get',
],
workingDirectory: directory.path,
logger: logger,
);
progress?.finish(showTiming: true);
return result.exitCode == 0;
}
bool get flutterPackage {
// Look for:
// environment:
// flutter: ">=2.5.0"
var pubspecFile = File(path.join(directory.path, 'pubspec.yaml'));
if (!pubspecFile.existsSync()) {
return false;
}
var pubspec = yaml.loadYaml(pubspecFile.readAsStringSync()) as Map;
if (pubspec.containsKey('environment')) {
var env = pubspec['environment'] as Map;
return env.containsKey('flutter');
}
return false;
}
}