Eliminate unnecessary errors in the logs and fail gracefully if we cannot find build to reschedule. (#2401)
* Fail gracefully so we do not polute logs with stack traces.
* Add custom exception.
* Remove comments.
* Remove trailing white spaces.
* Formatting.
diff --git a/app_dart/lib/src/service/NoBuildFoundException.dart b/app_dart/lib/src/service/NoBuildFoundException.dart
new file mode 100644
index 0000000..6f107bd
--- /dev/null
+++ b/app_dart/lib/src/service/NoBuildFoundException.dart
@@ -0,0 +1,13 @@
+// Copyright 2022 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.
+
+class NoBuildFoundException implements Exception {
+ /// Create a custom exception for no build found Errors.
+ NoBuildFoundException(this.cause);
+
+ final String cause;
+
+ @override
+ String toString() => cause;
+}
diff --git a/app_dart/lib/src/service/luci_build_service.dart b/app_dart/lib/src/service/luci_build_service.dart
index e3762e3..a67e7b5 100644
--- a/app_dart/lib/src/service/luci_build_service.dart
+++ b/app_dart/lib/src/service/luci_build_service.dart
@@ -6,6 +6,7 @@
import 'dart:convert';
import 'dart:math';
+import 'package:cocoon_service/src/service/NoBuildFoundException.dart';
import 'package:github/github.dart' as github;
import 'package:github/hooks.dart';
@@ -379,31 +380,35 @@
final Iterable<Build> builds = await getTryBuilds(slug, sha, checkName);
- final Build build = builds.first;
+ if (builds.isNotEmpty) {
+ final Build build = builds.first;
- final String prString = build.tags!['buildset']!.firstWhere((String? element) => element!.startsWith('pr/git/'))!;
- final String cipdVersion = build.tags!['cipd_version']![0]!;
- final int prNumber = int.parse(prString.split('/')[2]);
+ final String prString = build.tags!['buildset']!.firstWhere((String? element) => element!.startsWith('pr/git/'))!;
+ final String cipdVersion = build.tags!['cipd_version']![0]!;
+ final int prNumber = int.parse(prString.split('/')[2]);
- final Map<String, dynamic> userData = <String, dynamic>{'check_run_id': githubCheckRun.id};
- final Map<String, dynamic>? properties = build.input!.properties;
- log.info('input ${build.input!} properties $properties');
+ final Map<String, dynamic> userData = <String, dynamic>{'check_run_id': githubCheckRun.id};
+ final Map<String, dynamic>? properties = build.input!.properties;
+ log.info('input ${build.input!} properties $properties');
- final ScheduleBuildRequest scheduleBuildRequest = _createPresubmitScheduleBuild(
- slug: slug,
- sha: sha,
- checkName: checkName,
- pullRequestNumber: prNumber,
- cipdVersion: cipdVersion,
- properties: properties,
- userData: userData,
- );
+ final ScheduleBuildRequest scheduleBuildRequest = _createPresubmitScheduleBuild(
+ slug: slug,
+ sha: sha,
+ checkName: checkName,
+ pullRequestNumber: prNumber,
+ cipdVersion: cipdVersion,
+ properties: properties,
+ userData: userData,
+ );
- final Build scheduleBuild = await buildBucketClient.scheduleBuild(scheduleBuildRequest);
+ final Build scheduleBuild = await buildBucketClient.scheduleBuild(scheduleBuildRequest);
- final String buildUrl = 'https://ci.chromium.org/ui/b/${scheduleBuild.id}';
- await githubChecksUtil.updateCheckRun(config, slug, githubCheckRun, detailsUrl: buildUrl);
- return scheduleBuild;
+ final String buildUrl = 'https://ci.chromium.org/ui/b/${scheduleBuild.id}';
+ await githubChecksUtil.updateCheckRun(config, slug, githubCheckRun, detailsUrl: buildUrl);
+ return scheduleBuild;
+ } else {
+ throw NoBuildFoundException('Unable to find try build.');
+ }
}
/// Gets [Build] using its [id] and passing the additional
diff --git a/app_dart/lib/src/service/scheduler.dart b/app_dart/lib/src/service/scheduler.dart
index e2f7235..0a8a7cf 100644
--- a/app_dart/lib/src/service/scheduler.dart
+++ b/app_dart/lib/src/service/scheduler.dart
@@ -5,6 +5,7 @@
import 'dart:math';
import 'dart:typed_data';
+import 'package:cocoon_service/src/service/NoBuildFoundException.dart';
import 'package:cocoon_service/src/service/build_status_provider.dart';
import 'package:cocoon_service/src/service/scheduler/policy.dart';
import 'package:gcloud/db.dart';
@@ -450,8 +451,12 @@
success = true;
}
} else {
- await luciBuildService.rescheduleUsingCheckRunEvent(checkRunEvent);
- success = true;
+ try {
+ await luciBuildService.rescheduleUsingCheckRunEvent(checkRunEvent);
+ success = true;
+ } on NoBuildFoundException {
+ log.warning('No build found to reschedule.');
+ }
}
log.fine('CheckName: $name State: $success');
diff --git a/app_dart/test/service/luci_build_service_test.dart b/app_dart/test/service/luci_build_service_test.dart
index 284e0b7..af50931 100644
--- a/app_dart/test/service/luci_build_service_test.dart
+++ b/app_dart/test/service/luci_build_service_test.dart
@@ -12,11 +12,13 @@
import 'package:cocoon_service/src/model/luci/buildbucket.dart';
import 'package:cocoon_service/src/model/luci/push_message.dart' as push_message;
import 'package:cocoon_service/src/request_handling/exceptions.dart';
+import 'package:cocoon_service/src/service/NoBuildFoundException.dart';
import 'package:cocoon_service/src/service/config.dart';
import 'package:cocoon_service/src/service/datastore.dart';
import 'package:cocoon_service/src/service/logging.dart';
import 'package:cocoon_service/src/service/luci_build_service.dart';
import 'package:github/github.dart';
+import 'package:cocoon_service/src/model/github/checks.dart' as cocoon_checks;
import 'package:logging/logging.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
@@ -28,6 +30,7 @@
import '../src/utilities/entity_generators.dart';
import '../src/utilities/mocks.dart';
import '../src/utilities/push_message.dart';
+import '../src/utilities/webhook_generators.dart';
void main() {
late FakeConfig config;
@@ -533,6 +536,31 @@
});
});
+ test('reschedule using checkrun event fails gracefully', () async {
+ when(mockGithubChecksUtil.createCheckRun(any, any, any, any))
+ .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1'));
+
+ when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
+ return const BatchResponse(
+ responses: <Response>[
+ Response(
+ searchBuilds: SearchBuildsResponse(
+ builds: <Build>[],
+ ),
+ )
+ ],
+ );
+ });
+
+ final pushMessage = generateCheckRunEvent(action: 'created', numberOfPullRequests: 1);
+ final Map<String, dynamic> jsonMap = json.decode(pushMessage.data!);
+ final Map<String, dynamic> jsonSubMap = json.decode(jsonMap['2']);
+ final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(jsonSubMap);
+
+ expect(() async => await service.rescheduleUsingCheckRunEvent(checkRunEvent),
+ throwsA(const TypeMatcher<NoBuildFoundException>()));
+ });
+
test('do not create postsubmit checkrun for bringup: true target', () async {
when(mockGithubChecksUtil.createCheckRun(any, any, any, any))
.thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1'));