diff --git a/app_dart/lib/src/request_handlers/presubmit_luci_subscription_v2.dart b/app_dart/lib/src/request_handlers/presubmit_luci_subscription_v2.dart index 5964a011f..a0bdbc81f 100644 --- a/app_dart/lib/src/request_handlers/presubmit_luci_subscription_v2.dart +++ b/app_dart/lib/src/request_handlers/presubmit_luci_subscription_v2.dart @@ -143,10 +143,16 @@ class PresubmitLuciSubscriptionV2 extends SubscriptionHandlerV2 { sha: userData['commit_sha'] as String, ); late CiYaml ciYaml; - if (commit.branch == Config.defaultBranch(commit.slug)) { - ciYaml = await scheduler.getCiYaml(commit, validate: true); - } else { - ciYaml = await scheduler.getCiYaml(commit); + try { + if (commit.branch == Config.defaultBranch(commit.slug)) { + ciYaml = await scheduler.getCiYaml(commit, validate: true); + } else { + ciYaml = await scheduler.getCiYaml(commit); + } + } on FormatException { + // If ci.yaml no longer passes validation (for example, because a builder + // has been removed), ensure no retries. + return 0; } // Do not block on the target not found. diff --git a/app_dart/test/request_handlers/presubmit_luci_subscription_v2_test.dart b/app_dart/test/request_handlers/presubmit_luci_subscription_v2_test.dart index 4c2144637..7b25fead5 100644 --- a/app_dart/test/request_handlers/presubmit_luci_subscription_v2_test.dart +++ b/app_dart/test/request_handlers/presubmit_luci_subscription_v2_test.dart @@ -300,4 +300,58 @@ void main() { ), ).called(1); }); + + test('Build not rescheduled if ci.yaml fails validation.', () async { + scheduler.failCiYamlValidation = true; + when( + mockGithubChecksService.updateCheckStatus( + build: anyNamed('build'), + userDataMap: anyNamed('userDataMap'), + luciBuildService: anyNamed('luciBuildService'), + slug: anyNamed('slug'), + rescheduled: false, + ), + ).thenAnswer((_) async => true); + when(mockGithubChecksService.taskFailed(any)).thenAnswer((_) => true); + when(mockGithubChecksService.currentAttempt(any)).thenAnswer((_) => 1); + + final Map userDataMap = { + 'repo_owner': 'flutter', + 'commit_branch': Config.defaultBranch(Config.flutterSlug), + 'commit_sha': 'abc', + 'repo_name': 'flutter', + }; + + tester.message = createPushMessageV2( + Int64(1), + status: bbv2.Status.SUCCESS, + builder: 'Linux C', + userData: userDataMap, + ); + + final bbv2.BuildsV2PubSub buildsV2PubSub = createBuild( + Int64(1), + status: bbv2.Status.SUCCESS, + builder: 'Linux C', + ); + + await tester.post(handler); + verifyNever( + mockLuciBuildService.rescheduleBuild( + build: buildsV2PubSub.build, + builderName: 'Linux C', + userDataMap: userDataMap, + rescheduleAttempt: 1, + ), + ); + verify( + mockGithubChecksService.updateCheckStatus( + build: anyNamed('build'), + userDataMap: anyNamed('userDataMap'), + luciBuildService: anyNamed('luciBuildService'), + slug: anyNamed('slug'), + rescheduled: false, + ), + ).called(1); + }); } diff --git a/app_dart/test/src/service/fake_scheduler_v2.dart b/app_dart/test/src/service/fake_scheduler_v2.dart index f06e6e9e4..5bf56285b 100644 --- a/app_dart/test/src/service/fake_scheduler_v2.dart +++ b/app_dart/test/src/service/fake_scheduler_v2.dart @@ -45,14 +45,22 @@ class FakeSchedulerV2 extends SchedulerV2 { /// [CiYaml] value to be injected on [getCiYaml]. CiYaml? ciYaml; + /// If true, getCiYaml will throw a [FormatException] when validation is + /// enforced, simulating failing validation. + bool failCiYamlValidation = false; + @override Future getCiYaml( Commit commit, { CiYaml? totCiYaml, RetryOptions? retryOptions, bool validate = false, - }) async => - ciYaml ?? _defaultConfig; + }) async { + if (validate && failCiYamlValidation) { + throw const FormatException('Failed validation!'); + } + return ciYaml ?? _defaultConfig; + } @override Future generateTotCommit({required String branch, required RepositorySlug slug}) async {