make flutter upgrade less verbose (#3237)

* more terse flutter upgrade

* fix an issue when updating and the engine update code has changed

* call flutter precache; add a --no-color hidden option

* fix a lint related to getters/setters
diff --git a/packages/flutter_tools/lib/src/base/logger.dart b/packages/flutter_tools/lib/src/base/logger.dart
index 4425aae..559d966 100644
--- a/packages/flutter_tools/lib/src/base/logger.dart
+++ b/packages/flutter_tools/lib/src/base/logger.dart
@@ -13,6 +13,10 @@
 
   String get separator => _sep;
 
+  set supportsColor(bool value) {
+    _terminal.supportsColor = value;
+  }
+
   /// Display an error level message to the user. Commands should use this if they
   /// fail in some way.
   void printError(String message, [StackTrace stackTrace]);
@@ -195,14 +199,13 @@
   _AnsiTerminal() {
     // TODO(devoncarew): This detection does not work for Windows.
     String term = Platform.environment['TERM'];
-    _supportsColor = term != null && term != 'dumb';
+    supportsColor = term != null && term != 'dumb';
   }
 
   static const String _bold = '\u001B[1m';
   static const String _reset = '\u001B[0m';
 
-  bool _supportsColor;
-  bool get supportsColor => _supportsColor;
+  bool supportsColor;
 
   String writeBold(String str) => supportsColor ? '$_bold$str$_reset' : str;
 }
diff --git a/packages/flutter_tools/lib/src/base/process.dart b/packages/flutter_tools/lib/src/base/process.dart
index 0108df3..e8465a0 100644
--- a/packages/flutter_tools/lib/src/base/process.dart
+++ b/packages/flutter_tools/lib/src/base/process.dart
@@ -15,7 +15,7 @@
 
 /// This runs the command in the background from the specified working
 /// directory. Completes when the process has been started.
-Future<Process> runCommand(List<String> cmd, {String workingDirectory}) async {
+Future<Process> runCommand(List<String> cmd, { String workingDirectory }) async {
   printTrace(cmd.join(' '));
   String executable = cmd[0];
   List<String> arguments = cmd.length > 1 ? cmd.sublist(1) : [];
diff --git a/packages/flutter_tools/lib/src/commands/upgrade.dart b/packages/flutter_tools/lib/src/commands/upgrade.dart
index 82fea11..562342c 100644
--- a/packages/flutter_tools/lib/src/commands/upgrade.dart
+++ b/packages/flutter_tools/lib/src/commands/upgrade.dart
@@ -20,42 +20,39 @@
   final String description = 'Upgrade your copy of Flutter.';
 
   @override
-  Validator projectRootValidator = () => true;
+  bool get requiresProjectRoot => false;
 
   @override
   Future<int> runInProject() async {
-    printStatus(FlutterVersion.getVersion(ArtifactStore.flutterRoot).toString());
-
     try {
       runCheckedSync(<String>[
         'git', 'rev-parse', '@{u}'
       ], workingDirectory: ArtifactStore.flutterRoot);
     } catch (e) {
-      printError('Unable to upgrade Flutter. No upstream repository configured for Flutter.');
+      printError('Unable to upgrade Flutter: no upstream repository configured.');
       return 1;
     }
 
-    printStatus('');
     printStatus('Upgrading Flutter...');
 
-    int code = await runCommandAndStreamOutput(<String>[
-      'git', 'pull', '--ff-only'
-    ], workingDirectory: ArtifactStore.flutterRoot);
+    int code = await runCommandAndStreamOutput(
+      <String>['git', 'pull', '--ff-only'],
+      workingDirectory: ArtifactStore.flutterRoot,
+      mapFunction: (String line) => matchesGitLine(line) ? null : line
+    );
 
     if (code != 0)
       return code;
 
-    // Causes us to update our locally cached packages.
+    // Check for and download any engine and pkg/ updates.
     printStatus('');
+    printStatus('Upgrading engine...');
     code = await runCommandAndStreamOutput(<String>[
-      'bin/flutter', '--version'
+      'bin/flutter', '--no-color', 'precache'
     ], workingDirectory: ArtifactStore.flutterRoot);
 
-    if (code != 0)
-      return code;
-
-    // Check for and download any engine updates.
-    await cache.updateAll();
+    printStatus('');
+    printStatus(FlutterVersion.getVersion(ArtifactStore.flutterRoot).toString());
 
     if (FileSystemEntity.isFileSync('pubspec.yaml')) {
       printStatus('');
@@ -67,4 +64,19 @@
 
     return 0;
   }
+
+  //  dev/benchmarks/complex_layout/lib/main.dart        |  24 +-
+  static final RegExp _gitDiffRegex = new RegExp(r' (\S+)\s+\|\s+\d+ [+-]+');
+
+  //  rename {packages/flutter/doc => dev/docs}/styles.html (92%)
+  //  delete mode 100644 doc/index.html
+  //  create mode 100644 examples/material_gallery/lib/gallery/demo.dart
+  static final RegExp _gitChangedRegex = new RegExp(r' (rename|delete mode|create mode) .+');
+
+  // Public for testing.
+  static bool matchesGitLine(String line) {
+    return _gitDiffRegex.hasMatch(line)
+      || _gitChangedRegex.hasMatch(line)
+      || line == 'Fast-forward';
+  }
 }
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index afe477d..043bc59 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -56,7 +56,7 @@
   /// Returns [SimControl] active in the current app context (i.e. zone).
   static SimControl get instance => context[SimControl] ?? (context[SimControl] = new SimControl());
 
-  Future<bool> boot({String deviceName}) async {
+  Future<bool> boot({ String deviceName }) async {
     if (_isAnyConnected())
       return true;
 
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 dead817..f493fe3 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
@@ -40,6 +40,10 @@
     argParser.addFlag('version',
         negatable: false,
         help: 'Reports the version of this tool.');
+    argParser.addFlag('color',
+        negatable: true,
+        hide: !verboseHelp,
+        help: 'Whether to use terminal colors.');
 
     String packagesHelp;
     if (FileSystemEntity.isFileSync('.packages'))
@@ -192,6 +196,9 @@
     if (globalResults['verbose'])
       context[Logger] = new VerboseLogger();
 
+    if (!globalResults['color'])
+      logger.supportsColor = false;
+
     // we must set ArtifactStore.flutterRoot early because other features use it
     // (e.g. enginePath's initialiser uses it)
     ArtifactStore.flutterRoot = path.normalize(path.absolute(globalResults['flutter-root']));
diff --git a/packages/flutter_tools/test/all.dart b/packages/flutter_tools/test/all.dart
index 43addc6..067e4ab 100644
--- a/packages/flutter_tools/test/all.dart
+++ b/packages/flutter_tools/test/all.dart
@@ -26,6 +26,7 @@
 import 'service_protocol_test.dart' as service_protocol_test;
 import 'stop_test.dart' as stop_test;
 import 'trace_test.dart' as trace_test;
+import 'upgrade_test.dart' as upgrade_test;
 
 void main() {
   adb_test.main();
@@ -47,4 +48,5 @@
   service_protocol_test.main();
   stop_test.main();
   trace_test.main();
+  upgrade_test.main();
 }
diff --git a/packages/flutter_tools/test/upgrade_test.dart b/packages/flutter_tools/test/upgrade_test.dart
new file mode 100644
index 0000000..5eef614
--- /dev/null
+++ b/packages/flutter_tools/test/upgrade_test.dart
@@ -0,0 +1,28 @@
+// Copyright 2016 The Chromium 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:flutter_tools/src/commands/upgrade.dart';
+import 'package:test/test.dart';
+
+void main() {
+  group('upgrade', () {
+    bool _match(String line) => UpgradeCommand.matchesGitLine(line);
+
+    test('regex match', () {
+      expect(_match(' .../material_gallery/lib/demo/buttons_demo.dart    | 10 +--'), true);
+      expect(_match(' dev/benchmarks/complex_layout/lib/main.dart        |  24 +-'), true);
+
+      expect(_match(' rename {packages/flutter/doc => dev/docs}/styles.html (92%)'), true);
+      expect(_match(' delete mode 100644 doc/index.html'), true);
+      expect(_match(' create mode 100644 examples/material_gallery/lib/gallery/demo.dart'), true);
+
+      expect(_match('Fast-forward'), true);
+    });
+
+    test('regex doesn\'t match', () {
+      expect(_match('Updating 79cfe1e..5046107'), false);
+      expect(_match('229 files changed, 6179 insertions(+), 3065 deletions(-)'), false);
+    });
+  });
+}