blob: b778c5d48ff4188c8f4f512fc72fae068efe0573 [file] [log] [blame]
// Copyright 2023 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:cocoon_service/src/model/luci/buildbucket.dart';
import 'package:meta/meta.dart';
import '../../cocoon_service.dart';
import '../model/appengine/task.dart';
import '../request_handling/subscription_handler.dart';
import '../service/datastore.dart';
import '../service/logging.dart';
/// TODO(drewroengoogle): Make this subscription generic so we can accept more
/// than just dart-internal builds.
///
/// An endpoint for listening to build updates for dart-internal builds and
/// saving the results to the datastore.
///
/// The PubSub subscription is set up here:
/// https://console.cloud.google.com/cloudpubsub/subscription/detail/dart-internal-build-results-sub?project=flutter-dashboard
@immutable
class DartInternalSubscription extends SubscriptionHandler {
/// Creates an endpoint for listening for dart-internal build results.
/// The message should contain a single buildbucket id
const DartInternalSubscription({
required super.cache,
required super.config,
super.authProvider,
required this.buildBucketClient,
@visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
}) : super(subscriptionName: 'dart-internal-build-results-sub');
final BuildBucketClient buildBucketClient;
final DatastoreServiceProvider datastoreProvider;
@override
Future<Body> post() async {
final DatastoreService datastore = datastoreProvider(config.db);
if (message.data == null) {
log.info('no data in message');
return Body.empty;
}
final dynamic buildData = json.decode(message.data!);
log.info('Build data json: $buildData');
if (buildData['build'] == null) {
log.info('no build information in message');
return Body.empty;
}
final String project = buildData['build']['builder']['project'];
final String bucket = buildData['build']['builder']['bucket'];
final String builder = buildData['build']['builder']['builder'];
// This should already be covered by the pubsub filter, but adding an additional check
// to ensure we don't process builds that aren't from dart-internal/flutter.
if (project != 'dart-internal' || bucket != 'flutter') {
log.info('Ignoring build not from dart-internal/flutter bucket');
return Body.empty;
}
// Only publish the parent release_builder builds to the datastore.
// TODO(drewroengoogle): Remove this regex in favor of supporting *all* dart-internal build results.
// Issue: https://github.com/flutter/flutter/issues/134674
final regex =
RegExp(r'(Linux|Mac|Windows)\s+(engine_release_builder|packaging_release_builder|flutter_release_builder)');
if (!regex.hasMatch(builder)) {
log.info('Ignoring builder that is not a release builder');
return Body.empty;
}
final String buildbucketId = buildData['build']['id'];
log.info('Creating build request object with build id $buildbucketId');
final GetBuildRequest request = GetBuildRequest(
id: buildbucketId,
);
log.info(
'Calling buildbucket api to get build data for build $buildbucketId',
);
final Build build = await buildBucketClient.getBuild(request);
log.info('Checking for existing task in datastore');
final Task? existingTask = await datastore.getTaskFromBuildbucketBuild(build);
late Task taskToInsert;
if (existingTask != null) {
log.info('Updating Task from existing Task');
existingTask.updateFromBuildbucketBuild(build);
taskToInsert = existingTask;
} else {
log.info('Creating Task from Buildbucket result');
taskToInsert = await Task.fromBuildbucketBuild(build, datastore);
}
log.info('Inserting Task into the datastore: ${taskToInsert.toString()}');
await datastore.insert(<Task>[taskToInsert]);
return Body.forJson(taskToInsert.toString());
}
}