blob: 3231ffa4ffcf973335298f1da1829b06cf17552c [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:async';
import 'package:github/github.dart';
import 'package:meta/meta.dart';
import '../datastore/cocoon_config.dart';
import '../model/appengine/github_build_status_update.dart';
import '../model/appengine/task.dart';
import '../request_handling/api_request_handler.dart';
import '../request_handling/authentication.dart';
import '../request_handling/body.dart';
import '../service/datastore.dart';
import '../service/luci.dart';
@immutable
class PushEngineStatusToGithub extends ApiRequestHandler<Body> {
const PushEngineStatusToGithub(
Config config,
AuthenticationProvider authenticationProvider, {
@visibleForTesting LuciServiceProvider luciServiceProvider,
@visibleForTesting DatastoreServiceProvider datastoreProvider,
}) : luciServiceProvider = luciServiceProvider ?? _createLuciService,
datastoreProvider =
datastoreProvider ?? DatastoreService.defaultProvider,
super(config: config, authenticationProvider: authenticationProvider);
final LuciServiceProvider luciServiceProvider;
final DatastoreServiceProvider datastoreProvider;
static LuciService _createLuciService(ApiRequestHandler<dynamic> handler) {
return LuciService(
config: handler.config,
clientContext: handler.authContext.clientContext,
);
}
@override
Future<Body> get() async {
if (authContext.clientContext.isDevelopmentEnvironment) {
// Don't push GitHub status from the local dev server.
return Body.empty;
}
final LuciService luciService = luciServiceProvider(this);
final Map<LuciBuilder, List<LuciTask>> luciTasks =
await luciService.getRecentTasks(repo: 'engine');
String latestStatus = GithubBuildStatusUpdate.statusSuccess;
for (List<LuciTask> tasks in luciTasks.values) {
latestStatus = _getLatestStatus(tasks);
if (latestStatus == GithubBuildStatusUpdate.statusFailure) {
break;
}
}
final RepositorySlug slug = RepositorySlug('flutter', 'engine');
final DatastoreService datastore = datastoreProvider(config.db);
final GitHub github =
await config.createGitHubClient(slug.owner, slug.name);
final List<GithubBuildStatusUpdate> updates = <GithubBuildStatusUpdate>[];
await for (PullRequest pr in github.pullRequests.list(slug)) {
final GithubBuildStatusUpdate update =
await datastore.queryLastStatusUpdate(slug, pr);
if (update.status != latestStatus) {
log.debug(
'Updating status of ${slug.fullName}#${pr.number} from ${update.status}');
final CreateStatus request = CreateStatus(latestStatus);
request.targetUrl =
'https://ci.chromium.org/p/flutter/g/engine/console';
request.context = 'luci-engine';
if (latestStatus != GithubBuildStatusUpdate.statusSuccess) {
request.description =
'Flutter build is currently broken. Please do not merge this '
'PR unless it contains a fix to the broken build.';
}
try {
await github.repositories.createStatus(slug, pr.head.sha, request);
update.status = latestStatus;
update.updates += 1;
update.updateTimeMillis = DateTime.now().millisecondsSinceEpoch;
updates.add(update);
} catch (error) {
log.error(
'Failed to post status update to ${slug.fullName}#${pr.number}: $error');
}
}
}
await datastore.insert(updates);
log.debug('Committed all updates');
return Body.empty;
}
String _getLatestStatus(List<LuciTask> tasks) {
for (LuciTask task in tasks) {
switch (task.status) {
case Task.statusFailed:
return GithubBuildStatusUpdate.statusFailure;
case Task.statusSucceeded:
return GithubBuildStatusUpdate.statusSuccess;
}
}
return GithubBuildStatusUpdate.statusSuccess;
}
}