Unverified Commit a0be9802 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] ensure pub get can run from partially valid state (#74249)

parent 7a413e71
...@@ -76,6 +76,8 @@ class PackagesGetCommand extends FlutterCommand { ...@@ -76,6 +76,8 @@ class PackagesGetCommand extends FlutterCommand {
return '${runner.executableName} pub $name [<target directory>]'; return '${runner.executableName} pub $name [<target directory>]';
} }
/// The pub packages usage values are incorrect since these are calculated/sent
/// before pub get completes. This needs to be performed after dependency resolution.
@override @override
Future<Map<CustomDimensions, String>> get usageValues async { Future<Map<CustomDimensions, String>> get usageValues async {
final Map<CustomDimensions, String> usageValues = <CustomDimensions, String>{}; final Map<CustomDimensions, String> usageValues = <CustomDimensions, String>{};
...@@ -85,9 +87,14 @@ class PackagesGetCommand extends FlutterCommand { ...@@ -85,9 +87,14 @@ class PackagesGetCommand extends FlutterCommand {
return usageValues; return usageValues;
} }
final FlutterProject rootProject = FlutterProject.fromPath(target); final FlutterProject rootProject = FlutterProject.fromPath(target);
final bool hasPlugins = rootProject.flutterPluginsDependenciesFile.existsSync(); // Do not send plugin analytics if pub has not run before.
final bool hasPlugins = rootProject.flutterPluginsDependenciesFile.existsSync()
&& rootProject.packagesFile.existsSync()
&& rootProject.packageConfigFile.existsSync();
if (hasPlugins) { if (hasPlugins) {
final List<Plugin> plugins = await findPlugins(rootProject); // Do not fail pub get if package config files are invalid before pub has
// had a chance to run.
final List<Plugin> plugins = await findPlugins(rootProject, throwOnError: false);
usageValues[CustomDimensions.commandPackagesNumberPlugins] = plugins.length.toString(); usageValues[CustomDimensions.commandPackagesNumberPlugins] = plugins.length.toString();
} else { } else {
usageValues[CustomDimensions.commandPackagesNumberPlugins] = '0'; usageValues[CustomDimensions.commandPackagesNumberPlugins] = '0';
......
...@@ -345,7 +345,7 @@ Plugin _pluginFromPackage(String name, Uri packageRoot) { ...@@ -345,7 +345,7 @@ Plugin _pluginFromPackage(String name, Uri packageRoot) {
); );
} }
Future<List<Plugin>> findPlugins(FlutterProject project) async { Future<List<Plugin>> findPlugins(FlutterProject project, { bool throwOnError = true}) async {
final List<Plugin> plugins = <Plugin>[]; final List<Plugin> plugins = <Plugin>[];
final String packagesFile = globals.fs.path.join( final String packagesFile = globals.fs.path.join(
project.directory.path, project.directory.path,
...@@ -354,6 +354,7 @@ Future<List<Plugin>> findPlugins(FlutterProject project) async { ...@@ -354,6 +354,7 @@ Future<List<Plugin>> findPlugins(FlutterProject project) async {
final PackageConfig packageConfig = await loadPackageConfigWithLogging( final PackageConfig packageConfig = await loadPackageConfigWithLogging(
globals.fs.file(packagesFile), globals.fs.file(packagesFile),
logger: globals.logger, logger: globals.logger,
throwOnError: throwOnError,
); );
for (final Package package in packageConfig.packages) { for (final Package package in packageConfig.packages) {
final Uri packageRoot = package.packageUriRoot.resolve('..'); final Uri packageRoot = package.packageUriRoot.resolve('..');
......
...@@ -162,6 +162,12 @@ class FlutterProject { ...@@ -162,6 +162,12 @@ class FlutterProject {
/// The `.packages` file of this project. /// The `.packages` file of this project.
File get packagesFile => directory.childFile('.packages'); File get packagesFile => directory.childFile('.packages');
/// The `package_config.json` file of the project.
///
/// This is the replacement for .packages which contains language
/// version information.
File get packageConfigFile => directory.childDirectory('.dart_tool').childFile('package_config.json');
/// The `.metadata` file of this project. /// The `.metadata` file of this project.
File get metadataFile => directory.childFile('.metadata'); File get metadataFile => directory.childFile('.metadata');
......
// Copyright 2014 The Flutter Authors. 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:args/command_runner.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/packages.dart';
import 'package:flutter_tools/src/dart/pub.dart';
import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:meta/meta.dart';
import 'package:test/fake.dart';
import '../../src/common.dart';
import '../../src/context.dart';
void main() {
FileSystem fileSystem;
FakePub pub;
setUp(() {
Cache.disableLocking();
fileSystem = MemoryFileSystem.test();
pub = FakePub(fileSystem);
});
tearDown(() {
Cache.enableLocking();
});
testUsingContext('pub get usage values are resilient to missing package config files before running "pub get"', () async {
fileSystem.currentDirectory.childFile('pubspec.yaml').createSync();
fileSystem.currentDirectory.childFile('.flutter-plugins').createSync();
fileSystem.currentDirectory.childFile('.flutter-plugins-dependencies').createSync();
final PackagesGetCommand command = PackagesGetCommand('get', false);
final CommandRunner<void> commandRunner = createTestCommandRunner(command);
await commandRunner.run(<String>['get']);
expect(await command.usageValues, <CustomDimensions, Object>{
CustomDimensions.commandPackagesNumberPlugins: '0',
CustomDimensions.commandPackagesProjectModule: 'false',
CustomDimensions.commandPackagesAndroidEmbeddingVersion: 'v1'
});
}, overrides: <Type, Generator>{
Pub: () => pub,
ProcessManager: () => FakeProcessManager.any(),
FileSystem: () => fileSystem,
});
testUsingContext('pub get usage values are resilient to poorly formatted package config before "pub get"', () async {
fileSystem.currentDirectory.childFile('pubspec.yaml').createSync();
fileSystem.currentDirectory.childFile('.flutter-plugins').createSync();
fileSystem.currentDirectory.childFile('.flutter-plugins-dependencies').createSync();
fileSystem.currentDirectory.childFile('.packages').writeAsBytesSync(<int>[0]);
fileSystem.currentDirectory.childFile('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsBytesSync(<int>[0]);
final PackagesGetCommand command = PackagesGetCommand('get', false);
final CommandRunner<void> commandRunner = createTestCommandRunner(command);
await commandRunner.run(<String>['get']);
expect(await command.usageValues, <CustomDimensions, Object>{
CustomDimensions.commandPackagesNumberPlugins: '0',
CustomDimensions.commandPackagesProjectModule: 'false',
CustomDimensions.commandPackagesAndroidEmbeddingVersion: 'v1'
});
}, overrides: <Type, Generator>{
Pub: () => pub,
ProcessManager: () => FakeProcessManager.any(),
FileSystem: () => fileSystem,
});
}
class FakePub extends Fake implements Pub {
FakePub(this.fileSystem);
final FileSystem fileSystem;
@override
Future<void> get({
@required PubContext context,
String directory,
bool skipIfAbsent = false,
bool upgrade = false,
bool offline = false,
bool generateSyntheticPackage = false,
String flutterRootOverride,
bool checkUpToDate = false,
}) async {
fileSystem.currentDirectory
.childDirectory('.dart_tool')
.childFile('package_config.json')
..createSync(recursive: true)
..writeAsStringSync('{"configVersion":2,"packages":[]}');
}
}
...@@ -8,10 +8,10 @@ import 'package:flutter_tools/src/base/io.dart'; ...@@ -8,10 +8,10 @@ import 'package:flutter_tools/src/base/io.dart';
import '../src/common.dart'; import '../src/common.dart';
import 'test_utils.dart'; import 'test_utils.dart';
/// Verifies that `dart migrate` will run successfuly on the default `flutter create` /// Verifies that `dart migrate` will run successfully on the default `flutter create`
/// template. /// template.
void main() { void main() {
testWithoutContext('dart migrate succeedes on flutter create template', () async { testWithoutContext('dart migrate succeeds on flutter create template', () async {
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', platform.isWindows ? 'flutter.bat' : 'flutter'); final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', platform.isWindows ? 'flutter.bat' : 'flutter');
final String dartBin = fileSystem.path.join(getFlutterRoot(), 'bin', platform.isWindows ? 'dart.bat' : 'dart'); final String dartBin = fileSystem.path.join(getFlutterRoot(), 'bin', platform.isWindows ? 'dart.bat' : 'dart');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment