Update the roll dev script to actually work (#14376)

diff --git a/dev/tools/lib/archive_publisher.dart b/dev/tools/lib/archive_publisher.dart
index 4ea085e..55481e3 100644
--- a/dev/tools/lib/archive_publisher.dart
+++ b/dev/tools/lib/archive_publisher.dart
@@ -22,7 +22,7 @@
     }
     final String stderr = result?.stderr ?? '';
     if (stderr.isNotEmpty) {
-      output += ':\n$result.stderr';
+      output += ':\n${result.stderr}';
     }
     return output;
   }
@@ -87,9 +87,6 @@
   /// Publishes the archive for the given constructor parameters.
   bool publishArchive() {
     assert(channel == Channel.dev, 'Channel must be dev (beta not yet supported)');
-    // Check for access early so that we don't try to publish things if the
-    // user doesn't have access to the metadata file.
-    _checkForGSUtilAccess();
     final List<String> platforms = <String>['linux', 'mac', 'win'];
     final Map<String, String> metadata = <String, String>{};
     for (String platform in platforms) {
@@ -108,12 +105,14 @@
 
   /// Checks to make sure the user has access to the Google Storage bucket
   /// required to publish. Will throw an [ArchivePublisherException] if not.
-  void _checkForGSUtilAccess() {
+  void checkForGSUtilAccess() {
     // Fetching ACLs requires FULL_CONTROL access.
     final ProcessResult result = _runGsUtil(<String>['acl', 'get', metadataGsPath]);
     if (result.exitCode != 0) {
       throw new ArchivePublisherException(
-          'GSUtil cannot get ACLs for metadata file $metadataGsPath', result);
+        'GSUtil cannot get ACLs for metadata file $metadataGsPath',
+        result,
+      );
     }
   }
 
diff --git a/dev/tools/lib/roll_dev.dart b/dev/tools/lib/roll_dev.dart
index 4672435..bba0de3 100644
--- a/dev/tools/lib/roll_dev.dart
+++ b/dev/tools/lib/roll_dev.dart
@@ -16,13 +16,12 @@
 const String kX = 'x';
 const String kY = 'y';
 const String kZ = 'z';
+const String kCommit = 'commit';
 const String kHelp = 'help';
 
-void main(List<String> args) {
-  // Set the cwd to the repo root, since we know where this script is located.
-  final Directory scriptLocation = new Directory(Platform.script.toFilePath());
-  Directory.current = scriptLocation.parent.parent.parent;
+const String kUpstreamRemote = 'git@github.com:flutter/flutter.git';
 
+void main(List<String> args) {
   final ArgParser argParser = new ArgParser(allowTrailingOptions: false);
   argParser.addOption(
     kIncrement,
@@ -35,6 +34,12 @@
       kZ: 'Indicates the least notable level of change. You normally want this.',
     },
   );
+  argParser.addOption(
+    kCommit,
+    help: 'Specifies which git commit to roll to the dev branch.',
+    valueHelp: 'hash',
+    defaultsTo: 'upstream/master',
+  );
   argParser.addFlag(kHelp, negatable: false, help: 'Show this help message.', hide: true);
   ArgResults argResults;
   try {
@@ -46,14 +51,21 @@
   }
 
   final String level = argResults[kIncrement];
+  final bool commit = argResults[kCommit];
   final bool help = argResults[kHelp];
 
   if (help || level == null) {
-    print('roll_dev.dart --increment=x • update the version tags and roll a new dev build.\n');
+    print('roll_dev.dart --increment=level --commit=hash • update the version tags and roll a new dev build.\n');
     print(argParser.usage);
     exit(0);
   }
 
+  if (getGitOutput('remote get-url upstream', 'check whether this is a flutter checkout') != kUpstreamRemote) {
+    print('The current directory is not a Flutter repository checkout with a correctly configured upstream remote.');
+    print('For more details see: https://github.com/flutter/flutter/wiki/Release-process');
+    exit(1);
+  }
+
   runGit('checkout master', 'switch to master branch');
 
   if (getGitOutput('status --porcelain', 'check status of your local checkout') != '') {
@@ -62,6 +74,9 @@
     exit(1);
   }
 
+  runGit('fetch upstream', 'fetch upstream');
+  runGit('reset $commit --hard', 'check out master branch');
+
   String version = getFullTag();
   final Match match = parseFullTag(version);
   if (match == null) {
@@ -97,10 +112,24 @@
   }
   version = parts.join('.');
 
-  runGit('fetch upstream', 'fetch upstream');
-  runGit('reset upstream/master --hard', 'check out master branch');
+  final String hash = getGitOutput('rev-parse HEAD', 'Get git hash for $commit');
+
+  final ArchivePublisher publisher = new ArchivePublisher(hash, version, Channel.dev);
+
+  // Check for access early so that we don't try to publish things if the
+  // user doesn't have access to the metadata file.
+  try {
+    publisher.checkForGSUtilAccess();
+  } on ArchivePublisherException {
+    print('You do not appear to have the credentials required to update the archive links.');
+    print('Make sure you have "gsutil" installed, then run "gsutil config".');
+    print('Talk to @gspencergoog for details on which project to use.');
+    exit(1);
+  }
+
   runGit('tag v$version', 'tag the commit with the version label');
-  final String hash = getGitOutput('rev-parse HEAD', 'Get git hash for $version tag');
+
+  // PROMPT
 
   print('Your tree is ready to publish Flutter $version (${hash.substring(0, 10)}) '
     'to the "dev" channel.');
@@ -114,12 +143,12 @@
   // Publish the archive before pushing the tag so that if something fails in
   // the publish step, we can clean up.
   try {
-    new ArchivePublisher(hash, version, Channel.dev)..publishArchive();
+    publisher.publishArchive();
   } on ArchivePublisherException catch (e) {
     print('Archive publishing failed.\n$e');
-    runGit('tag -d $version', 'remove the tag that was not published');
+    runGit('tag -d v$version', 'remove the tag that was not published');
     print('The dev roll has been aborted.');
-    exit(0);
+    exit(1);
   }
 
   runGit('push upstream v$version', 'publish the version');