Refactor flutter command exit code - part 3 of 3 (#6838)

* Remove the workaround that pinned args to v0.13.6
This reverts most of the changes in commit 6331b6c8b5d964ec0dbf2cd9bb84c60c650a0878
* throw exception if exit code is not an integer
* rework command infrastructure to throw ToolExit when non-zero exitCode
* convert commands to return Future<Null>
* cleanup remaining commands to use throwToolExit for non-zero exit code
* remove isUnusual exception message
* add type annotations for updated args package
diff --git a/dev/automated_tests/pubspec.yaml b/dev/automated_tests/pubspec.yaml
index 5f4288f..a76769d 100644
--- a/dev/automated_tests/pubspec.yaml
+++ b/dev/automated_tests/pubspec.yaml
@@ -1,11 +1,5 @@
 name: flutter_automated_tests
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
   flutter:
     sdk: flutter
   flutter_test:
diff --git a/dev/benchmarks/complex_layout/pubspec.yaml b/dev/benchmarks/complex_layout/pubspec.yaml
index 59c3ebe..8e3f79a 100644
--- a/dev/benchmarks/complex_layout/pubspec.yaml
+++ b/dev/benchmarks/complex_layout/pubspec.yaml
@@ -2,12 +2,6 @@
 description: A new flutter project.
 
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
   flutter:
     sdk: flutter
   flutter_driver:
diff --git a/dev/benchmarks/microbenchmarks/pubspec.yaml b/dev/benchmarks/microbenchmarks/pubspec.yaml
index 15319a2..007fc94 100644
--- a/dev/benchmarks/microbenchmarks/pubspec.yaml
+++ b/dev/benchmarks/microbenchmarks/pubspec.yaml
@@ -1,12 +1,6 @@
 name: microbenchmarks
 description: A new flutter project.
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
   flutter:
     sdk: flutter
   flutter_test:
diff --git a/dev/devicelab/pubspec.yaml b/dev/devicelab/pubspec.yaml
index 2126ea8..013912f 100644
--- a/dev/devicelab/pubspec.yaml
+++ b/dev/devicelab/pubspec.yaml
@@ -8,12 +8,7 @@
   sdk: '>=1.12.0 <2.0.0'
 
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
+  args: ^0.13.4
   meta: ^1.0.3
   path: ^1.3.0
   stack_trace: ^1.4.0
diff --git a/dev/manual_tests/pubspec.yaml b/dev/manual_tests/pubspec.yaml
index a1f49a5..026ef9e 100644
--- a/dev/manual_tests/pubspec.yaml
+++ b/dev/manual_tests/pubspec.yaml
@@ -1,12 +1,6 @@
 name: flutter_manual_tests
 
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
   flutter:
     sdk: flutter
 
diff --git a/dev/tools/pubspec.yaml b/dev/tools/pubspec.yaml
index 62febc5..87f23b4 100644
--- a/dev/tools/pubspec.yaml
+++ b/dev/tools/pubspec.yaml
@@ -2,10 +2,5 @@
 description: Various repository development tools for flutter.
 
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
+  args: ^0.13.4
   path: ^1.3.0
diff --git a/examples/flutter_gallery/pubspec.yaml b/examples/flutter_gallery/pubspec.yaml
index 4fc5f47..b8128aa 100644
--- a/examples/flutter_gallery/pubspec.yaml
+++ b/examples/flutter_gallery/pubspec.yaml
@@ -1,11 +1,5 @@
 name: flutter_gallery
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
   collection: '>=1.9.1 <2.0.0'
   intl: '>=0.14.0 <0.15.0'
   string_scanner: ^1.0.0
diff --git a/examples/hello_services/pubspec.yaml b/examples/hello_services/pubspec.yaml
index 7472d21..16e9afc 100644
--- a/examples/hello_services/pubspec.yaml
+++ b/examples/hello_services/pubspec.yaml
@@ -1,12 +1,6 @@
 name: hello_services
 
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
   flutter:
     sdk: flutter
 
diff --git a/examples/hello_world/pubspec.yaml b/examples/hello_world/pubspec.yaml
index 8be6c8c..0588b4e 100644
--- a/examples/hello_world/pubspec.yaml
+++ b/examples/hello_world/pubspec.yaml
@@ -1,12 +1,6 @@
 name: hello_world
 
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
   flutter:
     sdk: flutter
 
diff --git a/examples/layers/pubspec.yaml b/examples/layers/pubspec.yaml
index 3c8c1cd..c88f60f 100644
--- a/examples/layers/pubspec.yaml
+++ b/examples/layers/pubspec.yaml
@@ -1,11 +1,5 @@
 name: flutter_examples_layers
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
   flutter:
     sdk: flutter
 
diff --git a/examples/stocks/pubspec.yaml b/examples/stocks/pubspec.yaml
index caeb30d..471dad2 100644
--- a/examples/stocks/pubspec.yaml
+++ b/examples/stocks/pubspec.yaml
@@ -1,11 +1,5 @@
 name: stocks
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
   flutter:
     sdk: flutter
   intl: '>=0.14.0 <0.15.0'
diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml
index 1ebb32c..ef53fcc 100644
--- a/packages/flutter/pubspec.yaml
+++ b/packages/flutter/pubspec.yaml
@@ -5,12 +5,6 @@
 homepage: http://flutter.io
 
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
   collection: '>=1.9.1 <2.0.0'
   intl: '>=0.14.0 <0.15.0'
   meta: ^1.0.3
diff --git a/packages/flutter_driver/pubspec.yaml b/packages/flutter_driver/pubspec.yaml
index 23563e1..bb5a624 100644
--- a/packages/flutter_driver/pubspec.yaml
+++ b/packages/flutter_driver/pubspec.yaml
@@ -8,12 +8,6 @@
   sdk: '>=1.19.0 <2.0.0'
 
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
   file: '^0.1.0'
   json_rpc_2: '^2.0.0'
   matcher: '>=0.12.0 <1.0.0'
diff --git a/packages/flutter_markdown/pubspec.yaml b/packages/flutter_markdown/pubspec.yaml
index 55f6094..fb17122 100644
--- a/packages/flutter_markdown/pubspec.yaml
+++ b/packages/flutter_markdown/pubspec.yaml
@@ -5,12 +5,6 @@
 homepage: http://flutter.io
 
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
   flutter:
     sdk: flutter
   markdown: '^0.9.0'
diff --git a/packages/flutter_test/pubspec.yaml b/packages/flutter_test/pubspec.yaml
index b063d5d..a78ab3c 100644
--- a/packages/flutter_test/pubspec.yaml
+++ b/packages/flutter_test/pubspec.yaml
@@ -1,11 +1,5 @@
 name: flutter_test
 dependencies:
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
   quiver: ^0.21.4
 
   # The flutter tools depend on very specific internal implementation
diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart
index 209cead..2f1406d 100644
--- a/packages/flutter_tools/lib/executable.dart
+++ b/packages/flutter_tools/lib/executable.dart
@@ -94,8 +94,8 @@
     if (context[HotRunnerConfig] == null)
       context[HotRunnerConfig] = new HotRunnerConfig();
 
-    dynamic result = await runner.run(args);
-    _exit(result is int ? result : 1);
+    await runner.run(args);
+    _exit(0);
   }, onError: (dynamic error, Chain chain) {
     if (error is UsageException) {
       stderr.writeln(error.message);
@@ -114,10 +114,6 @@
         stderr.writeln(chain.terse.toString());
         stderr.writeln();
       }
-      if (error.isUnusual) {
-        stderr.writeln('If this problem persists, please report the problem at');
-        stderr.writeln('https://github.com/flutter/flutter/issues/new');
-      }
       _exit(error.exitCode ?? 1);
     } else if (error is ProcessExit) {
       // We've caught an exit code.
diff --git a/packages/flutter_tools/lib/src/base/common.dart b/packages/flutter_tools/lib/src/base/common.dart
index a2e474b..0e3f867 100644
--- a/packages/flutter_tools/lib/src/base/common.dart
+++ b/packages/flutter_tools/lib/src/base/common.dart
@@ -27,8 +27,8 @@
 /// where the tool should exit with a clear message to the user
 /// and no stack trace unless the --verbose option is specified.
 /// For example: network errors
-void throwToolExit(String message, { int exitCode, bool isUnusual: false }) {
-  throw new ToolExit(message, exitCode: exitCode, isUnusual: isUnusual);
+void throwToolExit(String message, { int exitCode }) {
+  throw new ToolExit(message, exitCode: exitCode );
 }
 
 /// Specialized exception for expected situations
@@ -37,11 +37,10 @@
 /// For example: network errors
 class ToolExit implements Exception {
 
-  ToolExit(this.message, { this.exitCode, this.isUnusual: false });
+  ToolExit(this.message, { this.exitCode });
 
   final String message;
   final int exitCode;
-  final bool isUnusual;
 
   @override
   String toString() => "Exception: $message";
diff --git a/packages/flutter_tools/lib/src/base/net.dart b/packages/flutter_tools/lib/src/base/net.dart
index 982003f..7734bde 100644
--- a/packages/flutter_tools/lib/src/base/net.dart
+++ b/packages/flutter_tools/lib/src/base/net.dart
@@ -24,7 +24,6 @@
       'Download failed: $url\n'
           '  because (${response.statusCode}) ${response.reasonPhrase}',
       exitCode: kNetworkProblemExitCode,
-      isUnusual: true,
     );
   }
 
@@ -38,7 +37,6 @@
     throw new ToolExit(
       'Download failed: $url\n  $e',
       exitCode: kNetworkProblemExitCode,
-      isUnusual: true,
     );
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/analyze.dart b/packages/flutter_tools/lib/src/commands/analyze.dart
index a4e3dce..9b34e38 100644
--- a/packages/flutter_tools/lib/src/commands/analyze.dart
+++ b/packages/flutter_tools/lib/src/commands/analyze.dart
@@ -53,7 +53,7 @@
   }
 
   @override
-  Future<int> runCommand() {
+  Future<Null> runCommand() {
     if (argResults['watch']) {
       return new AnalyzeContinuously(argResults, runner.getRepoAnalysisEntryPoints()).analyze();
     } else {
diff --git a/packages/flutter_tools/lib/src/commands/analyze_base.dart b/packages/flutter_tools/lib/src/commands/analyze_base.dart
index da8407b..2e3ea36 100644
--- a/packages/flutter_tools/lib/src/commands/analyze_base.dart
+++ b/packages/flutter_tools/lib/src/commands/analyze_base.dart
@@ -20,7 +20,7 @@
   AnalyzeBase(this.argResults);
 
   /// Called by [AnalyzeCommand] to start the analysis process.
-  Future<int> analyze();
+  Future<Null> analyze();
 
   void dumpErrors(Iterable<String> errors) {
     if (argResults['write'] != null) {
diff --git a/packages/flutter_tools/lib/src/commands/analyze_continuously.dart b/packages/flutter_tools/lib/src/commands/analyze_continuously.dart
index dcd73cb..b04d6f4 100644
--- a/packages/flutter_tools/lib/src/commands/analyze_continuously.dart
+++ b/packages/flutter_tools/lib/src/commands/analyze_continuously.dart
@@ -31,7 +31,7 @@
   Status analysisStatus;
 
   @override
-  Future<int> analyze() async {
+  Future<Null> analyze() async {
     List<String> directories;
 
     if (argResults['flutter-repo']) {
@@ -58,7 +58,6 @@
     if (exitCode != 0)
       throwToolExit(message, exitCode: exitCode);
     printStatus(message);
-    return 0;
   }
 
   void _handleAnalysisStatus(AnalysisServer server, bool isAnalyzing) {
diff --git a/packages/flutter_tools/lib/src/commands/analyze_once.dart b/packages/flutter_tools/lib/src/commands/analyze_once.dart
index b7b8b61..7b4c266 100644
--- a/packages/flutter_tools/lib/src/commands/analyze_once.dart
+++ b/packages/flutter_tools/lib/src/commands/analyze_once.dart
@@ -28,7 +28,7 @@
   AnalyzeOnce(ArgResults argResults, this.repoPackages) : super(argResults);
 
   @override
-  Future<int> analyze() async {
+  Future<Null> analyze() async {
     Stopwatch stopwatch = new Stopwatch()..start();
     Set<Directory> pubSpecDirectories = new HashSet<Directory>();
     List<File> dartFiles = <File>[];
@@ -205,7 +205,6 @@
         printStatus('No analyzer warnings! (ran in ${elapsed}s)');
       }
     }
-    return 0;
   }
 
   List<String> flutterRootComponents;
diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart
index 82c9214..21eff64 100644
--- a/packages/flutter_tools/lib/src/commands/build.dart
+++ b/packages/flutter_tools/lib/src/commands/build.dart
@@ -33,26 +33,26 @@
   final String description = 'Flutter build commands.';
 
   @override
-  Future<int> verifyThenRunCommand() async {
+  Future<Null> verifyThenRunCommand() async {
     commandValidator();
     return super.verifyThenRunCommand();
   }
 
   @override
-  Future<int> runCommand() => new Future<int>.value(0);
+  Future<Null> runCommand() async { }
 }
 
 abstract class BuildSubCommand extends FlutterCommand {
   @override
   @mustCallSuper
-  Future<int> verifyThenRunCommand() async {
+  Future<Null> verifyThenRunCommand() async {
     commandValidator();
     return super.verifyThenRunCommand();
   }
 
   @override
   @mustCallSuper
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     if (isRunningOnBot) {
       File dotPackages = new File('.packages');
       printStatus('Contents of .packages:');
@@ -68,7 +68,6 @@
       else
         printError('File not found: ${pubspecLock.absolute.path}');
     }
-    return 0;
   }
 }
 
@@ -80,24 +79,23 @@
   final String description = 'Delete the build/ directory.';
 
   @override
-  Future<int> verifyThenRunCommand() async {
+  Future<Null> verifyThenRunCommand() async {
     commandValidator();
     return super.verifyThenRunCommand();
   }
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     Directory buildDir = new Directory(getBuildDirectory());
     printStatus("Deleting '${buildDir.path}${Platform.pathSeparator}'.");
 
     if (!buildDir.existsSync())
-      return 0;
+      return;
 
     try {
       buildDir.deleteSync(recursive: true);
     } catch (error) {
       throwToolExit(error.toString());
     }
-    return 0;
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/build_aot.dart b/packages/flutter_tools/lib/src/commands/build_aot.dart
index e9b02ff..e5b31a9 100644
--- a/packages/flutter_tools/lib/src/commands/build_aot.dart
+++ b/packages/flutter_tools/lib/src/commands/build_aot.dart
@@ -43,7 +43,7 @@
   final String description = "Build an ahead-of-time compiled snapshot of your app's Dart code.";
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     await super.runCommand();
     String targetPlatform = argResults['target-platform'];
     TargetPlatform platform = getTargetPlatformForName(targetPlatform);
@@ -65,7 +65,6 @@
       throwToolExit(null);
 
     printStatus('Built to $outputPath${Platform.pathSeparator}.');
-    return 0;
   }
 }
 
diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart
index 01b1933..114b862 100644
--- a/packages/flutter_tools/lib/src/commands/build_apk.dart
+++ b/packages/flutter_tools/lib/src/commands/build_apk.dart
@@ -224,7 +224,7 @@
   }
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     await super.runCommand();
 
     TargetPlatform targetPlatform = _getTargetPlatform(argResults['target-arch']);
@@ -258,7 +258,6 @@
         )
       );
     }
-    return 0;
   }
 }
 
diff --git a/packages/flutter_tools/lib/src/commands/build_flx.dart b/packages/flutter_tools/lib/src/commands/build_flx.dart
index a96bf63..53927fc 100644
--- a/packages/flutter_tools/lib/src/commands/build_flx.dart
+++ b/packages/flutter_tools/lib/src/commands/build_flx.dart
@@ -37,7 +37,7 @@
     'they are used by some Flutter Android and iOS runtimes.';
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     await super.runCommand();
     String outputPath = argResults['output-file'];
 
@@ -53,6 +53,5 @@
       includeRobotoFonts: argResults['include-roboto-fonts'],
       reportLicensedPackages: argResults['report-licensed-packages']
     );
-    return 0;
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/build_ios.dart b/packages/flutter_tools/lib/src/commands/build_ios.dart
index 9d207c5..3d739b5 100644
--- a/packages/flutter_tools/lib/src/commands/build_ios.dart
+++ b/packages/flutter_tools/lib/src/commands/build_ios.dart
@@ -39,7 +39,7 @@
   final String description = 'Build an iOS application bundle (Mac OS X host only).';
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     bool forSimulator = argResults['simulator'];
     defaultBuildMode = forSimulator ? BuildMode.debug : BuildMode.release;
 
@@ -83,7 +83,5 @@
 
     if (result.output != null)
       printStatus('Built ${result.output}.');
-
-    return 0;
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/channel.dart b/packages/flutter_tools/lib/src/commands/channel.dart
index 236c2a6..30ec6a4 100644
--- a/packages/flutter_tools/lib/src/commands/channel.dart
+++ b/packages/flutter_tools/lib/src/commands/channel.dart
@@ -21,7 +21,7 @@
   String get invocation => '${runner.executableName} $name [<channel-name>]';
 
   @override
-  Future<int> runCommand() {
+  Future<Null> runCommand() {
     switch (argResults.rest.length) {
       case 0:
         return _listChannels();
@@ -32,7 +32,7 @@
     }
   }
 
-  Future<int> _listChannels() async {
+  Future<Null> _listChannels() async {
     String currentBranch = runSync(
         <String>['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
         workingDirectory: Cache.flutterRoot);
@@ -52,10 +52,9 @@
     );
     if (result != 0)
       throwToolExit('List channels failed: $result', exitCode: result);
-    return 0;
   }
 
-  Future<int> _switchChannel(String branchName) async {
+  Future<Null> _switchChannel(String branchName) async {
     printStatus('Switching to flutter channel named $branchName');
     int result = await runCommandAndStreamOutput(
       <String>['git', 'checkout', branchName],
@@ -63,6 +62,5 @@
     );
     if (result != 0)
       throwToolExit('Switch channel failed: $result', exitCode: result);
-    return 0;
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/config.dart b/packages/flutter_tools/lib/src/commands/config.dart
index 8addb33..af16a39 100644
--- a/packages/flutter_tools/lib/src/commands/config.dart
+++ b/packages/flutter_tools/lib/src/commands/config.dart
@@ -46,7 +46,7 @@
   String get usagePath => null;
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     if (argResults.wasParsed('analytics')) {
       bool value = argResults['analytics'];
       flutterUsage.enabled = value;
@@ -61,8 +61,6 @@
 
     if (argResults.arguments.isEmpty)
       printStatus(usage);
-
-    return 0;
   }
 
   void _updateConfig(String keyName, String keyValue) {
diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index 47c8d43..7676e8c 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -46,7 +46,7 @@
   String get invocation => "${runner.executableName} $name <output directory>";
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     if (argResults.rest.isEmpty)
       throwToolExit('No option specified for the output directory.\n$usage', exitCode: 2);
 
@@ -134,7 +134,6 @@
         "directory in order to launch your app.");
       printStatus("Your main program file is: $relativePath/lib/main.dart");
     }
-    return 0;
   }
 
   int _renderTemplates(String projectName, String projectDescription, String dirPath,
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index f55f6bb..e44e17e 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -43,7 +43,7 @@
   final bool hidden;
 
   @override
-  Future<int> runCommand() {
+  Future<Null> runCommand() {
     printStatus('Starting device daemon...');
 
     AppContext appContext = new AppContext();
@@ -60,7 +60,6 @@
       int code = await daemon.onExit;
       if (code != null)
         throwToolExit(null, exitCode: code);
-      return 0;
     }, onError: _handleError);
   }
 
diff --git a/packages/flutter_tools/lib/src/commands/devices.dart b/packages/flutter_tools/lib/src/commands/devices.dart
index afe91a8..687a3ac 100644
--- a/packages/flutter_tools/lib/src/commands/devices.dart
+++ b/packages/flutter_tools/lib/src/commands/devices.dart
@@ -18,7 +18,7 @@
   final String description = 'List all connected devices.';
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     if (!doctor.canListAnything) {
       throwToolExit(
         "Unable to locate a development device; please run 'flutter doctor' for "
@@ -37,6 +37,5 @@
       printStatus('${devices.length} connected ${pluralize('device', devices.length)}:\n');
       Device.printDevices(devices);
     }
-    return 0;
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/doctor.dart b/packages/flutter_tools/lib/src/commands/doctor.dart
index 9fa6de3..f1a6a17 100644
--- a/packages/flutter_tools/lib/src/commands/doctor.dart
+++ b/packages/flutter_tools/lib/src/commands/doctor.dart
@@ -15,8 +15,7 @@
   final String description = 'Show information about the installed tooling.';
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     await doctor.diagnose();
-    return 0;
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/drive.dart b/packages/flutter_tools/lib/src/commands/drive.dart
index ba68053..ece12f6 100644
--- a/packages/flutter_tools/lib/src/commands/drive.dart
+++ b/packages/flutter_tools/lib/src/commands/drive.dart
@@ -90,13 +90,13 @@
   int get debugPort => int.parse(argResults['debug-port']);
 
   @override
-  Future<int> verifyThenRunCommand() async {
+  Future<Null> verifyThenRunCommand() async {
     commandValidator();
     return super.verifyThenRunCommand();
   }
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     String testFile = _getTestFile();
     if (testFile == null)
       throwToolExit(null);
@@ -149,7 +149,6 @@
         printStatus('Leaving the application running.');
       }
     }
-    return 0;
   }
 
   String _getTestFile() {
diff --git a/packages/flutter_tools/lib/src/commands/format.dart b/packages/flutter_tools/lib/src/commands/format.dart
index 84a4001..9b741a0 100644
--- a/packages/flutter_tools/lib/src/commands/format.dart
+++ b/packages/flutter_tools/lib/src/commands/format.dart
@@ -25,7 +25,7 @@
   String get invocation => "${runner.executableName} $name <one or more paths>";
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     if (argResults.rest.isEmpty) {
       throwToolExit(
         'No files specified to be formatted.\n'
@@ -43,6 +43,5 @@
     int result = await runCommandAndStreamOutput(cmd);
     if (result != 0)
       throwToolExit('Formatting failed: $result', exitCode: result);
-    return 0;
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/install.dart b/packages/flutter_tools/lib/src/commands/install.dart
index 908439b..fc1de89 100644
--- a/packages/flutter_tools/lib/src/commands/install.dart
+++ b/packages/flutter_tools/lib/src/commands/install.dart
@@ -21,7 +21,7 @@
   Device device;
 
   @override
-  Future<int> verifyThenRunCommand() async {
+  Future<Null> verifyThenRunCommand() async {
     commandValidator();
     device = await findTargetDevice();
     if (device == null)
@@ -30,7 +30,7 @@
   }
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
 
     Cache.releaseLockEarly();
@@ -39,7 +39,6 @@
 
     if (!installApp(device, package))
       throwToolExit('Install failed');
-    return 0;
   }
 }
 
diff --git a/packages/flutter_tools/lib/src/commands/logs.dart b/packages/flutter_tools/lib/src/commands/logs.dart
index 65a6228..1b987cc 100644
--- a/packages/flutter_tools/lib/src/commands/logs.dart
+++ b/packages/flutter_tools/lib/src/commands/logs.dart
@@ -29,7 +29,7 @@
   Device device;
 
   @override
-  Future<int> verifyThenRunCommand() async {
+  Future<Null> verifyThenRunCommand() async {
     device = await findTargetDevice();
     if (device == null)
       throwToolExit(null);
@@ -37,7 +37,7 @@
   }
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     if (argResults['clear'])
       device.clearLogs();
 
@@ -76,6 +76,5 @@
     subscription.cancel();
     if (result != 0)
       throwToolExit('Error listening to $logReader logs.');
-    return 0;
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/packages.dart b/packages/flutter_tools/lib/src/commands/packages.dart
index bf4ae4d..eace49a 100644
--- a/packages/flutter_tools/lib/src/commands/packages.dart
+++ b/packages/flutter_tools/lib/src/commands/packages.dart
@@ -25,13 +25,13 @@
   final String description = 'Commands for managing Flutter packages.';
 
   @override
-  Future<int> verifyThenRunCommand() async {
+  Future<Null> verifyThenRunCommand() async {
     commandValidator();
     return super.verifyThenRunCommand();
   }
 
   @override
-  Future<int> runCommand() => new Future<int>.value(0);
+  Future<Null> runCommand() async { }
 }
 
 class PackagesGetCommand extends FlutterCommand {
@@ -52,7 +52,7 @@
       "${runner.executableName} packages $name [<target directory>]";
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     if (argResults.rest.length > 1)
       throwToolExit('Too many arguments.\n$usage');
 
@@ -69,6 +69,5 @@
     // TODO: If the user is using a local build, we should use the packages from their build instead of the cache.
 
     await pubGet(directory: target, upgrade: upgrade, checkLastModified: false);
-    return 0;
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/precache.dart b/packages/flutter_tools/lib/src/commands/precache.dart
index 303d467..c8665c4 100644
--- a/packages/flutter_tools/lib/src/commands/precache.dart
+++ b/packages/flutter_tools/lib/src/commands/precache.dart
@@ -20,7 +20,7 @@
   final String description = 'Populates the Flutter tool\'s cache of binary artifacts.';
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     if (argResults['all-platforms'])
       cache.includeAllPlatforms = true;
 
@@ -28,7 +28,5 @@
       printStatus('Already up-to-date.');
     else
       await cache.updateAll();
-
-    return 0;
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index 669fedf..03981ee 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -148,7 +148,7 @@
       argResults['use-application-binary'] != null;
 
   @override
-  Future<int> verifyThenRunCommand() async {
+  Future<Null> verifyThenRunCommand() async {
     commandValidator();
     device = await findTargetDevice();
     if (device == null)
@@ -157,7 +157,7 @@
   }
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
 
     Cache.releaseLockEarly();
 
@@ -238,6 +238,5 @@
     );
     if (result != 0)
       throwToolExit(null, exitCode: result);
-    return 0;
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/screenshot.dart b/packages/flutter_tools/lib/src/commands/screenshot.dart
index 2e793e6..f2208e5 100644
--- a/packages/flutter_tools/lib/src/commands/screenshot.dart
+++ b/packages/flutter_tools/lib/src/commands/screenshot.dart
@@ -52,7 +52,7 @@
   Device device;
 
   @override
-  Future<int> verifyThenRunCommand() async {
+  Future<Null> verifyThenRunCommand() async {
     if (argResults[_kSkia] != null) {
       if (argResults[_kOut] != null && argResults[_kSkiaServe] != null)
         throwToolExit('Cannot specify both --$_kOut and --$_kSkiaServe');
@@ -69,7 +69,7 @@
   }
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     File outputFile;
     if (argResults.wasParsed(_kOut))
       outputFile = new File(argResults[_kOut]);
@@ -81,7 +81,7 @@
     }
   }
 
-  Future<int> runScreenshot(File outputFile) async {
+  Future<Null> runScreenshot(File outputFile) async {
     outputFile ??= getUniqueFile(Directory.current, 'flutter', 'png');
     try {
       if (!await device.takeScreenshot(outputFile))
@@ -90,10 +90,9 @@
       throwToolExit('Error taking screenshot: $error');
     }
     await showOutputFileInfo(outputFile);
-    return 0;
   }
 
-  Future<int> runSkia(File outputFile) async {
+  Future<Null> runSkia(File outputFile) async {
     Uri skpUri = new Uri(scheme: 'http', host: '127.0.0.1',
         port: int.parse(argResults[_kSkia]),
         path: '/skp');
@@ -136,7 +135,6 @@
           throwToolExit('\nIt appears the output file contains an error message, not valid skia output.\n\n$errorHelpText');
       }
     }
-    return 0;
   }
 
   Future<Null> showOutputFileInfo(File outputFile) async {
diff --git a/packages/flutter_tools/lib/src/commands/setup.dart b/packages/flutter_tools/lib/src/commands/setup.dart
index b2209bc..4f3b0cf 100644
--- a/packages/flutter_tools/lib/src/commands/setup.dart
+++ b/packages/flutter_tools/lib/src/commands/setup.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 
+import '../base/common.dart';
 import '../base/os.dart';
 import '../base/process.dart';
 import '../globals.dart';
@@ -26,7 +27,7 @@
   final bool hidden;
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     printStatus('Running Flutter setup...');
 
     // setup brew on mac
@@ -57,9 +58,9 @@
       printStatus('\nThe flutter command is available on the path.');
     }
 
-    if (goodInstall)
-      printStatus('\nFlutter setup complete!');
+    if (!goodInstall)
+      throwToolExit(null);
 
-    return goodInstall ? 0 : 1;
+    printStatus('\nFlutter setup complete!');
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/stop.dart b/packages/flutter_tools/lib/src/commands/stop.dart
index e2d56cd..4a01ad2 100644
--- a/packages/flutter_tools/lib/src/commands/stop.dart
+++ b/packages/flutter_tools/lib/src/commands/stop.dart
@@ -5,6 +5,7 @@
 import 'dart:async';
 
 import '../application_package.dart';
+import '../base/common.dart';
 import '../build_info.dart';
 import '../device.dart';
 import '../globals.dart';
@@ -20,24 +21,23 @@
   Device device;
 
   @override
-  Future<int> verifyThenRunCommand() async {
-    if (!commandValidator())
-      return 1;
+  Future<Null> verifyThenRunCommand() async {
+    commandValidator();
     device = await findTargetDevice();
     if (device == null)
-      return 1;
+      throwToolExit(null);
     return super.verifyThenRunCommand();
   }
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     ApplicationPackage app = applicationPackages.getPackageForPlatform(device.platform);
     if (app == null) {
       String platformName = getNameForTargetPlatform(device.platform);
-      printError('No Flutter application for $platformName found in the current directory.');
-      return 1;
+      throwToolExit('No Flutter application for $platformName found in the current directory.');
     }
     printStatus('Stopping apps on ${device.name}.');
-    return await device.stopApp(app) ? 0 : 1;
+    if (!await device.stopApp(app))
+      throwToolExit(null);
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart
index 17e6556..cf421cd 100644
--- a/packages/flutter_tools/lib/src/commands/test.dart
+++ b/packages/flutter_tools/lib/src/commands/test.dart
@@ -8,6 +8,7 @@
 import 'package:path/path.dart' as path;
 import 'package:test/src/executable.dart' as executable; // ignore: implementation_imports
 
+import '../base/common.dart';
 import '../base/logger.dart';
 import '../base/os.dart';
 import '../cache.dart';
@@ -38,14 +39,12 @@
     );
     commandValidator = () {
       if (!FileSystemEntity.isFileSync('pubspec.yaml')) {
-        printError(
+        throwToolExit(
           'Error: No pubspec.yaml file found in the current working directory.\n'
           'Run this command from the root of your project. Test files must be\n'
           'called *_test.dart and must reside in the package\'s \'test\'\n'
           'directory (or one of its subdirectories).');
-        return false;
       }
-      return true;
     };
   }
 
@@ -142,20 +141,17 @@
   }
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     List<String> testArgs = argResults.rest.map((String testPath) => path.absolute(testPath)).toList();
 
-    if (!commandValidator())
-      return 1;
+    commandValidator();
 
     Directory testDir;
 
     if (testArgs.isEmpty) {
       testDir = _currentPackageTestDir;
-      if (!testDir.existsSync()) {
-        printError("Test directory '${testDir.path}' not found.");
-        return 1;
-      }
+      if (!testDir.existsSync())
+        throwToolExit("Test directory '${testDir.path}' not found.");
 
       testArgs.addAll(_findTests(testDir));
     }
@@ -169,10 +165,8 @@
 
     loader.installHook();
     loader.shellPath = tools.getHostToolPath(HostTool.SkyShell);
-    if (!FileSystemEntity.isFileSync(loader.shellPath)) {
-        printError('Cannot find Flutter shell at ${loader.shellPath}');
-      return 1;
-    }
+    if (!FileSystemEntity.isFileSync(loader.shellPath))
+      throwToolExit('Cannot find Flutter shell at ${loader.shellPath}');
 
     Cache.releaseLockEarly();
 
@@ -183,9 +177,10 @@
 
     if (collector.enabled) {
       if (!await _collectCoverageData(collector, mergeCoverageData: argResults['merge-coverage']))
-        return 1;
+        throwToolExit(null);
     }
 
-    return result;
+    if (result != 0)
+      throwToolExit(null);
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/trace.dart b/packages/flutter_tools/lib/src/commands/trace.dart
index 886e828..7464020 100644
--- a/packages/flutter_tools/lib/src/commands/trace.dart
+++ b/packages/flutter_tools/lib/src/commands/trace.dart
@@ -46,14 +46,13 @@
     'with --start and later with --stop.';
 
   @override
-  Future<int> verifyThenRunCommand() async {
-    if (!commandValidator())
-      return 1;
+  Future<Null> verifyThenRunCommand() async {
+    commandValidator();
     return super.verifyThenRunCommand();
   }
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     int observatoryPort = int.parse(argResults['debug-port']);
 
     Tracing tracing;
@@ -61,8 +60,7 @@
     try {
       tracing = await Tracing.connect(observatoryPort);
     } catch (error) {
-      printError('Error connecting to observatory: $error');
-      return 1;
+      throwToolExit('Error connecting to observatory: $error');
     }
 
     Cache.releaseLockEarly();
@@ -81,8 +79,6 @@
     } else {
       await tracing.startTracing();
     }
-
-    return 0;
   }
 
   Future<Null> _stopTracing(Tracing tracing) async {
diff --git a/packages/flutter_tools/lib/src/commands/update_packages.dart b/packages/flutter_tools/lib/src/commands/update_packages.dart
index 6e5b845..30a869f 100644
--- a/packages/flutter_tools/lib/src/commands/update_packages.dart
+++ b/packages/flutter_tools/lib/src/commands/update_packages.dart
@@ -46,24 +46,19 @@
   }
 
   @override
-  Future<int> runCommand() async {
-    try {
-      final Stopwatch timer = new Stopwatch()..start();
-      int count = 0;
-      final bool upgrade = argResults['upgrade'];
+  Future<Null> runCommand() async {
+    final Stopwatch timer = new Stopwatch()..start();
+    int count = 0;
+    final bool upgrade = argResults['upgrade'];
 
-      for (Directory dir in runner.getRepoPackages()) {
-        await pubGet(directory: dir.path, upgrade: upgrade, checkLastModified: false);
-        count++;
-      }
-
-      await _downloadCoverageData();
-
-      final double seconds = timer.elapsedMilliseconds / 1000.0;
-      printStatus('\nRan \'pub\' $count time${count == 1 ? "" : "s"} and fetched coverage data in ${seconds.toStringAsFixed(1)}s.');
-      return 0;
-    } on int catch (code) {
-      return code;
+    for (Directory dir in runner.getRepoPackages()) {
+      await pubGet(directory: dir.path, upgrade: upgrade, checkLastModified: false);
+      count++;
     }
+
+    await _downloadCoverageData();
+
+    final double seconds = timer.elapsedMilliseconds / 1000.0;
+    printStatus('\nRan \'pub\' $count time${count == 1 ? "" : "s"} and fetched coverage data in ${seconds.toStringAsFixed(1)}s.');
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/upgrade.dart b/packages/flutter_tools/lib/src/commands/upgrade.dart
index 3f1913f..2e5c8d0 100644
--- a/packages/flutter_tools/lib/src/commands/upgrade.dart
+++ b/packages/flutter_tools/lib/src/commands/upgrade.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 
+import '../base/common.dart';
 import '../base/os.dart';
 import '../base/process.dart';
 import '../dart/pub.dart';
@@ -21,14 +22,13 @@
   final String description = 'Upgrade your copy of Flutter.';
 
   @override
-  Future<int> runCommand() async {
+  Future<Null> runCommand() async {
     try {
       runCheckedSync(<String>[
         'git', 'rev-parse', '@{u}'
       ], workingDirectory: Cache.flutterRoot);
     } catch (e) {
-      printError('Unable to upgrade Flutter: no upstream repository configured.');
-      return 1;
+      throwToolExit('Unable to upgrade Flutter: no upstream repository configured.');
     }
 
     FlutterVersion version = new FlutterVersion(Cache.flutterRoot);
@@ -48,7 +48,7 @@
     );
 
     if (code != 0)
-      return code;
+      throwToolExit(null, exitCode: code);
 
     await buildUnlinkedForPackages(Cache.flutterRoot);
 
@@ -79,8 +79,6 @@
     printStatus('');
     printStatus('Running flutter doctor...');
     await doctor.diagnose();
-
-    return 0;
   }
 
   //  dev/benchmarks/complex_layout/lib/main.dart        |  24 +-
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index d2083bd..e38c53c 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -9,6 +9,7 @@
 import 'package:meta/meta.dart';
 
 import '../application_package.dart';
+import '../base/common.dart';
 import '../build_info.dart';
 import '../dart/package_map.dart';
 import '../dart/pub.dart';
@@ -18,9 +19,9 @@
 import '../usage.dart';
 import 'flutter_command_runner.dart';
 
-typedef bool Validator();
+typedef void Validator();
 
-abstract class FlutterCommand extends Command {
+abstract class FlutterCommand extends Command<Null> {
   FlutterCommand() {
     commandValidator = commonCommandValidator;
   }
@@ -107,18 +108,17 @@
   /// and [runCommand] to execute the command
   /// so that this method can record and report the overall time to analytics.
   @override
-  Future<int> run() {
+  Future<Null> run() {
     Stopwatch stopwatch = new Stopwatch()..start();
     UsageTimer analyticsTimer = usagePath == null ? null : flutterUsage.startTimer(name);
 
     if (flutterUsage.isFirstRun)
       flutterUsage.printUsage();
 
-    return verifyThenRunCommand().then((int exitCode) {
+    return verifyThenRunCommand().whenComplete(() {
       int ms = stopwatch.elapsedMilliseconds;
-      printTrace("'flutter $name' took ${ms}ms; exiting with code $exitCode.");
+      printTrace("'flutter $name' took ${ms}ms.");
       analyticsTimer?.finish();
-      return exitCode;
     });
   }
 
@@ -130,7 +130,7 @@
   /// then call this method to execute the command
   /// rather than calling [runCommand] directly.
   @mustCallSuper
-  Future<int> verifyThenRunCommand() async {
+  Future<Null> verifyThenRunCommand() async {
     // Populate the cache. We call this before pub get below so that the sky_engine
     // package is available in the flutter cache for pub to find.
     await cache.updateAll();
@@ -144,11 +144,11 @@
     if (commandPath != null)
       flutterUsage.sendCommand(usagePath);
 
-    return await runCommand();
+    await runCommand();
   }
 
   /// Subclasses must implement this to execute the command.
-  Future<int> runCommand();
+  Future<Null> runCommand();
 
   /// Find and return the target [Device] based upon currently connected
   /// devices and criteria entered by the user on the command line.
@@ -203,35 +203,28 @@
   // This is a field so that you can modify the value for testing.
   Validator commandValidator;
 
-  bool commonCommandValidator() {
+  void commonCommandValidator() {
     if (!PackageMap.isUsingCustomPackagesPath) {
       // Don't expect a pubspec.yaml file if the user passed in an explicit .packages file path.
       if (!FileSystemEntity.isFileSync('pubspec.yaml')) {
-        printError('Error: No pubspec.yaml file found.\n'
+        throw new ToolExit('Error: No pubspec.yaml file found.\n'
           'This command should be run from the root of your Flutter project.\n'
           'Do not run this command from the root of your git clone of Flutter.');
-        return false;
       }
     }
 
     if (_usesTargetOption) {
       String targetPath = targetFile;
-      if (!FileSystemEntity.isFileSync(targetPath)) {
-        printError('Target file "$targetPath" not found.');
-        return false;
-      }
+      if (!FileSystemEntity.isFileSync(targetPath))
+        throw new ToolExit('Target file "$targetPath" not found.');
     }
 
     // Validate the current package map only if we will not be running "pub get" later.
     if (!(_usesPubOption && argResults['pub'])) {
       String error = new PackageMap(PackageMap.globalPackagesPath).checkValid();
-      if (error != null) {
-        printError(error);
-        return false;
-      }
+      if (error != null)
+        throw new ToolExit(error);
     }
-
-    return true;
   }
 
   ApplicationPackageStore applicationPackages;
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
index e592d4f..8edc60b 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
@@ -25,7 +25,7 @@
 const String kFlutterToolsScriptFileName = 'flutter_tools.dart'; // in //flutter/packages/flutter_tools/bin/
 const String kFlutterEnginePackageName = 'sky_engine';
 
-class FlutterCommandRunner extends CommandRunner {
+class FlutterCommandRunner extends CommandRunner<Null> {
   FlutterCommandRunner({ bool verboseHelp: false }) : super(
     'flutter',
     'Manage your Flutter app development.\n'
@@ -124,7 +124,7 @@
   }
 
   @override
-  Future<dynamic> run(Iterable<String> args) {
+  Future<Null> run(Iterable<String> args) {
     // Have an invocation of 'build' print out it's sub-commands.
     if (args.length == 1 && args.first == 'build')
       args = <String>['build', '-h'];
@@ -133,7 +133,7 @@
   }
 
   @override
-  Future<int> runCommand(ArgResults globalResults) async {
+  Future<Null> runCommand(ArgResults globalResults) async {
     // Check for verbose.
     if (globalResults['verbose'])
       context[Logger] = new VerboseLogger();
@@ -153,8 +153,7 @@
     if (globalResults['suppress-analytics'])
       flutterUsage.suppressAnalytics = true;
 
-    if (!_checkFlutterCopy())
-      return 1;
+    _checkFlutterCopy();
 
     if (globalResults.wasParsed('packages'))
       PackageMap.globalPackagesPath = path.normalize(path.absolute(globalResults['packages']));
@@ -176,10 +175,10 @@
     if (globalResults['version']) {
       flutterUsage.sendCommand('version');
       printStatus(FlutterVersion.getVersion(Cache.flutterRoot).toString());
-      return 0;
+      return;
     }
 
-    return await super.runCommand(globalResults);
+    await super.runCommand(globalResults);
   }
 
   String _tryEnginePath(String enginePath) {
@@ -285,7 +284,7 @@
     return result;
   }
 
-  bool _checkFlutterCopy() {
+  void _checkFlutterCopy() {
     // If the current directory is contained by a flutter repo, check that it's
     // the same flutter that is currently running.
     String directory = path.normalize(path.absolute(Directory.current.path));
@@ -334,8 +333,6 @@
         }
       }
     }
-
-    return true;
   }
 
   // Check if `bin/flutter` and `bin/cache/engine.stamp` exist.
diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml
index 7b867fb..b716b7d 100644
--- a/packages/flutter_tools/pubspec.yaml
+++ b/packages/flutter_tools/pubspec.yaml
@@ -9,12 +9,7 @@
 
 dependencies:
   archive: ^1.0.20
-
-  # version 0.13.6+1 does not return the value from the command
-  # causing flutter tools to always have exit code 0
-  # see https://github.com/flutter/flutter/issues/6766
-  args: 0.13.6
-
+  args: ^0.13.4
   coverage: ^0.8.0
   crypto: '>=1.1.1 <3.0.0'
   file: ^0.1.0
diff --git a/packages/flutter_tools/test/analytics_test.dart b/packages/flutter_tools/test/analytics_test.dart
index 06536a3..789d7fc 100644
--- a/packages/flutter_tools/test/analytics_test.dart
+++ b/packages/flutter_tools/test/analytics_test.dart
@@ -39,22 +39,19 @@
 
       flutterUsage.enabled = false;
       CreateCommand command = new CreateCommand();
-      CommandRunner runner = createTestCommandRunner(command);
-      int code = await runner.run(<String>['create', '--no-pub', temp.path]);
-      expect(code, 0);
+      CommandRunner<Null> runner = createTestCommandRunner(command);
+      await runner.run(<String>['create', '--no-pub', temp.path]);
       expect(count, 0);
 
       flutterUsage.enabled = true;
-      code = await runner.run(<String>['create', '--no-pub', temp.path]);
-      expect(code, 0);
+      await runner.run(<String>['create', '--no-pub', temp.path]);
       expect(count, flutterUsage.isFirstRun ? 0 : 2);
 
       count = 0;
       flutterUsage.enabled = false;
       DoctorCommand doctorCommand = new DoctorCommand();
       runner = createTestCommandRunner(doctorCommand);
-      code = await runner.run(<String>['doctor']);
-      expect(code, 0);
+      await runner.run(<String>['doctor']);
       expect(count, 0);
     }, overrides: <Type, dynamic>{
       Usage: new Usage()
@@ -67,7 +64,7 @@
 
       flutterUsage.enabled = false;
       ConfigCommand command = new ConfigCommand();
-      CommandRunner runner = createTestCommandRunner(command);
+      CommandRunner<Null> runner = createTestCommandRunner(command);
       await runner.run(<String>['config']);
       expect(count, 0);
 
diff --git a/packages/flutter_tools/test/analyze_duplicate_names_test.dart b/packages/flutter_tools/test/analyze_duplicate_names_test.dart
index 1de19c6..7fbe33e 100644
--- a/packages/flutter_tools/test/analyze_duplicate_names_test.dart
+++ b/packages/flutter_tools/test/analyze_duplicate_names_test.dart
@@ -37,8 +37,7 @@
       applyMocksToCommand(command);
       return createTestCommandRunner(command).run(
         <String>['analyze', '--no-current-package', '--no-current-directory', dartFileA.path, dartFileB.path]
-      ).then((int code) {
-        expect(code, 0);
+      ).then((_) {
         expect(testLogger.statusText, startsWith('Analyzing 2 files...\nNo analyzer warnings!'));
       });
 
diff --git a/packages/flutter_tools/test/channel_test.dart b/packages/flutter_tools/test/channel_test.dart
index 15d7367..987fba2 100644
--- a/packages/flutter_tools/test/channel_test.dart
+++ b/packages/flutter_tools/test/channel_test.dart
@@ -3,8 +3,6 @@
 // found in the LICENSE file.
 
 import 'package:args/command_runner.dart';
-import 'package:flutter_tools/src/base/context.dart';
-import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/commands/channel.dart';
 import 'package:test/test.dart';
 
@@ -15,14 +13,13 @@
   group('channel', () {
     testUsingContext('list', () async {
       ChannelCommand command = new ChannelCommand();
-      CommandRunner runner = createTestCommandRunner(command);
-      expect(await runner.run(<String>['channel']), 0);
-      BufferLogger logger = context[Logger];
-      expect(logger.errorText, hasLength(0));
+      CommandRunner<Null> runner = createTestCommandRunner(command);
+      await runner.run(<String>['channel']);
+      expect(testLogger.errorText, hasLength(0));
       // The bots may return an empty list of channels (network hiccup?)
       // and when run locally the list of branches might be different
       // so we check for the header text rather than any specific channel name.
-      expect(logger.statusText, contains('Flutter channels:'));
+      expect(testLogger.statusText, contains('Flutter channels:'));
     });
   });
 }
diff --git a/packages/flutter_tools/test/create_test.dart b/packages/flutter_tools/test/create_test.dart
index 399f05e..7a8760b 100644
--- a/packages/flutter_tools/test/create_test.dart
+++ b/packages/flutter_tools/test/create_test.dart
@@ -43,10 +43,9 @@
       Cache.flutterRoot = '../..';
 
       CreateCommand command = new CreateCommand();
-      CommandRunner runner = createTestCommandRunner(command);
+      CommandRunner<Null> runner = createTestCommandRunner(command);
 
-      int code = await runner.run(<String>['create', '--no-pub', temp.path]);
-      expect(code, 0);
+      await runner.run(<String>['create', '--no-pub', temp.path]);
 
       void expectExists(String relPath) {
         expect(FileSystemEntity.isFileSync('${temp.path}/$relPath'), true);
@@ -74,13 +73,11 @@
       Cache.flutterRoot = '../..';
 
       CreateCommand command = new CreateCommand();
-      CommandRunner runner = createTestCommandRunner(command);
+      CommandRunner<Null> runner = createTestCommandRunner(command);
 
-      int code = await runner.run(<String>['create', '--no-pub', temp.path]);
-      expect(code, 0);
+      await runner.run(<String>['create', '--no-pub', temp.path]);
 
-      code = await runner.run(<String>['create', '--no-pub', temp.path]);
-      expect(code, 0);
+      await runner.run(<String>['create', '--no-pub', temp.path]);
     });
 
     // Verify that we help the user correct an option ordering issue
@@ -88,7 +85,7 @@
       Cache.flutterRoot = '../..';
 
       CreateCommand command = new CreateCommand();
-      CommandRunner runner = createTestCommandRunner(command);
+      CommandRunner<Null> runner = createTestCommandRunner(command);
 
       try {
         await runner.run(<String>['create', temp.path, '--pub']);
@@ -103,7 +100,7 @@
     testUsingContext('fails when file exists', () async {
       Cache.flutterRoot = '../..';
       CreateCommand command = new CreateCommand();
-      CommandRunner runner = createTestCommandRunner(command);
+      CommandRunner<Null> runner = createTestCommandRunner(command);
       File existingFile = new File("${temp.path.toString()}/bad");
       if (!existingFile.existsSync()) existingFile.createSync();
       try {
@@ -119,12 +116,11 @@
 Future<Null> _createAndAnalyzeProject(Directory dir, List<String> createArgs) async {
   Cache.flutterRoot = '../..';
   CreateCommand command = new CreateCommand();
-  CommandRunner runner = createTestCommandRunner(command);
+  CommandRunner<Null> runner = createTestCommandRunner(command);
   List<String> args = <String>['create'];
   args.addAll(createArgs);
   args.add(dir.path);
-  int code = await runner.run(args);
-  expect(code, 0);
+  await runner.run(args);
 
   String mainPath = path.join(dir.path, 'lib', 'main.dart');
   expect(new File(mainPath).existsSync(), true);
diff --git a/packages/flutter_tools/test/devices_test.dart b/packages/flutter_tools/test/devices_test.dart
index 25009e4..f6f6387 100644
--- a/packages/flutter_tools/test/devices_test.dart
+++ b/packages/flutter_tools/test/devices_test.dart
@@ -12,19 +12,15 @@
 
 void main() {
   group('devices', () {
-    testUsingContext('returns 0 when called', () {
+    testUsingContext('returns 0 when called', () async {
       DevicesCommand command = new DevicesCommand();
-      return createTestCommandRunner(command).run(<String>['devices']).then((int code) {
-        expect(code, 0);
-      });
+      await createTestCommandRunner(command).run(<String>['devices']);
     });
 
-    testUsingContext('no error when no connected devices', () {
+    testUsingContext('no error when no connected devices', () async {
       DevicesCommand command = new DevicesCommand();
-      return createTestCommandRunner(command).run(<String>['devices']).then((int code) {
-        expect(code, 0);
-        expect(testLogger.statusText, contains('No devices detected'));
-      });
+      await createTestCommandRunner(command).run(<String>['devices']);
+      expect(testLogger.statusText, contains('No devices detected'));
     }, overrides: <Type, dynamic>{
       AndroidSdk: null,
       DeviceManager: new DeviceManager()
diff --git a/packages/flutter_tools/test/drive_test.dart b/packages/flutter_tools/test/drive_test.dart
index 72c3019..4db7bad 100644
--- a/packages/flutter_tools/test/drive_test.dart
+++ b/packages/flutter_tools/test/drive_test.dart
@@ -8,11 +8,9 @@
 import 'package:flutter_tools/src/android/android_device.dart';
 import 'package:flutter_tools/src/base/common.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
-import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/os.dart';
 import 'package:flutter_tools/src/commands/drive.dart';
 import 'package:flutter_tools/src/device.dart';
-import 'package:flutter_tools/src/globals.dart';
 import 'package:flutter_tools/src/ios/simulators.dart';
 import 'package:mockito/mockito.dart';
 import 'package:test/test.dart';
@@ -164,11 +162,8 @@
         'drive',
         '--target=$testApp',
       ];
-      return createTestCommandRunner(command).run(args).then((int code) {
-        expect(code, 0);
-        BufferLogger buffer = logger;
-        expect(buffer.errorText, isEmpty);
-      });
+      await createTestCommandRunner(command).run(args);
+      expect(testLogger.errorText, isEmpty);
     });
 
     testUsingContext('returns exitCode set by test runner', () async {
diff --git a/packages/flutter_tools/test/format_test.dart b/packages/flutter_tools/test/format_test.dart
index f7aacc5..d339e39 100644
--- a/packages/flutter_tools/test/format_test.dart
+++ b/packages/flutter_tools/test/format_test.dart
@@ -30,10 +30,9 @@
 
     Future<Null> createProject() async {
       CreateCommand command = new CreateCommand();
-      CommandRunner runner = createTestCommandRunner(command);
+      CommandRunner<Null> runner = createTestCommandRunner(command);
 
-      int code = await runner.run(<String>['create', '--no-pub', temp.path]);
-      expect(code, 0);
+      await runner.run(<String>['create', '--no-pub', temp.path]);
     }
 
     testUsingContext('a file', () async {
@@ -44,9 +43,8 @@
       srcFile.writeAsStringSync(original.replaceFirst('main()', 'main(  )'));
 
       FormatCommand command = new FormatCommand();
-      CommandRunner runner = createTestCommandRunner(command);
-      int code = await runner.run(<String>['format', srcFile.path]);
-      expect(code, 0);
+      CommandRunner<Null> runner = createTestCommandRunner(command);
+      await runner.run(<String>['format', srcFile.path]);
 
       String formatted = srcFile.readAsStringSync();
       expect(formatted, original);
diff --git a/packages/flutter_tools/test/install_test.dart b/packages/flutter_tools/test/install_test.dart
index 60048ba..d4f66be 100644
--- a/packages/flutter_tools/test/install_test.dart
+++ b/packages/flutter_tools/test/install_test.dart
@@ -12,7 +12,7 @@
 
 void main() {
   group('install', () {
-    testUsingContext('returns 0 when Android is connected and ready for an install', () {
+    testUsingContext('returns 0 when Android is connected and ready for an install', () async {
       InstallCommand command = new InstallCommand();
       applyMocksToCommand(command);
 
@@ -21,12 +21,10 @@
       when(device.installApp(any)).thenReturn(true);
       testDeviceManager.addDevice(device);
 
-      return createTestCommandRunner(command).run(<String>['install']).then((int code) {
-        expect(code, 0);
-      });
+      await createTestCommandRunner(command).run(<String>['install']);
     });
 
-    testUsingContext('returns 0 when iOS is connected and ready for an install', () {
+    testUsingContext('returns 0 when iOS is connected and ready for an install', () async {
       InstallCommand command = new InstallCommand();
       applyMocksToCommand(command);
 
@@ -35,9 +33,7 @@
       when(device.installApp(any)).thenReturn(true);
       testDeviceManager.addDevice(device);
 
-      return createTestCommandRunner(command).run(<String>['install']).then((int code) {
-        expect(code, 0);
-      });
+      await createTestCommandRunner(command).run(<String>['install']);
     });
   });
 }
diff --git a/packages/flutter_tools/test/packages_test.dart b/packages/flutter_tools/test/packages_test.dart
index 9f84458..8792b8d 100644
--- a/packages/flutter_tools/test/packages_test.dart
+++ b/packages/flutter_tools/test/packages_test.dart
@@ -27,20 +27,18 @@
 
     Future<Null> createProject() async {
       CreateCommand command = new CreateCommand();
-      CommandRunner runner = createTestCommandRunner(command);
+      CommandRunner<Null> runner = createTestCommandRunner(command);
 
-      int code = await runner.run(<String>['create', '--no-pub', temp.path]);
-      expect(code, 0);
+      await runner.run(<String>['create', '--no-pub', temp.path]);
     }
 
     Future<Null> runCommand(String verb) async {
       await createProject();
 
       PackagesCommand command = new PackagesCommand();
-      CommandRunner runner = createTestCommandRunner(command);
+      CommandRunner<Null> runner = createTestCommandRunner(command);
 
-      int code = await runner.run(<String>['packages', verb, temp.path]);
-      expect(code, 0);
+      await runner.run(<String>['packages', verb, temp.path]);
     }
 
     void expectExists(String relPath) {
diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart
index 021d598..f85f2d9 100644
--- a/packages/flutter_tools/test/src/common.dart
+++ b/packages/flutter_tools/test/src/common.dart
@@ -6,7 +6,7 @@
 import 'package:flutter_tools/src/runner/flutter_command.dart';
 import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
 
-CommandRunner createTestCommandRunner([FlutterCommand command]) {
+CommandRunner<Null> createTestCommandRunner([FlutterCommand command]) {
   FlutterCommandRunner runner  = new FlutterCommandRunner();
   if (command != null)
     runner.addCommand(command);
diff --git a/packages/flutter_tools/test/stop_test.dart b/packages/flutter_tools/test/stop_test.dart
index 16b5c6d..69d9250 100644
--- a/packages/flutter_tools/test/stop_test.dart
+++ b/packages/flutter_tools/test/stop_test.dart
@@ -14,27 +14,23 @@
 
 void main() {
   group('stop', () {
-    testUsingContext('returns 0 when Android is connected and ready to be stopped', () {
+    testUsingContext('returns 0 when Android is connected and ready to be stopped', () async {
       StopCommand command = new StopCommand();
       applyMocksToCommand(command);
       MockAndroidDevice device = new MockAndroidDevice();
       when(device.stopApp(any)).thenReturn(new Future<bool>.value(true));
       testDeviceManager.addDevice(device);
-      return createTestCommandRunner(command).run(<String>['stop']).then((int code) {
-        expect(code, 0);
-      });
+      await createTestCommandRunner(command).run(<String>['stop']);
     });
 
-    testUsingContext('returns 0 when iOS is connected and ready to be stopped', () {
+    testUsingContext('returns 0 when iOS is connected and ready to be stopped', () async {
       StopCommand command = new StopCommand();
       applyMocksToCommand(command);
       MockIOSDevice device = new MockIOSDevice();
       when(device.stopApp(any)).thenReturn(new Future<bool>.value(true));
       testDeviceManager.addDevice(device);
 
-      return createTestCommandRunner(command).run(<String>['stop']).then((int code) {
-        expect(code, 0);
-      });
+      await createTestCommandRunner(command).run(<String>['stop']);
     });
   });
 }
diff --git a/packages/flutter_tools/test/trace_test.dart b/packages/flutter_tools/test/trace_test.dart
index d3e9e2a..3befcde 100644
--- a/packages/flutter_tools/test/trace_test.dart
+++ b/packages/flutter_tools/test/trace_test.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:flutter_tools/src/base/common.dart';
 import 'package:flutter_tools/src/commands/trace.dart';
 import 'package:test/test.dart';
 
@@ -11,12 +12,15 @@
 
 void main() {
   group('trace', () {
-    testUsingContext('returns 1 when no Android device is connected', () {
+    testUsingContext('returns 1 when no Android device is connected', () async {
       TraceCommand command = new TraceCommand();
       applyMocksToCommand(command);
-      return createTestCommandRunner(command).run(<String>['trace']).then((int code) {
-        expect(code, 1);
-      });
+      try {
+        await createTestCommandRunner(command).run(<String>['trace']);
+        fail('Exception expected');
+      } on ToolExit catch (e) {
+        expect(e.exitCode ?? 1, 1);
+      }
     });
   });
 }
diff --git a/packages/flutter_tools/test/upgrade_test.dart b/packages/flutter_tools/test/upgrade_test.dart
index 0fa11f8..8de04f2 100644
--- a/packages/flutter_tools/test/upgrade_test.dart
+++ b/packages/flutter_tools/test/upgrade_test.dart
@@ -49,9 +49,8 @@
 
       Future<Null> createProject() async {
         CreateCommand command = new CreateCommand();
-        CommandRunner runner = createTestCommandRunner(command);
-        int code = await runner.run(<String>['create', '--no-pub', temp.path]);
-        expect(code, 0);
+        CommandRunner<Null> runner = createTestCommandRunner(command);
+        await runner.run(<String>['create', '--no-pub', temp.path]);
       }
 
       testUsingContext('in project', () async {