Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Schedule Merge Queue builds #4068

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app_dart/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@

# Conventional directory for build output.
build/

coverage/
18 changes: 14 additions & 4 deletions app_dart/lib/src/request_handlers/github/webhook_subscription.dart
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ class GithubWebhookSubscription extends SubscriptionHandler {
final mergeGroupEvent = MergeGroupEvent.fromJson(request);
final MergeGroupEvent(:mergeGroup, :action) = mergeGroupEvent;
final headSha = mergeGroup.headSha;
final slug = mergeGroupEvent.repository!.slug();

// See the API reference:
// https://docs.github.com/en/webhooks/webhook-events-and-payloads#merge_group
Expand All @@ -234,24 +235,33 @@ class GithubWebhookSubscription extends SubscriptionHandler {
// into the main branch. Cocoon should kick off CI jobs needed to verify
// the PR group.
case 'checks_requested':
log.fine('Simulating checks requests for merge queue @ $headSha');
log.fine('Checks requests for merge queue @ $headSha');

if (!await _shaExistsInGob(slug, headSha)) {
throw InternalServerError(
'$slug/$headSha was not found on GoB. Failing so this event can be retried',
);
}
log.fine('$slug/$headSha was found on GoB mirror. Scheduling merge group tasks');
await scheduler.triggerMergeGroupTargets(mergeGroupEvent: mergeGroupEvent);
break;

// A merge group was deleted. This can happen when a PR is pulled from the
// merge queue. All CI jobs pertaining to this merge group should be
// stopped to save CI resources, as Github will no longer merge this group
// into the main branch.
case 'destroyed':
log.fine('Simulating destruction of a merge group @ $headSha');
log.fine('Merge group destroyed for $slug/$headSha');
await scheduler.cancelMergeGroupTargets(headSha: headSha);
break;
}
}

Future<bool> _commitExistsInGob(PullRequest pr) async {
final RepositorySlug slug = pr.base!.repo!.slug();
final String sha = pr.mergeCommitSha!;
return _shaExistsInGob(slug, sha);
}

Future<bool> _shaExistsInGob(RepositorySlug slug, String sha) async {
final GerritCommit? gobCommit = await gerritService.findMirroredCommit(slug, sha);
return gobCommit != null;
}
Expand Down
148 changes: 146 additions & 2 deletions app_dart/lib/src/service/luci_build_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,63 @@ class LuciBuildService {
return <Tuple<Target, Task, int>>[];
}

/// Schedules [targets] for building of prod artifacts while in a merge queue.
Future<void> scheduleMergeGroupBuilds({
required Commit commit,
required List<Target> targets,
}) async {
final buildRequests = <bbv2.BatchRequest_Request>[];

final Set<String> availableBuilderSet;
try {
availableBuilderSet = await getAvailableBuilderSet(
project: 'flutter',
bucket: 'prod',
);
} catch (error) {
log.warning('Failed to get buildbucket builder list', error);
throw 'Failed to get buildbucket builder list due to $error';
jtmcdole marked this conversation as resolved.
Show resolved Hide resolved
}
log.info('Available builder list: $availableBuilderSet');
for (var target in targets) {
// Non-existing builder target will be skipped from scheduling.
jtmcdole marked this conversation as resolved.
Show resolved Hide resolved
if (!availableBuilderSet.contains(target.value.name)) {
log.warning(
'Found no available builder for ${target.value.name}, commit ${commit.sha}',
);
continue;
}
log.info(
'create postsubmit schedule request for target: ${target.value} in commit ${commit.sha}',
);

final scheduleBuildRequest = await _createMergeGroupScheduleBuild(
commit: commit,
target: target,
);
buildRequests.add(bbv2.BatchRequest_Request(scheduleBuild: scheduleBuildRequest));
log.info(
'created postsubmit schedule request for target: ${target.value} in commit ${commit.sha}',
);
}

final batchRequest = bbv2.BatchRequest(requests: buildRequests);
log.fine(batchRequest);
final List<String> messageIds;

try {
messageIds = await pubsub.publish(
'cocoon-scheduler-requests',
batchRequest.toProto3Json(),
);
log.info('Published $messageIds for commit ${commit.sha}');
} catch (error) {
log.severe('Failed to publish message to pub/sub due to $error');
rethrow;
}
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].
bbv2.ScheduleBuildRequest _createPresubmitScheduleBuild({
Expand Down Expand Up @@ -991,15 +1048,102 @@ class LuciBuildService {
);
}

/// Creates a build request for a commit in a merge queue which will notify
/// presubmit channels.
Future<bbv2.ScheduleBuildRequest> _createMergeGroupScheduleBuild({
required Commit commit,
required Target target,
int priority = kDefaultPriority,
}) async {
log.info(
'Creating merge group schedule builder for ${target.value.name} on commit ${commit.sha}',
);
final tags = <bbv2.StringPair>[];
tags.addAll([
bbv2.StringPair(
key: 'buildset',
value: 'commit/git/${commit.sha}',
),
bbv2.StringPair(
key: 'buildset',
value: 'commit/gitiles/flutter.googlesource.com/mirrors/${commit.slug.name}/+/${commit.sha}',
),
bbv2.StringPair(
key: 'user_agent',
value: 'flutter-cocoon',
),
bbv2.StringPair(
key: 'scheduler_job_id',
value: 'flutter/${target.value.name}',
),
bbv2.StringPair(
key: 'current_attempt',
value: '1',
),
]);

log.info(
'Scheduling builder: ${target.value.name} for commit ${commit.sha}',
);

final rawUserData = <String, dynamic>{};

await createPostsubmitCheckRun(
commit,
target,
rawUserData,
);

final processedProperties = target.getProperties().cast<String, Object?>();
processedProperties['git_branch'] = commit.branch!;
final cipdExe = 'refs/heads/${commit.branch}';
processedProperties['exe_cipd_version'] = cipdExe;
processedProperties['is_fusion'] = 'true';

final propertiesStruct = bbv2.Struct()..mergeFromProto3Json(processedProperties);
final requestedDimensions = target.getDimensions();
final executable = bbv2.Executable(cipdVersion: cipdExe);

log.info(
'Constructing the merge group schedule build request for ${target.value.name} on commit ${commit.sha}.',
);

return bbv2.ScheduleBuildRequest(
builder: bbv2.BuilderID(
project: 'flutter',
bucket: target.getBucket(),
builder: target.value.name,
),
dimensions: requestedDimensions,
exe: executable,
gitilesCommit: bbv2.GitilesCommit(
project: 'mirrors/${commit.slug.name}',
host: 'flutter.googlesource.com',
ref: 'refs/heads/${commit.branch}',
id: commit.sha,
),
notify: bbv2.NotificationConfig(
// IMPORTANT: We're not post-submit yet, so we want to handle updates to
// the MQ differently.
pubsubTopic: 'projects/flutter-dashboard/topics/build-bucket-presubmit',
userData: UserData.encodeUserDataToBytes(rawUserData),
),
tags: tags,
properties: propertiesStruct,
priority: priority,
);
}

/// Creates postsubmit check runs for prod targets in supported repositories.
Future<void> createPostsubmitCheckRun(
Commit commit,
Target target,
Map<String, dynamic> rawUserData,
) async {
// We are not tracking this check run in the PrCheckRuns firestore doc because
// there is no PR to track here.
final github.CheckRun checkRun = await githubChecksUtil.createCheckRun(
// there is no PR to look up later. The check run is important because it
// informs the staging document setup for Merge Groups in triggerMergeGroupTargets.
final checkRun = await githubChecksUtil.createCheckRun(
config,
target.slug,
commit.sha!,
Expand Down
Loading