Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions lib/src/entrypoint.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1046,8 +1046,10 @@ To update `$lockFilePath` run `$topLevelProgram pub get`$suffix without
}

// For cached sources, make sure the directory exists and looks like a
// package. This is also done by [_arePackagesAvailable] but that may
// not be run if the lockfile is newer than the pubspec.
// package. The main loop in [isResolutionUpToDate] also checks that
// the `pubspec.yaml` exists, but it stops early at the first pubspec
// that is strictly newer than the lockfile, so it may not have
// reached this package.
if (source is CachedSource && !dirExists(lockFilePackagePath) ||
!fileExists(p.join(lockFilePackagePath, 'pubspec.yaml'))) {
return false;
Expand Down Expand Up @@ -1321,15 +1323,20 @@ To update `$lockFilePath` run `$topLevelProgram pub get`$suffix without
'pubspec.yaml',
),
);
if (p.isWithin(cache.rootDir, pubspecPath)) {
continue;
}
final pubspecStat = tryStatFile(pubspecPath);
if (pubspecStat == null) {
log.fine('Could not find `$pubspecPath`');
// A dependency is missing - do a full new resolution.
return null;
}
// Cached packages are immutable, so their timestamps say nothing about
// this resolution: an entry in a shared cache may well have been
// downloaded by another project after this lockfile was written. They do
// have to exist though, which is why they are only skipped after the
// stat above. See https://github.com/dart-lang/pub/issues/4559.
if (p.isWithin(cache.rootDir, pubspecPath)) {
continue;
}

if (!lockFileModified.isAfter(pubspecStat.modified)) {
log.fine('`$pubspecPath` is newer than `$lockFilePath`');
Expand Down
10 changes: 10 additions & 0 deletions lib/src/source/hosted.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,16 @@ class HostedSource extends CachedSource {
contentHash = hashFromCache;
}
}
if (dirExists(packageDir) &&
!fileExists(p.join(packageDir, 'pubspec.yaml'))) {
// The entry is unusable without a `pubspec.yaml`. Pub never extracts a
// package without one, so something else has damaged the cache.
log.warning(
'Cached version of ${id.name}-${id.version} is missing its '
'pubspec.yaml - redownloading.',
);
deleteEntry(packageDir);
}
if (dirExists(packageDir)) {
contentHash ??= sha256FromCache(id, cache);
} else {
Expand Down
25 changes: 25 additions & 0 deletions test/deps_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@TestOn('vm')
library;

import 'package:pub/src/io.dart';
import 'package:pub/src/path.dart';
import 'package:test/test.dart';

Expand Down Expand Up @@ -340,6 +341,30 @@ void main() {
);
});

test(
're-resolves and succeeds when a cached package is deleted with --json',
() async {
await pubGet();
deleteEntry(p.join(d.sandbox, cachePath));
await runPub(
args: ['deps', '--json'],
output: contains('"name": "normal"'),
);
},
);

test("re-resolves and succeeds when a cached package's pubspec.yaml is "
'deleted with --json', () async {
await pubGet();
deleteEntry(
p.join(d.sandbox, d.hostedCachePath(), 'normal-1.2.3', 'pubspec.yaml'),
);
await runPub(
args: ['deps', '--json'],
output: contains('"name": "normal"'),
);
});

test('with the Flutter SDK, if applicable', () async {
await pubGet();

Expand Down
18 changes: 17 additions & 1 deletion test/embedding/ensure_pubspec_resolved.dart
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,23 @@ void testEnsurePubspecResolved() {
// Ensure that the pubspec looks newer than the lockfile.
await _touch('pubspec.yaml');

await _implicitPubGet('`pubspec.yaml` is newer than `pubspec.lock`');
await _implicitPubGet(
r'Could not find `$SANDBOX/cache/hosted/localhost%58$PORT/foo-1.0.0/pubspec.yaml`',
);
});

test('a cached package was deleted', () async {
await d.dir(appPath, [
d.appPubspec(dependencies: {'foo': '1.0.0'}),
]).create();

await pubGet();

deleteEntry(p.join(d.sandbox, cachePath));

await _implicitPubGet(
r'Could not find `$SANDBOX/cache/hosted/localhost%58$PORT/foo-1.0.0/pubspec.yaml`',
);
});

test('the package_config.json file points to the wrong place', () async {
Expand Down
22 changes: 22 additions & 0 deletions test/get/hosted/get_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -444,4 +444,26 @@ void main() {
exitCode: UNAVAILABLE,
);
});

test('redownloads a cached package whose pubspec.yaml is missing', () async {
final server = await servePackages();
server.serve('foo', '1.0.0');
await d.appDir(dependencies: {'foo': '1.0.0'}).create();
await pubGet();

final pubspecPath = p.join(
d.sandbox,
d.hostedCachePath(),
'foo-1.0.0',
'pubspec.yaml',
);
deleteEntry(pubspecPath);

await pubGet(
warning:
'Cached version of foo-1.0.0 is missing its pubspec.yaml '
'- redownloading.',
);
expect(fileExists(pubspecPath), isTrue);
});
}
Loading