Download packages from the archive link instead of hardcoded uri (#2366)
diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart
index de32b21..146f0f8 100644
--- a/lib/src/source/hosted.dart
+++ b/lib/src/source/hosted.dart
@@ -151,6 +151,14 @@
}
}
+/// Information about a package version retrieved from /api/packages/$package
+class _VersionInfo {
+ final Pubspec pubspec;
+ final Uri archiveUrl;
+
+ _VersionInfo(this.pubspec, this.archiveUrl);
+}
+
/// The [BoundSource] for [HostedSource].
class BoundHostedSource extends CachedSource {
@override
@@ -158,14 +166,14 @@
@override
final SystemCache systemCache;
- RateLimitedScheduler<PackageRef, Map<PackageId, Pubspec>> _scheduler;
+ RateLimitedScheduler<PackageRef, Map<PackageId, _VersionInfo>> _scheduler;
BoundHostedSource(this.source, this.systemCache) {
_scheduler =
RateLimitedScheduler(_fetchVersions, maxConcurrentOperations: 10);
}
- Future<Map<PackageId, Pubspec>> _fetchVersions(PackageRef ref) async {
+ Future<Map<PackageId, _VersionInfo>> _fetchVersions(PackageRef ref) async {
var url = _makeUrl(
ref.description, (server, package) => '$server/api/packages/$package');
log.io('Get versions from $url.');
@@ -186,7 +194,10 @@
expectedName: ref.name, location: url);
var id = source.idFor(ref.name, pubspec.version,
url: _serverFor(ref.description));
- return MapEntry(id, pubspec);
+ final archiveUrlValue = map['archive_url'];
+ final archiveUrl =
+ archiveUrlValue is String ? Uri.tryParse(archiveUrlValue) : null;
+ return MapEntry(id, _VersionInfo(pubspec, archiveUrl));
}));
// Prefetch the dependencies of the latest version, we are likely to need
@@ -200,7 +211,8 @@
final latestVersionId =
PackageId(ref.name, source, latestVersion, ref.description);
- final dependencies = result[latestVersionId]?.dependencies?.values ?? [];
+ final dependencies =
+ result[latestVersionId]?.pubspec?.dependencies?.values ?? [];
unawaited(withDependencyType(DependencyType.none, () async {
for (final packageRange in dependencies) {
if (packageRange.source is HostedSource) {
@@ -239,7 +251,7 @@
final versions = await _scheduler.schedule(id.toRef());
final url = _makeUrl(
id.description, (server, package) => '$server/api/packages/$package');
- return versions[id] ??
+ return versions[id]?.pubspec ??
(throw PackageNotFoundException('Could not find package $id at $url'));
}
@@ -249,8 +261,7 @@
if (!isInSystemCache(id)) {
var packageDir = getDirectory(id);
ensureDir(p.dirname(packageDir));
- var parsed = source._parseDescription(id.description);
- await _download(parsed.last, parsed.first, id.version, packageDir);
+ await _download(id, packageDir);
}
return Package.load(id.name, getDirectory(id), systemCache.sources);
@@ -295,9 +306,8 @@
for (var package in packages) {
var id = source.idFor(package.name, package.version, url: url);
-
try {
- await _download(url, package.name, package.version, package.dir);
+ await _download(id, package.dir);
successes.add(id);
} catch (error, stackTrace) {
failures.add(id);
@@ -352,13 +362,31 @@
.toList();
}
- /// Downloads package [package] at [version] from [server], and unpacks it
- /// into [destPath].
- Future _download(
- String server, String package, Version version, String destPath) async {
- var url = Uri.parse('$server/packages/$package/versions/$version.tar.gz');
+ /// Downloads package [package] at [version] from the archive_url and unpacks
+ /// it into [destPath].
+ ///
+ /// If there is no archive_url, try to fetch it from
+ /// `$server/packages/$package/versions/$version.tar.gz` where server comes
+ /// from `id.description`.
+ Future _download(PackageId id, String destPath) async {
+ final versions = await _scheduler.schedule(id.toRef());
+ final versionInfo = versions[id];
+ final packageName = id.name;
+ final version = id.version;
+ if (versionInfo == null) {
+ throw PackageNotFoundException(
+ 'Package $packageName has no version $version');
+ }
+ var url = versionInfo.archiveUrl;
+ if (url == null) {
+ // To support old servers that has no archive_url we fall back to the
+ // hard-coded path.
+ final parsedDescription = source._parseDescription(id.description);
+ final server = parsedDescription.last;
+ url = Uri.parse('$server/packages/$packageName/versions/$version.tar.gz');
+ }
log.io('Get package from $url.');
- log.message('Downloading ${log.bold(package)} $version...');
+ log.message('Downloading ${log.bold(id.name)} ${id.version}...');
// Download and extract the archive to a temp directory.
var tempDir = systemCache.createTempDir();
@@ -521,8 +549,7 @@
}
@override
- Future _download(
- String server, String package, Version version, String destPath) {
+ Future _download(PackageId id, String destPath) {
// Since HostedSource is cached, this will only be called for uncached
// packages.
throw UnsupportedError('Cannot download packages when offline.');
diff --git a/test/cache/repair/handles_failure_test.dart b/test/cache/repair/handles_failure_test.dart
index 653ae2e..3759d76 100644
--- a/test/cache/repair/handles_failure_test.dart
+++ b/test/cache/repair/handles_failure_test.dart
@@ -35,11 +35,13 @@
var pub = await startPub(args: ['cache', 'repair']);
expect(pub.stdout, emits('Downloading foo 1.2.3...'));
- expect(pub.stdout, emits('Downloading foo 1.2.4...'));
expect(pub.stdout, emits('Downloading foo 1.2.5...'));
expect(pub.stderr, emits(startsWith('Failed to repair foo 1.2.4. Error:')));
- expect(pub.stderr, emits('HTTP error 404: Not Found'));
+ expect(
+ pub.stderr,
+ emits('Package doesn\'t exist '
+ '(Package foo has no version 1.2.4).'));
expect(pub.stdout, emits('Reinstalled 2 packages.'));
expect(pub.stdout, emits('Failed to reinstall 1 package:'));
diff --git a/test/package_server.dart b/test/package_server.dart
index 9f261bf..493bcba 100644
--- a/test/package_server.dart
+++ b/test/package_server.dart
@@ -100,13 +100,15 @@
'name': name,
'uploaders': ['nweiz@google.com'],
'versions': versions
- .map((version) => packageVersionApiMap(version.pubspec))
+ .map((version) => packageVersionApiMap(url, version.pubspec))
.toList()
})),
d.dir(name, [
d.dir('versions', versions.map((version) {
- return d.file(version.version.toString(),
- jsonEncode(packageVersionApiMap(version.pubspec, full: true)));
+ return d.file(
+ version.version.toString(),
+ jsonEncode(
+ packageVersionApiMap(url, version.pubspec, full: true)));
}))
])
]);
diff --git a/test/test_pub.dart b/test/test_pub.dart
index 27fd919..94e81f8 100644
--- a/test/test_pub.dart
+++ b/test/test_pub.dart
@@ -614,17 +614,13 @@
/// [pubspec] is the parsed pubspec of the package version. If [full] is true,
/// this returns the complete map, including metadata that's only included when
/// requesting the package version directly.
-Map packageVersionApiMap(Map pubspec, {bool full = false}) {
+Map packageVersionApiMap(String hostedUrl, Map pubspec, {bool full = false}) {
var name = pubspec['name'];
var version = pubspec['version'];
var map = {
'pubspec': pubspec,
'version': version,
- 'url': '/api/packages/$name/versions/$version',
- 'archive_url': '/packages/$name/versions/$version.tar.gz',
- 'new_dartdoc_url': '/api/packages/$name/versions/$version'
- '/new_dartdoc',
- 'package_url': '/api/packages/$name'
+ 'archive_url': '$hostedUrl/packages/$name/versions/$version.tar.gz',
};
if (full) {
diff --git a/test/validator/dependency_test.dart b/test/validator/dependency_test.dart
index 6ffd61f..31ed937 100644
--- a/test/validator/dependency_test.dart
+++ b/test/validator/dependency_test.dart
@@ -45,8 +45,8 @@
'name': 'foo',
'uploaders': ['nweiz@google.com'],
'versions': hostedVersions
- .map((version) =>
- packageVersionApiMap(packageMap('foo', version)))
+ .map((version) => packageVersionApiMap(
+ 'https://pub.dartlang.org', packageMap('foo', version)))
.toList()
}),
200));