Set the cipd_version flag when a task is rerun from the Github GUI (#1970)

diff --git a/app_dart/lib/src/service/luci_build_service.dart b/app_dart/lib/src/service/luci_build_service.dart
index b185b96..763e6e7 100644
--- a/app_dart/lib/src/service/luci_build_service.dart
+++ b/app_dart/lib/src/service/luci_build_service.dart
@@ -163,69 +163,6 @@
     return {for (Build b in builds) b.builderId.builder: b};
   }
 
-  /// Creates BuildBucket [Request] using [checkSuiteEvent], [pullRequest], [builder], [builderId], and [userData].
-  Future<Request> _createBuildRequest({
-    CheckSuiteEvent? checkSuiteEvent,
-    required github.PullRequest pullRequest,
-    String? builder,
-    required BuilderId builderId,
-    required Map<String, dynamic> userData,
-    Map<String, dynamic>? properties,
-    List<RequestedDimension>? dimensions,
-    List<String>? branches,
-  }) async {
-    int? checkRunId;
-    final github.RepositorySlug slug = pullRequest.base!.repo!.slug();
-    if (checkSuiteEvent != null || config.githubPresubmitSupportedRepo(slug)) {
-      final String sha = pullRequest.head!.sha!;
-      final github.CheckRun checkRun = await githubChecksUtil.createCheckRun(
-        config,
-        slug,
-        sha,
-        builder!,
-      );
-      userData['check_run_id'] = checkRun.id;
-      checkRunId = checkRun.id;
-      userData['commit_sha'] = sha;
-      userData['commit_branch'] = pullRequest.base!.ref!.replaceAll('refs/heads/', '');
-      userData['builder_name'] = builder;
-      log.info(
-          'successfully created check run id: ${checkRun.id} for slug: ${slug.fullName}, sha: $sha, builder: $builder');
-    }
-    String cipdVersion = 'refs/heads/${pullRequest.base!.ref!}';
-    log.info('Branches from recipes repo: $branches, expected ref $cipdVersion');
-    cipdVersion = branches != null && branches.contains(cipdVersion) ? cipdVersion : config.defaultRecipeBundleRef;
-    properties ??= <String, dynamic>{};
-    properties.addAll(<String, String>{
-      'git_branch': pullRequest.base!.ref!.replaceAll('refs/heads/', ''),
-      'git_url': 'https://github.com/${pullRequest.base!.repo!.fullName}',
-      'git_ref': 'refs/pull/${pullRequest.number}/head',
-      'exe_cipd_version': cipdVersion,
-    });
-    return Request(
-      scheduleBuild: ScheduleBuildRequest(
-        builderId: builderId,
-        tags: <String, List<String>>{
-          'buildset': <String>['pr/git/${pullRequest.number}', 'sha/git/${pullRequest.head!.sha}'],
-          'user_agent': const <String>['flutter-cocoon'],
-          'github_link': <String>['https://github.com/${pullRequest.base!.repo!.fullName}/pull/${pullRequest.number}'],
-          'github_checkrun': <String>[checkRunId.toString()],
-          'cipd_version': <String>[cipdVersion],
-        },
-        properties: properties,
-        dimensions: dimensions,
-        notify: NotificationConfig(
-          pubsubTopic: 'projects/flutter-dashboard/topics/luci-builds',
-          userData: base64Encode(json.encode(userData).codeUnits),
-        ),
-        fields: 'id,builder,number,status,tags',
-        exe: <String, dynamic>{
-          'cipdVersion': cipdVersion,
-        },
-      ),
-    );
-  }
-
   /// Schedules presubmit [targets] on BuildBucket for [pullRequest].
   Future<List<Target>> scheduleTryBuilds({
     required List<Target> targets,
@@ -279,28 +216,51 @@
     final List<Request> requests = <Request>[];
     final List<String> branches =
         await gerritService.branches('flutter-review.googlesource.com', 'recipes', 'flutter-');
+
+    final String sha = pullRequest.head!.sha!;
+    String cipdVersion = 'refs/heads/${pullRequest.base!.ref!}';
+    cipdVersion = branches.contains(cipdVersion) ? cipdVersion : config.defaultRecipeBundleRef;
+
     for (Target target in targets) {
-      final BuilderId builderId = BuilderId(
-        project: 'flutter',
-        bucket: 'try',
-        builder: target.value.name,
+      final github.CheckRun checkRun = await githubChecksUtil.createCheckRun(
+        config,
+        target.slug,
+        sha,
+        target.value.name,
       );
-      final Map<String, dynamic> userData = <String, dynamic>{
-        'repo_owner': pullRequest.base!.repo!.owner!.login,
-        'repo_name': pullRequest.base!.repo!.name,
-        'user_agent': 'flutter-cocoon',
+
+      final github.RepositorySlug slug = pullRequest.base!.repo!.slug();
+
+      final Map<String, dynamic> userData = <String, dynamic>{};
+      if (checkSuiteEvent != null || config.githubPresubmitSupportedRepo(slug)) {
+        userData['check_run_id'] = checkRun.id;
+        userData['commit_sha'] = sha;
+        userData['commit_branch'] = pullRequest.base!.ref!.replaceAll('refs/heads/', '');
+        userData['builder_name'] = target.value.name;
+      }
+
+      Map<String, List<String>> tags = <String, List<String>>{
+        'github_checkrun': <String>[checkRun.id.toString()],
       };
-      requests.add(await _createBuildRequest(
-        checkSuiteEvent: checkSuiteEvent,
-        pullRequest: pullRequest,
-        builder: target.value.name,
-        builderId: builderId,
+
+      Map<String, Object> properties = target.getProperties();
+      properties.putIfAbsent('git_branch', () => pullRequest.base!.ref!.replaceAll('refs/heads/', ''));
+
+      requests.add(Request(
+          scheduleBuild: _createPresubmitScheduleBuild(
+        slug: slug,
+        sha: pullRequest.head!.sha!,
+        //Use target.value.name here otherwise tests will die due to null checkRun.name.
+        checkName: target.value.name,
+        pullRequestNumber: pullRequest.number!,
+        cipdVersion: cipdVersion,
         userData: userData,
-        properties: target.getProperties(),
+        properties: properties,
+        tags: tags,
         dimensions: target.getDimensions(),
-        branches: branches,
-      ));
+      )));
     }
+
     final Iterable<List<Request>> requestPartitions = await shard(requests, config.schedulingShardSize);
     for (List<Request> requestPartition in requestPartitions) {
       final BatchRequest batchRequest = BatchRequest(requests: requestPartition);
@@ -385,55 +345,40 @@
   /// Returns true if it is able to send the scheduleBuildRequest. Otherwise, false.
   Future<bool> rescheduleUsingCheckRunEvent(cocoon_checks.CheckRunEvent checkRunEvent) async {
     final github.RepositorySlug slug = checkRunEvent.repository!.slug();
-    final Map<String, dynamic> userData = <String, dynamic>{};
+
     final String sha = checkRunEvent.checkRun!.headSha!;
     final String checkName = checkRunEvent.checkRun!.name!;
+
     final github.CheckRun githubCheckRun = await githubChecksUtil.createCheckRun(
       config,
       slug,
       sha,
       checkName,
     );
+
     final Iterable<Build> builds = await getTryBuilds(slug, sha, checkName);
 
     final Build build = builds.first;
+
     final String prString = build.tags!['buildset']!.firstWhere((String? element) => element!.startsWith('pr/git/'))!;
     final String cipdVersion = build.tags!['cipd_version']![0]!;
     final int prNumber = int.parse(prString.split('/')[2]);
-    log.info('input ${build.input!} properties ${build.input!.properties!}');
-    Map<String, dynamic> properties = <String, dynamic>{};
-    properties.addAll(build.input!.properties!);
-    properties.addEntries(
-      <String, dynamic>{
-        'git_url': 'https://github.com/${slug.owner}/${slug.name}',
-        'git_ref': 'refs/pull/$prNumber/head',
-        'exe_cipd_version': cipdVersion,
-      }.entries,
-    );
-    userData['check_run_id'] = githubCheckRun.id;
-    userData['repo_owner'] = slug.owner;
-    userData['repo_name'] = slug.name;
-    userData['user_agent'] = 'flutter-cocoon';
-    final Build scheduleBuild = await buildBucketClient.scheduleBuild(ScheduleBuildRequest(
-      builderId: BuilderId(
-        project: 'flutter',
-        bucket: 'try',
-        builder: checkRunEvent.checkRun!.name,
-      ),
-      tags: <String, List<String>>{
-        'buildset': <String>['pr/git/$prNumber', 'sha/git/$sha'],
-        'user_agent': const <String>['flutter-cocoon'],
-        'github_link': <String>['https://github.com/${slug.owner}/${slug.name}/pull/$prNumber'],
-      },
-      properties: properties,
-      notify: NotificationConfig(
-        pubsubTopic: 'projects/flutter-dashboard/topics/luci-builds',
-        userData: base64Encode(json.encode(userData).codeUnits),
-      ),
-      exe: <String, dynamic>{
-        'cipdVersion': cipdVersion,
-      },
-    ));
+
+    Map<String, dynamic> userData = <String, dynamic>{'check_run_id': githubCheckRun.id};
+    Map<String, dynamic>? properties = build.input!.properties;
+    log.info('input ${build.input!} properties $properties');
+
+    ScheduleBuildRequest scheduleBuildRequest = _createPresubmitScheduleBuild(
+        slug: slug,
+        sha: sha,
+        checkName: checkName,
+        pullRequestNumber: prNumber,
+        cipdVersion: cipdVersion,
+        properties: properties,
+        userData: userData);
+
+    final Build scheduleBuild = await buildBucketClient.scheduleBuild(scheduleBuildRequest);
+
     final String buildUrl = 'https://ci.chromium.org/ui/b/${scheduleBuild.id}';
     await githubChecksUtil.updateCheckRun(config, slug, githubCheckRun, detailsUrl: buildUrl);
     return true;
@@ -494,6 +439,59 @@
     log.info('Published a request with ${buildRequests.length} builds');
   }
 
+  /// Create a Presubmit ScheduleBuildRequest using the [slug], [sha], and
+  /// [checkName] for the provided [build] with the provided [checkRunId].
+  ScheduleBuildRequest _createPresubmitScheduleBuild({
+    required github.RepositorySlug slug,
+    required String sha,
+    required String checkName,
+    required int pullRequestNumber,
+    required String cipdVersion,
+    Map<String, dynamic>? properties,
+    Map<String, List<String>>? tags,
+    Map<String, dynamic>? userData,
+    List<RequestedDimension>? dimensions,
+  }) {
+    final Map<String, dynamic> processedProperties = <String, dynamic>{};
+    processedProperties.addAll(properties ?? <String, dynamic>{});
+    processedProperties.addEntries(
+      <String, dynamic>{
+        'git_url': 'https://github.com/${slug.owner}/${slug.name}',
+        'git_ref': 'refs/pull/$pullRequestNumber/head',
+        'exe_cipd_version': cipdVersion,
+      }.entries,
+    );
+
+    final Map<String, dynamic> processedUserData = userData ?? <String, dynamic>{};
+    processedUserData['repo_owner'] = slug.owner;
+    processedUserData['repo_name'] = slug.name;
+    processedUserData['user_agent'] = 'flutter-cocoon';
+
+    final BuilderId builderId = BuilderId(project: 'flutter', bucket: 'try', builder: checkName);
+
+    final Map<String, List<String>> processedTags = tags ?? <String, List<String>>{};
+    processedTags['buildset'] = <String>['pr/git/$pullRequestNumber', 'sha/git/$sha'];
+    processedTags['user_agent'] = const <String>['flutter-cocoon'];
+    processedTags['github_link'] = <String>['https://github.com/${slug.owner}/${slug.name}/pull/$pullRequestNumber'];
+    processedTags['cipd_version'] = <String>[cipdVersion];
+
+    final NotificationConfig notificationConfig = NotificationConfig(
+        pubsubTopic: 'projects/flutter-dashboard/topics/luci-builds',
+        userData: base64Encode(json.encode(processedUserData).codeUnits));
+
+    final Map<String, dynamic> exec = <String, dynamic>{'cipdVersion': cipdVersion};
+
+    return ScheduleBuildRequest(
+      builderId: builderId,
+      tags: processedTags,
+      properties: processedProperties,
+      notify: notificationConfig,
+      fields: 'id,builder,number,status,tags',
+      exe: exec,
+      dimensions: dimensions,
+    );
+  }
+
   /// Creates a [ScheduleBuildRequest] for [target] and [task] against [commit].
   ///
   /// By default, build [priority] is increased for release branches.
diff --git a/app_dart/test/service/buildbucket_test.dart b/app_dart/test/service/buildbucket_test.dart
index 5c41aed..504087a 100644
--- a/app_dart/test/service/buildbucket_test.dart
+++ b/app_dart/test/service/buildbucket_test.dart
@@ -96,7 +96,8 @@
         experimental: Trinary.yes,
         tags: <String, List<String>>{
           'user_agent': <String>['flutter_cocoon'],
-          'flutter_pr': <String>['true', '1']
+          'flutter_pr': <String>['true', '1'],
+          'cipd_version': <String>['/refs/heads/main'],
         },
         properties: <String, String>{
           'git_url': 'https://github.com/flutter/flutter',
@@ -111,11 +112,13 @@
         'ScheduleBuild',
         (BuildBucketClient client) => client.scheduleBuild(request, buildBucketUri: 'https://localhost/builds'),
       );
+
       expect(build.id, '123');
-      expect(build.tags!.length, 2);
+      expect(build.tags!.length, 3);
       expect(build.tags, <String?, List<String?>>{
         'user_agent': <String>['flutter_cocoon'],
-        'flutter_pr': <String>['1']
+        'flutter_pr': <String>['1'],
+        'cipd_version': <String>['/refs/heads/main'],
       });
     });
 
@@ -134,7 +137,7 @@
       );
 
       expect(build.id, '123');
-      expect(build.tags!.length, 2);
+      expect(build.tags!.length, 3);
     });
 
     test('BatchBuildRequest', () async {
@@ -145,7 +148,8 @@
           experimental: Trinary.yes,
           tags: <String, List<String>>{
             'user_agent': <String>['flutter_cocoon'],
-            'flutter_pr': <String>['true', '1']
+            'flutter_pr': <String>['true', '1'],
+            'cipd_version': <String>['/refs/heads/main'],
           },
           properties: <String, String>{
             'git_url': 'https://github.com/flutter/flutter',
@@ -165,7 +169,8 @@
       expect(response.responses!.first.getBuild!.status, Status.success);
       expect(response.responses!.first.getBuild!.tags, <String?, List<String?>>{
         'user_agent': <String>['flutter_cocoon'],
-        'flutter_pr': <String>['1']
+        'flutter_pr': <String>['1'],
+        'cipd_version': <String>['/refs/heads/main'],
       });
     });
 
@@ -204,7 +209,7 @@
       );
 
       expect(build.id, '123');
-      expect(build.tags!.length, 2);
+      expect(build.tags!.length, 3);
     });
 
     test('SearchBuilds', () async {
@@ -324,6 +329,10 @@
           {
             "key": "flutter_pr",
             "value": "1"
+          },
+          {
+            "key": "cipd_version",
+            "value": "/refs/heads/main"
           }
         ]
       }
@@ -354,5 +363,9 @@
   }, {
     "key": "flutter_pr",
     "value": "1"
-  }]
+  },
+  {
+            "key": "cipd_version",
+            "value": "/refs/heads/main"
+          }]
 }''';
diff --git a/app_dart/test/service/luci_build_service_test.dart b/app_dart/test/service/luci_build_service_test.dart
index 0751e77..7fdbd68 100644
--- a/app_dart/test/service/luci_build_service_test.dart
+++ b/app_dart/test/service/luci_build_service_test.dart
@@ -60,6 +60,7 @@
       );
       slug = RepositorySlug('flutter', 'cocoon');
     });
+
     test('Null build', () async {
       when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
         return BatchResponse(
@@ -79,6 +80,7 @@
       );
       expect(builds.first, macBuild);
     });
+
     test('Existing prod build', () async {
       when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
         return const BatchResponse(
@@ -98,6 +100,7 @@
       );
       expect(builds, isEmpty);
     });
+
     test('Existing try build', () async {
       when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
         return BatchResponse(
@@ -133,6 +136,7 @@
       );
       slug = RepositorySlug('flutter', 'flutter');
     });
+
     test('with one rpc call', () async {
       when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async {
         return const ListBuildersResponse(builders: [
@@ -168,6 +172,7 @@
       expect(builders, <String>{'test1', 'test2', 'test3', 'test4'});
     });
   });
+
   group('buildsForRepositoryAndPr', () {
     final Build macBuild = generateBuild(999, name: 'Mac', status: Status.started);
     final Build linuxBuild = generateBuild(998, name: 'Linux', status: Status.started);
@@ -184,6 +189,7 @@
       );
       slug = RepositorySlug('flutter', 'cocoon');
     });
+
     test('Empty responses are handled correctly', () async {
       when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
         return const BatchResponse(
@@ -221,6 +227,7 @@
       expect(builds, equals(<String, Build>{'Mac': macBuild, 'Linux': linuxBuild}));
     });
   });
+
   group('scheduleBuilds', () {
     setUp(() {
       githubService = FakeGithubService();
@@ -249,7 +256,8 @@
           ],
         );
       });
-      when(mockGithubChecksUtil.createCheckRun(any, any, any, any)).thenAnswer((_) async => generateCheckRun(1));
+      when(mockGithubChecksUtil.createCheckRun(any, any, any, any))
+          .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1'));
       final List<Target> scheduledTargets = await service.scheduleTryBuilds(
         pullRequest: pullRequest,
         targets: targets,
@@ -258,6 +266,7 @@
       expect(scheduledTargetNames, <String>['Linux 1']);
       final BatchRequest batchRequest = pubsub.messages.single as BatchRequest;
       expect(batchRequest.requests!.single.scheduleBuild, isNotNull);
+
       final ScheduleBuildRequest scheduleBuild = batchRequest.requests!.single.scheduleBuild!;
       expect(scheduleBuild.builderId.bucket, 'try');
       expect(scheduleBuild.builderId.builder, 'Linux 1');
@@ -273,6 +282,7 @@
         'commit_branch': 'master',
         'builder_name': 'Linux 1'
       });
+
       final Map<String, dynamic> properties = scheduleBuild.properties!;
       final List<RequestedDimension> dimensions = scheduleBuild.dimensions!;
       expect(properties, <String, dynamic>{
diff --git a/app_dart/test/src/utilities/entity_generators.dart b/app_dart/test/src/utilities/entity_generators.dart
index 29a92b1..ced5fdc 100644
--- a/app_dart/test/src/utilities/entity_generators.dart
+++ b/app_dart/test/src/utilities/entity_generators.dart
@@ -141,12 +141,14 @@
 
 github.CheckRun generateCheckRun(
   int i, {
+  String name = 'name',
   int checkSuite = 2,
   DateTime? startedAt,
 }) {
   startedAt ??= DateTime.utc(2020, 05, 12);
   return github.CheckRun.fromJson(<String, dynamic>{
     'id': i,
+    'name': name,
     'started_at': startedAt.toIso8601String(),
     'check_suite': <String, dynamic>{'id': checkSuite}
   });