diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 90b346d784a..312845ddc29 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'package:args/args.dart'; import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; import 'package:meta/meta.dart'; import 'package:quiver/strings.dart'; @@ -707,8 +708,20 @@ abstract class FlutterCommand extends Command { Future validateCommand() async { if (_requiresPubspecYaml && !PackageMap.isUsingCustomPackagesPath) { // Don't expect a pubspec.yaml file if the user passed in an explicit .packages file path. - if (!globals.fs.isFileSync('pubspec.yaml')) { - throw ToolExit(userMessages.flutterNoPubspec); + + // If there is no pubspec in the current directory, look in the parent + // until one can be found. + bool changedDirectory = false; + while (!globals.fs.isFileSync('pubspec.yaml')) { + final Directory nextCurrent = globals.fs.currentDirectory.parent; + if (nextCurrent == null || nextCurrent.path == globals.fs.currentDirectory.path) { + throw ToolExit(userMessages.flutterNoPubspec); + } + globals.fs.currentDirectory = nextCurrent; + changedDirectory = true; + } + if (changedDirectory) { + globals.printStatus('Changing current working directory to: ${globals.fs.currentDirectory.path}'); } // Validate the current package map only if we will not be running "pub get" later. diff --git a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart index 0257dc10b8b..c0e44144277 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart @@ -125,6 +125,58 @@ void main() { DeviceManager: () => MockDeviceManager(), }); + testUsingContext('Walks upward looking for a pubspec.yaml and succeeds if found', () async { + globals.fs.file('pubspec.yaml').createSync(); + globals.fs.file('.packages') + ..createSync() + ..writeAsStringSync('Not a valid package'); + + globals.fs.currentDirectory = globals.fs.directory(globals.fs.path.join('a', 'b', 'c')) + ..createSync(recursive: true); + + final RunCommand command = RunCommand(); + applyMocksToCommand(command); + try { + await createTestCommandRunner(command).run([ + 'run', + '--fast-start', + '--no-pub', + ]); + fail('Expect exception'); + } catch (e) { + expect(e, isInstanceOf()); + } + final BufferLogger bufferLogger = globals.logger as BufferLogger; + expect(bufferLogger.statusText, contains( + 'Changing current working directory to:' + )); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + ProcessManager: () => FakeProcessManager.any(), + }); + + testUsingContext('Walks upward looking for a pubspec.yaml and exits if missing', () async { + globals.fs.currentDirectory = globals.fs.directory(globals.fs.path.join('a', 'b', 'c')) + ..createSync(recursive: true); + + final RunCommand command = RunCommand(); + applyMocksToCommand(command); + try { + await createTestCommandRunner(command).run([ + 'run', + '--fast-start', + '--no-pub', + ]); + fail('Expect exception'); + } catch (e) { + expect(e, isInstanceOf()); + expect(e.toString(), contains('No pubspec.yaml file found')); + } + }, overrides: { + FileSystem: () => MemoryFileSystem(), + ProcessManager: () => FakeProcessManager.any(), + }); + group('run app', () { MemoryFileSystem fs;