blob: 8b35a98b8149b1339c45f84756764504bacc5a4b [file] [log] [blame]
// Copyright 2019 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:convert';
import 'package:github/github.dart';
import 'package:meta/meta.dart';
import '../model/luci/push_message.dart';
import '../request_handling/authentication.dart';
import '../request_handling/body.dart';
import '../request_handling/subscription_handler.dart';
import '../service/buildbucket.dart';
import '../service/github_checks_service.dart';
import '../service/logging.dart';
import '../service/luci_build_service.dart';
/// An endpoint for listening to LUCI status updates for scheduled builds.
///
/// [ScheduleBuildRequest.notify] property is set to tell LUCI to use this
/// PubSub topic. LUCI then publishes updates about build status to that topic,
/// which we listen to on the github-updater subscription. When new messages
/// arrive, they are posted to this web service.
///
/// The PubSub subscription is set up here:
/// https://console.cloud.google.com/cloudpubsub/subscription/detail/github-updater?project=flutter-dashboard
///
/// This endpoint is responsible for updating GitHub with the status of
/// completed builds from LUCI.
@immutable
class PresubmitLuciSubscription extends SubscriptionHandler {
/// Creates an endpoint for listening to LUCI status updates.
const PresubmitLuciSubscription({
required super.cache,
required super.config,
required this.buildBucketClient,
required this.luciBuildService,
required this.githubChecksService,
AuthenticationProvider? authProvider,
}) : super(subscriptionName: 'github-updater');
final BuildBucketClient buildBucketClient;
final LuciBuildService luciBuildService;
final GithubChecksService githubChecksService;
@override
Future<Body> post() async {
RepositorySlug slug;
final String data = message.data!;
BuildPushMessage buildPushMessage;
try {
buildPushMessage =
BuildPushMessage.fromJson(json.decode(String.fromCharCodes(base64.decode(data))) as Map<String, dynamic>);
} on FormatException {
buildPushMessage = BuildPushMessage.fromJson(json.decode(data) as Map<String, dynamic>);
}
final Build build = buildPushMessage.build!;
final String builderName = build.tagsByName('builder').single;
log.fine('Available tags: ${build.tags.toString()}');
// Skip status update if we can not get the sha tag.
if (build.tagsByName('buildset').isEmpty) {
log.warning('Buildset tag not included, skipping Status Updates');
return Body.empty;
}
log.fine('Setting status: ${buildPushMessage.toJson()} for $builderName');
Map<String, dynamic>? userData;
try {
userData = jsonDecode(buildPushMessage.userData!) as Map<String, dynamic>;
} on FormatException {
userData = jsonDecode(String.fromCharCodes(base64.decode(buildPushMessage.userData!))) as Map<String, dynamic>;
}
if (userData.containsKey('repo_owner') && userData.containsKey('repo_name')) {
// Message is coming from a github checks api enabled repo. We need to
// create the slug from the data in the message and send the check status
// update.
slug = RepositorySlug(
userData['repo_owner'] as String,
userData['repo_name'] as String,
);
await githubChecksService.updateCheckStatus(
buildPushMessage,
luciBuildService,
slug,
);
} else {
log.shout('This repo does not support checks API');
}
return Body.empty;
}
}