fix PR scheduling (#641)
* fix PR scheduling
* comments
diff --git a/app_dart/lib/src/request_handlers/github_webhook.dart b/app_dart/lib/src/request_handlers/github_webhook.dart
index 466f053..194f5c5 100644
--- a/app_dart/lib/src/request_handlers/github_webhook.dart
+++ b/app_dart/lib/src/request_handlers/github_webhook.dart
@@ -82,33 +82,61 @@
// which unfortunately is a bit light on explanations.
switch (event.action) {
case 'closed':
+ // On a successful merge, check for gold.
+ // If it was closed without merging, cancel any outstanding tryjobs.
+ // We'll leave unfinished jobs if it was merged since we care about those
+ // results.
if (event.pullRequest.merged) {
await _checkForGoldenTriage(
event,
existingLabels,
);
+ } else {
+ await _cancelLuci(
+ event.repository.name,
+ event.number,
+ event.pullRequest.head.sha,
+ 'Pull request closed',
+ );
}
break;
case 'edited':
+ // Editing a PR should not trigger new jobs, but may update whether
+ // it has tests.
+ await _checkForLabelsAndTests(event, isDraft);
+ break;
case 'opened':
case 'ready_for_review':
case 'reopened':
+ // These cases should trigger LUCI jobs.
await _checkForLabelsAndTests(event, isDraft);
await _scheduleIfMergeable(
event,
- cancelRunningBuilds: event.action == 'edited',
labels: existingLabels,
);
break;
case 'labeled':
+ // This should only trigger a LUCI job for flutter/flutter right now,
+ // since it is in the needsCQLabelList.
+ if (needsCQLabelList
+ .contains(event.repository.fullName.toLowerCase())) {
+ await _scheduleIfMergeable(
+ event,
+ labels: existingLabels,
+ );
+ }
+ break;
case 'synchronize':
+ // This indicates the PR has new commits. We need to cancel old jobs
+ // and schedule new ones.
await _scheduleIfMergeable(
event,
- cancelRunningBuilds: event.action == 'synchronize',
labels: existingLabels,
);
break;
case 'unlabeled':
+ // Cancel the jobs if someone removed the label on a repo that needs
+ // them.
if (!needsCQLabelList
.contains(event.repository.fullName.toLowerCase())) {
break;
@@ -122,6 +150,7 @@
);
}
break;
+ // Ignore the rest of the events.
case 'assigned':
case 'locked':
case 'review_request_removed':
@@ -132,39 +161,37 @@
}
}
+ /// This method assumes that jobs should be cancelled if they are already
+ /// runnning.
Future<void> _scheduleIfMergeable(
PullRequestEvent event, {
- @required bool cancelRunningBuilds,
@required List<IssueLabel> labels,
}) async {
- assert(cancelRunningBuilds != null);
- if (cancelRunningBuilds) {
- await _cancelLuci(
- event.repository.name,
- event.number,
- event.pullRequest.head.sha,
- 'Newer commit available',
- );
- }
// The mergeable flag may be null. False indicates there's a merge conflict,
// null indicates unknown. Err on the side of allowing the job to run.
// For flutter/flutter tests need to be optimized before enforcing CQ.
- bool runCQ = true;
if (needsCQLabelList.contains(event.repository.fullName.toLowerCase())) {
- runCQ = await _checkForCqLabel(labels);
+ if (!await _checkForCqLabel(labels)) {
+ return;
+ }
}
- if (runCQ) {
- await _scheduleLuci(
- number: event.number,
- sha: event.pullRequest.head.sha,
- repositoryName: event.repository.name,
- skipRunningCheck: cancelRunningBuilds,
- );
- }
+ // Always cancel running builds so we don't ever schedule duplicates.
+ await _cancelLuci(
+ event.repository.name,
+ event.number,
+ event.pullRequest.head.sha,
+ 'Newer commit available',
+ );
+ await _scheduleLuci(
+ number: event.number,
+ sha: event.pullRequest.head.sha,
+ repositoryName: event.repository.name,
+ );
}
+ /// This method checks if there are running builds for this PR
Future<List<Build>> _buildsForRepositoryAndPr(
String repositoryName,
int number,
@@ -217,12 +244,10 @@
@required int number,
@required String sha,
@required String repositoryName,
- bool skipRunningCheck = false,
}) async {
assert(number != null);
assert(sha != null);
assert(repositoryName != null);
- assert(skipRunningCheck != null);
if (!supportedRepos.contains(repositoryName)) {
log.error('Unsupported repo on webhook: $repositoryName');
throw BadRequestException(
@@ -231,21 +256,19 @@
final ServiceAccountInfo serviceAccount =
await config.deviceLabServiceAccount;
- if (!skipRunningCheck) {
- final List<Build> builds = await _buildsForRepositoryAndPr(
- repositoryName,
- number,
- sha,
- buildBucketClient,
- serviceAccount,
- );
- if (builds != null &&
- builds.any((Build build) {
- return build.status == Status.scheduled ||
- build.status == Status.started;
- })) {
- return false;
- }
+ final List<Build> builds = await _buildsForRepositoryAndPr(
+ repositoryName,
+ number,
+ sha,
+ buildBucketClient,
+ serviceAccount,
+ );
+ if (builds != null &&
+ builds.any((Build build) {
+ return build.status == Status.scheduled ||
+ build.status == Status.started;
+ })) {
+ return false;
}
final List<Map<String, dynamic>> builders = config.luciTryBuilders;
diff --git a/app_dart/test/request_handlers/github_webhook_test.dart b/app_dart/test/request_handlers/github_webhook_test.dart
index 3c5bb82..0e9b1e9 100644
--- a/app_dart/test/request_handlers/github_webhook_test.dart
+++ b/app_dart/test/request_handlers/github_webhook_test.dart
@@ -33,9 +33,12 @@
MockBuildBucketClient mockBuildBucketClient;
RequestHandlerTester tester;
MockHttpClient mockHttpClient;
+ const String serviceAccountEmail = 'test@test';
const String keyString = 'not_a_real_key';
+ const String cqLabelName = 'CQ+1';
+
String getHmac(Uint8List list, Uint8List key) {
final Hmac hmac = Hmac(sha1, key);
return hmac.convert(list).toString();
@@ -65,6 +68,26 @@
config.githubOAuthTokenValue = 'githubOAuthKey';
config.webhookKeyValue = keyString;
config.githubClient = gitHubClient;
+ config.deviceLabServiceAccountValue =
+ const ServiceAccountInfo(email: serviceAccountEmail);
+
+ config.luciTryBuildersValue = json.decode('''[
+ {"name": "Cocoon", "repo": "cocoon"},
+ {"name": "Linux", "repo": "flutter", "taskName": "linux_bot"},
+ {"name": "Mac", "repo": "flutter", "taskName": "mac_bot"},
+ {"name": "Windows", "repo": "flutter", "taskName": "windows_bot"},
+ {"name": "Linux Coverage", "repo": "flutter"},
+ {"name": "Linux Host Engine", "repo": "engine"},
+ {"name": "Linux Android AOT Engine", "repo": "engine"},
+ {"name": "Linux Android Debug Engine", "repo": "engine"},
+ {"name": "Mac Host Engine", "repo": "engine"},
+ {"name": "Mac Android AOT Engine", "repo": "engine"},
+ {"name": "Mac Android Debug Engine", "repo": "engine"},
+ {"name": "Mac iOS Engine", "repo": "engine"},
+ {"name": "Windows Host Engine", "repo": "engine"},
+ {"name": "Windows Android AOT Engine", "repo": "engine"}
+ ]''').cast<Map<String, dynamic>>();
+ config.cqLabelNameValue = cqLabelName;
});
test('Rejects non-POST methods with methodNotAllowed', () async {
@@ -442,6 +465,18 @@
]);
});
+ when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
+ return const BatchResponse(
+ responses: <Response>[
+ Response(
+ searchBuilds: SearchBuildsResponse(
+ builds: <Build>[],
+ ),
+ ),
+ ],
+ );
+ });
+
await tester.post(webhook);
verifyNever(issuesService.createComment(
@@ -478,6 +513,18 @@
when(mockHttpRequest.close()).thenAnswer(
(_) => Future<MockHttpClientResponse>.value(mockHttpResponse));
+ when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
+ return const BatchResponse(
+ responses: <Response>[
+ Response(
+ searchBuilds: SearchBuildsResponse(
+ builds: <Build>[],
+ ),
+ ),
+ ],
+ );
+ });
+
await tester.post(webhook);
verifyNever(issuesService.createComment(
@@ -487,6 +534,64 @@
));
});
+ test('Cancels builds when pull request is closed without merging',
+ () async {
+ const int issueNumber = 123;
+ request.headers.set('X-GitHub-Event', 'pull_request');
+ request.body = jsonTemplate('closed', issueNumber, 'master');
+ final Uint8List body = utf8.encode(request.body);
+ final Uint8List key = utf8.encode(keyString);
+ final String hmac = getHmac(body, key);
+ request.headers.set('X-Hub-Signature', 'sha1=$hmac');
+ const RepositorySlug slug = RepositorySlug('flutter', 'flutter');
+
+ when(pullRequestsService.listFiles(slug, issueNumber))
+ .thenAnswer((_) => Stream<PullRequestFile>.value(
+ PullRequestFile()..filename = 'some_change.dart',
+ ));
+
+ final MockHttpClientRequest mockHttpRequest = MockHttpClientRequest();
+ final MockHttpClientResponse mockHttpResponse = MockHttpClientResponse(
+ utf8.encode(
+ skiaIgnoreTemplate(pullRequestNumber: issueNumber.toString())));
+ when(mockHttpClient
+ .getUrl(Uri.parse('https://flutter-gold.skia.org/json/ignores')))
+ .thenAnswer(
+ (_) => Future<MockHttpClientRequest>.value(mockHttpRequest));
+ when(mockHttpRequest.close()).thenAnswer(
+ (_) => Future<MockHttpClientResponse>.value(mockHttpResponse));
+
+ when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
+ return const BatchResponse(
+ responses: <Response>[
+ Response(
+ searchBuilds: SearchBuildsResponse(
+ builds: <Build>[
+ Build(
+ id: 999,
+ builderId: BuilderId(
+ project: 'flutter',
+ bucket: 'prod',
+ builder: 'Linux',
+ ),
+ status: Status.started,
+ ),
+ ],
+ ),
+ ),
+ ],
+ );
+ });
+
+ await tester.post(webhook);
+
+ expect(
+ json.encode(verify(mockBuildBucketClient.batch(captureAny)).captured),
+ '[{"requests":[{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"createdBy":"test@test","tags":[{"key":"buildset","value":"pr/git/123"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"},{"key":"user_agent","value":"flutter-cocoon"}]}}},'
+ '{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"user_agent","value":"recipe"}]}}}]},{"requests":[{"cancelBuild":{"id":"999","summaryMarkdown":"Pull request closed"}}]}]',
+ );
+ });
+
test('Labels draft issues as work in progress, does not test pest.',
() async {
const int issueNumber = 123;
@@ -613,30 +718,8 @@
group('BuildBucket', () {
const int issueNumber = 123;
- const String serviceAccountEmail = 'test@test';
- const String cqLabelName = 'CQ+1';
setUp(() {
- clearInteractions(mockBuildBucketClient);
- config.deviceLabServiceAccountValue =
- const ServiceAccountInfo(email: serviceAccountEmail);
- config.luciTryBuildersValue = json.decode('''[
- {"name": "Cocoon", "repo": "cocoon"},
- {"name": "Linux", "repo": "flutter", "taskName": "linux_bot"},
- {"name": "Mac", "repo": "flutter", "taskName": "mac_bot"},
- {"name": "Windows", "repo": "flutter", "taskName": "windows_bot"},
- {"name": "Linux Coverage", "repo": "flutter"},
- {"name": "Linux Host Engine", "repo": "engine"},
- {"name": "Linux Android AOT Engine", "repo": "engine"},
- {"name": "Linux Android Debug Engine", "repo": "engine"},
- {"name": "Mac Host Engine", "repo": "engine"},
- {"name": "Mac Android AOT Engine", "repo": "engine"},
- {"name": "Mac Android Debug Engine", "repo": "engine"},
- {"name": "Mac iOS Engine", "repo": "engine"},
- {"name": "Windows Host Engine", "repo": "engine"},
- {"name": "Windows Android AOT Engine", "repo": "engine"}
- ]''').cast<Map<String, dynamic>>();
- config.cqLabelNameValue = cqLabelName;
request.headers.set('X-GitHub-Event', 'pull_request');
});
@@ -660,12 +743,13 @@
);
});
- request.body = jsonTemplate('labeled', issueNumber, 'master',
+ request.body = jsonTemplate('synchronize', issueNumber, 'master',
repoFullName: 'flutter/cocoon', repoName: 'cocoon');
final Uint8List body = utf8.encode(request.body);
final Uint8List key = utf8.encode(keyString);
final String hmac = getHmac(body, key);
request.headers.set('X-Hub-Signature', 'sha1=$hmac');
+
expect(tester.post(webhook), throwsA(isA<InternalServerError>()));
});
@@ -689,7 +773,7 @@
verifyNever(mockBuildBucketClient.batch(any));
});
- Future<void> _testActions(String action) async {
+ Future<void> _testActions(String action, {bool never = false}) async {
when(issuesService.listLabelsByIssue(any, issueNumber)).thenAnswer((_) {
return Stream<IssueLabel>.fromIterable(<IssueLabel>[
IssueLabel()..name = 'Random Label',
@@ -723,6 +807,12 @@
request.headers.set('X-Hub-Signature', 'sha1=$hmac');
await tester.post(webhook);
+
+ if (never) {
+ verifyNever(mockBuildBucketClient.batch(captureAny));
+ return;
+ }
+
expect(
json.encode(
verify(mockBuildBucketClient.batch(captureAny)).captured),
@@ -734,6 +824,14 @@
'{"predicate":{"builder":{"project":"flutter","bucket":"try"},'
'"tags":[{"key":"buildset","value":"pr/git/583"},{"key":'
'"user_agent","value":"recipe"}]}}}]},{"requests":'
+ '[{"searchBuilds":{"predicate":{"builder":'
+ '{"project":"flutter","bucket":"try"},"createdBy":"test@test","tags":'
+ '[{"key":"buildset","value":"pr/git/583"},{"key":"github_link",'
+ '"value":"https://github.com/flutter/cocoon/pull/583"},'
+ '{"key":"user_agent","value":"flutter-cocoon"}]}}},{"searchBuilds":'
+ '{"predicate":{"builder":{"project":"flutter","bucket":"try"},'
+ '"tags":[{"key":"buildset","value":"pr/git/583"},{"key":'
+ '"user_agent","value":"recipe"}]}}}]},{"requests":'
'[{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try",'
'"builder":"Cocoon"},"properties":{"git_url":'
'"https://github.com/flutter/cocoon","git_ref":'
@@ -747,7 +845,7 @@
}
test('Edited Action works properly', () async {
- await _testActions('edited');
+ await _testActions('edited', never: true);
});
test('Opened Action works properly', () async {
@@ -763,67 +861,13 @@
});
test('Labeled Action works properly', () async {
- await _testActions('labeled');
+ await _testActions('labeled', never: true);
});
test('Synchronize Action works properly', () async {
await _testActions('synchronize');
});
- test('Mandatory repo - Always schedule CQ', () async {
- when(issuesService.listLabelsByIssue(any, issueNumber)).thenAnswer((_) {
- return Stream<IssueLabel>.fromIterable(<IssueLabel>[
- IssueLabel()..name = 'Random Label',
- ]);
- });
-
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return const BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[],
- ),
- ),
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[],
- ),
- ),
- ],
- );
- });
-
- request.body = jsonTemplate('labeled', issueNumber, 'master',
- repoFullName: 'flutter/cocoon', repoName: 'cocoon');
- final Uint8List body = utf8.encode(request.body);
- final Uint8List key = utf8.encode(keyString);
- final String hmac = getHmac(body, key);
- request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-
- await tester.post(webhook);
- expect(
- json.encode(
- verify(mockBuildBucketClient.batch(captureAny)).captured),
- '[{"requests":[{"searchBuilds":{"predicate":{"builder":{"project":'
- '"flutter","bucket":"try"},"createdBy":"test@test","tags":[{"key":'
- '"buildset","value":"pr/git/123"},{"key":"github_link","value":'
- '"https://github.com/flutter/cocoon/pull/123"},{"key":"user_agent",'
- '"value":"flutter-cocoon"}]}}},{"searchBuilds":{"predicate":'
- '{"builder":{"project":"flutter","bucket":"try"},"tags":[{"key":'
- '"buildset","value":"pr/git/123"},{"key":"user_agent","value":'
- '"recipe"}]}}}]},{"requests":[{"scheduleBuild":{"builder":'
- '{"project":"flutter","bucket":"try","builder":"Cocoon"},'
- '"properties":{"git_url":"https://github.com/flutter/cocoon",'
- '"git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset",'
- '"value":"pr/git/123"},{"key":"buildset","value":'
- '"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},'
- '{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link",'
- '"value":"https://github.com/flutter/cocoon/pull/123"}],"notify":'
- '{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds",'
- '"userData":"eyJyZXRyaWVzIjowfQ=="}}}]}]');
- });
-
test('Schedules builds when labeled', () async {
when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
return const BatchResponse(
@@ -851,8 +895,7 @@
await tester.post(webhook);
expect(
json.encode(verify(mockBuildBucketClient.batch(captureAny)).captured),
- '[{"requests":[{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"createdBy":"test@test","tags":[{"key":"buildset","value":"pr/git/123"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"},{"key":"user_agent","value":"flutter-cocoon"}]}}},{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"user_agent","value":"recipe"}]}}}]},'
- '{"requests":[{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Linux"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Mac"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Windows"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Linux Coverage"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}}]}]',
+ '[{"requests":[{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"createdBy":"test@test","tags":[{"key":"buildset","value":"pr/git/123"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"},{"key":"user_agent","value":"flutter-cocoon"}]}}},{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"user_agent","value":"recipe"}]}}}]},{"requests":[{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"createdBy":"test@test","tags":[{"key":"buildset","value":"pr/git/123"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"},{"key":"user_agent","value":"flutter-cocoon"}]}}},{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"user_agent","value":"recipe"}]}}}]},{"requests":[{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Linux"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Mac"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Windows"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Linux Coverage"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}}]}]',
);
});
@@ -965,61 +1008,7 @@
);
});
- test('Cancels and schedules builds when synchronized', () async {
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return const BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[
- Build(
- id: 999,
- builderId: BuilderId(
- project: 'flutter',
- bucket: 'prod',
- builder: 'Linux',
- ),
- status: Status.started,
- )
- ],
- ),
- ),
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[
- Build(
- id: 998,
- builderId: BuilderId(
- project: 'flutter',
- bucket: 'prod',
- builder: 'Linux',
- ),
- status: Status.success,
- )
- ],
- ),
- ),
- ],
- );
- });
-
- request.body = jsonTemplate('synchronize', issueNumber, 'master',
- includeCqLabel: true);
- final Uint8List body = utf8.encode(request.body);
- final Uint8List key = utf8.encode(keyString);
- final String hmac = getHmac(body, key);
- request.headers.set('X-Hub-Signature', 'sha1=$hmac');
-
- await tester.post(webhook);
- expect(
- json.encode(verify(mockBuildBucketClient.batch(captureAny)).captured),
- '[{"requests":[{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"createdBy":"test@test","tags":[{"key":"buildset","value":"pr/git/123"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"},{"key":"user_agent","value":"flutter-cocoon"}]}}},{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"user_agent","value":"recipe"}]}}}]},'
- '{"requests":[{"cancelBuild":{"id":"999","summaryMarkdown":"Newer commit available"}},{"cancelBuild":{"id":"998","summaryMarkdown":"Newer commit available"}}]},'
- '{"requests":[{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Linux"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Mac"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Windows"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Linux Coverage"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}}]}]',
- );
- });
-
- test('Only schedules builds when synchronized and no running builds',
+ test('When synchronized, cancels existing builds and schedules new ones',
() async {
when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
return const BatchResponse(
@@ -1068,8 +1057,7 @@
await tester.post(webhook);
expect(
json.encode(verify(mockBuildBucketClient.batch(captureAny)).captured),
- '[{"requests":[{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"createdBy":"test@test","tags":[{"key":"buildset","value":"pr/git/123"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"},{"key":"user_agent","value":"flutter-cocoon"}]}}},{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"user_agent","value":"recipe"}]}}}]},{"requests":[{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Linux"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Mac"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Windows"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},'
- '{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Linux Coverage"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}}]}]',
+ '[{"requests":[{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"createdBy":"test@test","tags":[{"key":"buildset","value":"pr/git/123"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"},{"key":"user_agent","value":"flutter-cocoon"}]}}},{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"user_agent","value":"recipe"}]}}}]},{"requests":[{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"createdBy":"test@test","tags":[{"key":"buildset","value":"pr/git/123"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"},{"key":"user_agent","value":"flutter-cocoon"}]}}},{"searchBuilds":{"predicate":{"builder":{"project":"flutter","bucket":"try"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"user_agent","value":"recipe"}]}}}]},{"requests":[{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Linux"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Mac"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Windows"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}},{"scheduleBuild":{"builder":{"project":"flutter","bucket":"try","builder":"Linux Coverage"},"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/123/head"},"tags":[{"key":"buildset","value":"pr/git/123"},{"key":"buildset","value":"sha/git/be6ff099a4ee56e152a5fa2f37edd10f79d1269a"},{"key":"user_agent","value":"flutter-cocoon"},{"key":"github_link","value":"https://github.com/flutter/flutter/pull/123"}],"notify":{"pubsubTopic":"projects/flutter-dashboard/topics/luci-builds","userData":"eyJyZXRyaWVzIjowfQ=="}}}]}]',
);
});
});