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}
});