Skip to content

Commit 363e9cd

Browse files
authored
feat: limit ci resources (#4982)
Once this lands; PRs will need to have a "labeled" event for `CICD` to trigger tests. This will only run for the HEAD sha at the time of labelling - so further pushes will required removing and re-adding the CICD label. --------- Signed-off-by: John McDole <codefu@google.com>
1 parent b052dab commit 363e9cd

9 files changed

Lines changed: 349 additions & 1434 deletions

File tree

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2026 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:convert';
6+
7+
import 'package:json_annotation/json_annotation.dart';
8+
9+
part 'labels.g.dart';
10+
11+
@JsonSerializable(fieldRename: FieldRename.snake)
12+
class LabeledEvent {
13+
LabeledEvent({required this.label});
14+
factory LabeledEvent.fromJson(Map<String, Object?> input) =>
15+
_$LabeledEventFromJson(input);
16+
17+
final Label label;
18+
19+
Map<String, Object?> toJson() => _$LabeledEventToJson(this);
20+
21+
@override
22+
String toString() {
23+
return '$LabeledEvent ${const JsonEncoder.withIndent(' ').convert(toJson())}';
24+
}
25+
}
26+
27+
@JsonSerializable(fieldRename: FieldRename.snake)
28+
class Label {
29+
Label({
30+
required this.id,
31+
required this.name,
32+
required this.nodeId,
33+
required this.url,
34+
required this.color,
35+
required this.description,
36+
});
37+
factory Label.fromJson(Map<String, Object?> input) => _$LabelFromJson(input);
38+
39+
/// id: 4232992339
40+
final int id;
41+
42+
/// name: autosubmit
43+
final String name;
44+
45+
/// node_id: LA_kwDOAeUeuM78TlZT
46+
final String nodeId;
47+
48+
/// url: https://api.github.com/repos/flutter/flutter/labels/autosubmit
49+
final Uri url;
50+
51+
/// color: 008820
52+
final String color;
53+
54+
// description: Merge PR when tree becomes green via auto submit App
55+
final String description;
56+
57+
Map<String, Object?> toJson() => _$LabelToJson(this);
58+
59+
@override
60+
String toString() {
61+
return '$Label ${const JsonEncoder.withIndent(' ').convert(toJson())}';
62+
}
63+
}

app_dart/lib/src/model/github/labels.g.dart

Lines changed: 33 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app_dart/lib/src/request_handlers/github/webhook_subscription.dart

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import 'package:meta/meta.dart';
1515
import '../../../cocoon_service.dart';
1616
import '../../../protos.dart' as pb;
1717
import '../../model/github/checks.dart' as cocoon_checks;
18+
import '../../model/github/labels.dart';
1819
import '../../model/github/workflow_job.dart' as workflow_job;
1920
import '../../request_handling/exceptions.dart';
2021
import '../../request_handling/subscription_handler.dart';
@@ -199,17 +200,9 @@ final class GithubWebhookSubscription extends SubscriptionHandler {
199200
return result.toResponse();
200201
case 'edited':
201202
await _checkForTests(pullRequestEvent);
202-
// In the event of the base ref changing we want to start new checks.
203-
if (pullRequestEvent.changes != null &&
204-
pullRequestEvent.changes!.base != null) {
205-
await _scheduleIfMergeable(pullRequestEvent);
206-
}
207203
break;
208204
case 'opened':
209-
// These cases should trigger LUCI jobs. The closed event should happen
210-
// before these which should cancel all in progress checks.
211205
await _checkForTests(pullRequestEvent);
212-
await _scheduleIfMergeable(pullRequestEvent);
213206
await _tryReleaseApproval(pullRequestEvent);
214207
break;
215208
case 'reopened':
@@ -220,18 +213,25 @@ final class GithubWebhookSubscription extends SubscriptionHandler {
220213
log.info(
221214
'$crumb: PR labels = [${pr.labels?.map((label) => '"${label.name}"').join(', ')}]',
222215
);
216+
217+
final labelEvent = _getLabeledEvent(rawRequest);
218+
if (labelEvent?.label case final label?) {
219+
if (Config.kCicdLabelIds.contains(label.id) &&
220+
label.name == Config.kCicdLabel) {
221+
log.info('new CICD label added - scheduling tests');
222+
await _scheduleIfMergeable(pullRequestEvent);
223+
}
224+
}
225+
223226
await _processLabels(pr);
224227
await _updatePullRequest(pr);
225-
break;
226-
case 'synchronize':
227-
// This indicates the PR has new commits. We need to cancel old jobs
228-
// and schedule new ones.
229-
await _scheduleIfMergeable(pullRequestEvent);
228+
230229
break;
231230
case 'dequeued':
232231
await _respondToPullRequestDequeued(pullRequestEvent);
233232
break;
234233
// Ignore the rest of the events.
234+
case 'synchronize':
235235
case 'ready_for_review':
236236
case 'unlabeled':
237237
case 'assigned':
@@ -967,6 +967,17 @@ final class GithubWebhookSubscription extends SubscriptionHandler {
967967
}
968968
}
969969

970+
LabeledEvent? _getLabeledEvent(String request) {
971+
try {
972+
return LabeledEvent.fromJson(
973+
json.decode(request) as Map<String, dynamic>,
974+
);
975+
} catch (e, s) {
976+
log.warn('_getLabeledEvent: Failed to parse $request', e, s);
977+
return null;
978+
}
979+
}
980+
970981
/// Returns true if the changes to [file] are all code comments.
971982
///
972983
/// If that cannot be determined with confidence, returns false. False

app_dart/lib/src/request_handlers/reset_try_task.dart

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
import 'dart:async';
66

7+
// ignore: unused_import
8+
import 'package:cocoon_server/logging.dart';
9+
// ignore: unused_import
710
import 'package:github/github.dart';
811
import 'package:meta/meta.dart';
912

@@ -20,6 +23,7 @@ final class ResetTryTask extends ApiRequestHandler {
2023
required Scheduler scheduler,
2124
}) : _scheduler = scheduler;
2225

26+
// ignore: unused_field
2327
final Scheduler _scheduler;
2428

2529
@visibleForTesting
@@ -40,25 +44,27 @@ final class ResetTryTask extends ApiRequestHandler {
4044
kRepoParam,
4145
kPullRequestNumberParam,
4246
]);
43-
final owner = request.uri.queryParameters[kOwnerParam] ?? 'flutter';
44-
final repo = request.uri.queryParameters[kRepoParam]!;
47+
// final owner = request.uri.queryParameters[kOwnerParam] ?? 'flutter';
48+
// final repo = request.uri.queryParameters[kRepoParam]!;
4549
final pr = request.uri.queryParameters[kPullRequestNumberParam]!;
46-
final builders = request.uri.queryParameters[kBuilderParam] ?? '';
47-
final builderList = getBuilderList(builders);
50+
// final builders = request.uri.queryParameters[kBuilderParam] ?? '';
51+
// final builderList = getBuilderList(builders);
4852

4953
final prNumber = int.tryParse(pr);
5054
if (prNumber == null) {
5155
throw const BadRequestException(
5256
'$kPullRequestNumberParam must be a number',
5357
);
5458
}
55-
final slug = RepositorySlug(owner, repo);
56-
final github = await config.createGitHubClient(slug: slug);
57-
final pullRequest = await github.pullRequests.get(slug, prNumber);
58-
await _scheduler.triggerPresubmitTargets(
59-
pullRequest: pullRequest,
60-
builderTriggerList: builderList,
61-
);
59+
// final slug = RepositorySlug(owner, repo);
60+
// final github = await config.createGitHubClient(slug: slug);
61+
// final pullRequest = await github.pullRequests.get(slug, prNumber);
62+
63+
// WARNING: Did you need this? If so, reach out to codefu.
64+
// await _scheduler.triggerPresubmitTargets(
65+
// pullRequest: pullRequest,
66+
// builderTriggerList: builderList,
67+
// );
6268
return Response.emptyOk;
6369
}
6470

app_dart/lib/src/service/config.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@ interface class Config extends DynamicallyUpdatedConfig {
3838
/// Keep this in sync with the similar `Config` class in `auto_submit`.
3939
static const String kEmergencyLabel = 'emergency';
4040

41+
/// Label required to schedule CI tasks on pull requests as a security stop-gap.
42+
static const String kCicdLabel = 'CICD';
43+
44+
/// GitHub ID for the CICD label.
45+
static const int kCicdLabelIdFlutter = 10404216443;
46+
47+
/// GitHub ID for the CICD label.
48+
static const int kCicdLabelIdPackages = 10404974695;
49+
50+
/// All valid labels for triggering cicd
51+
static const kCicdLabelIds = [kCicdLabelIdFlutter, kCicdLabelIdPackages];
52+
4153
/// Validates that CI tasks were successfully created from the .ci.yaml file.
4254
///
4355
/// If this check fails, it means Cocoon failed to fully populate the list of

0 commit comments

Comments
 (0)