bisector: Defer failures
Don't fail immediately if there's trouble processing one builder.
Continue to process other builders and launch builds for them and only
then fail.
Bug: b/401921575
Change-Id: I5338e788db441660238a1f6e6c185b89b1d8e23d
Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/277773
Lint: Lint 🤖 <android-build-ayeaye@system.gserviceaccount.com>
Reviewed-by: Ted Pudlik <tpudlik@google.com>
Commit-Queue: Rob Mohr <mohrr@google.com>
Presubmit-Verified: CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/recipes/bisector.py b/recipes/bisector.py
index 4f10025..5a60be0 100644
--- a/recipes/bisector.py
+++ b/recipes/bisector.py
@@ -68,6 +68,7 @@
'fuchsia/gerrit',
'fuchsia/gitiles',
'recipe_engine/buildbucket',
+ 'recipe_engine/defer',
'recipe_engine/luci_config',
'recipe_engine/properties',
'recipe_engine/step',
@@ -300,121 +301,134 @@
repos: dict[Remote, tuple[str, ...]] = {}
- for bucket in bb_cfg.buckets:
- if not include_bucket(props, bucket.name):
- if bucket.swarming.builders:
- api.step(
- f'excluding {len(bucket.swarming.builders)} builders in '
- f'bucket {bucket.name}',
- None,
- )
- continue
-
- with api.step.nest(bucket.name) as pres:
- included = excluded = 0
-
- for builder in bucket.swarming.builders:
- with api.step.nest(builder.name):
- result = process_builder(
- api=api,
- bucket_name=bucket.name,
- builder=builder,
- repos=repos,
- max_age_days=props.max_age_days,
- num_builds=props.num_builds,
+ with api.defer.context() as defer:
+ for bucket in bb_cfg.buckets:
+ if not include_bucket(props, bucket.name):
+ if bucket.swarming.builders:
+ api.step(
+ f'excluding {len(bucket.swarming.builders)} builders in '
+ f'bucket {bucket.name}',
+ None,
)
- if result.included:
- included += 1
- else:
- excluded += 1
- builds_to_launch.extend(result.triggers)
+ continue
- pres.step_summary_text = f'included {included}, excluded {excluded}'
+ with api.step.nest(bucket.name) as pres:
+ included = excluded = 0
- # These don't help users much but are useful for testing.
- api.step.empty(f'included {included}')
- api.step.empty(f'excluded {excluded}')
+ for builder in bucket.swarming.builders:
+ with api.step.nest(builder.name):
+ deferred_result = defer(
+ process_builder,
+ api=api,
+ bucket_name=bucket.name,
+ builder=builder,
+ repos=repos,
+ max_age_days=props.max_age_days,
+ num_builds=props.num_builds,
+ )
+ if deferred_result.is_ok():
+ result = deferred_result.result()
+ if result.included:
+ included += 1
+ else:
+ excluded += 1
+ builds_to_launch.extend(result.triggers)
- if not builds_to_launch:
- api.step('nothing to launch', None)
- return result_pb.RawResult(
- summary_markdown='nothing to launch',
- status=common_pb.SUCCESS,
- )
+ pres.step_summary_text = (
+ f'included {included}, excluded {excluded}'
+ )
- bb_requests: list[builds_service_pb.ScheduleBuildRequest] = []
+ # These don't help users much but are useful for testing.
+ api.step.empty(f'included {included}')
+ api.step.empty(f'excluded {excluded}')
- for trigger in builds_to_launch:
- host = api.gerrit.host_from_remote_url(trigger.remote)
- host = host.replace('-review.', '.')
+ if not builds_to_launch:
+ api.step('nothing to launch', None)
+ return result_pb.RawResult(
+ summary_markdown='nothing to launch',
+ status=common_pb.SUCCESS,
+ )
- bb_requests.append(
- api.buildbucket.schedule_request(
- bucket=trigger.bucket,
- builder=trigger.builder,
- gitiles_commit=common_pb.GitilesCommit(
- host=host,
- project=api.gerrit.project_from_remote_url(trigger.remote),
- id=trigger.commit,
- ref=f'refs/heads/{trigger.ref}',
+ bb_requests: list[builds_service_pb.ScheduleBuildRequest] = []
+
+ for trigger in builds_to_launch:
+ host = api.gerrit.host_from_remote_url(trigger.remote)
+ host = host.replace('-review.', '.')
+
+ bb_requests.append(
+ api.buildbucket.schedule_request(
+ bucket=trigger.bucket,
+ builder=trigger.builder,
+ gitiles_commit=common_pb.GitilesCommit(
+ host=host,
+ project=api.gerrit.project_from_remote_url(
+ trigger.remote,
+ ),
+ id=trigger.commit,
+ ref=f'refs/heads/{trigger.ref}',
+ ),
+ tags=[
+ common_pb.StringPair(
+ key='user_agent',
+ value='bisector',
+ ),
+ ],
),
- tags=[
- common_pb.StringPair(key='user_agent', value='bisector'),
- ],
- ),
- )
+ )
- if props.dry_run:
- with api.step.nest('dry-run, not launching builds'):
+ if props.dry_run:
+ with api.step.nest('dry-run, not launching builds'):
+ links: list[tuple[str, str]] = []
+
+ for req in bb_requests:
+ bucket_builder: str = (
+ f'{req.builder.bucket}/{req.builder.builder}'
+ )
+ pres = api.step.empty(bucket_builder).presentation
+ builder_link = (
+ f'https://ci.chromium.org/ui/p/{req.builder.project}/'
+ f'builders/{bucket_builder}'
+ )
+ pres.links['builder'] = builder_link
+ links.append((bucket_builder, builder_link))
+
+ commit = req.gitiles_commit
+ pres.links['commit'] = (
+ f'https://{commit.host}/{commit.project}/+/{commit.id}'
+ )
+
+ links_combined: str = ''.join(
+ f'<br/>[{name}]({link})' for name, link in links
+ )
+
+ return result_pb.RawResult(
+ summary_markdown=f'dry-run, would have launched: {links_combined}',
+ status=common_pb.SUCCESS,
+ )
+
+ with api.step.nest('launch') as pres:
links: list[tuple[str, str]] = []
- for req in bb_requests:
- bucket_builder: str = (
- f'{req.builder.bucket}/{req.builder.builder}'
- )
- pres = api.step.empty(bucket_builder).presentation
- builder_link = (
- f'https://ci.chromium.org/ui/p/{req.builder.project}/'
- f'builders/{bucket_builder}'
- )
- pres.links['builder'] = builder_link
- links.append((bucket_builder, builder_link))
+ if bb_requests:
+ deferred_result = defer(api.buildbucket.schedule, bb_requests)
+ if deferred_result.is_ok():
+ builds: list[build_pb.Build] = deferred_result.result()
+ for build in builds:
+ bucket_builder: str = (
+ f'{build.builder.bucket}/{build.builder.builder}'
+ )
+ link: str = api.buildbucket.build_url(build_id=build.id)
+ pres.links[bucket_builder] = link
+ links.append((bucket_builder, link))
- commit = req.gitiles_commit
- pres.links['commit'] = (
- f'https://{commit.host}/{commit.project}/+/{commit.id}'
- )
+ links_combined: str = ''.join(
+ f'<br/>[{name}]({link})' for name, link in links
+ )
- links_combined: str = ''.join(
- f'<br/>[{name}]({link})' for name, link in links
- )
-
- return result_pb.RawResult(
- summary_markdown=f'dry-run, would have launched: {links_combined}',
- status=common_pb.SUCCESS,
- )
-
- with api.step.nest('launch') as pres:
- links: list[tuple[str, str]] = []
-
- if bb_requests:
- builds: list[build_pb.Build] = api.buildbucket.schedule(bb_requests)
- for build in builds:
- bucket_builder: str = (
- f'{build.builder.bucket}/{build.builder.builder}'
- )
- link: str = api.buildbucket.build_url(build_id=build.id)
- pres.links[bucket_builder] = link
- links.append((bucket_builder, link))
-
- links_combined: str = ''.join(
- f'<br/>[{name}]({link})' for name, link in links
- )
-
- return result_pb.RawResult(
- summary_markdown=f'launched: {links_combined}',
- status=common_pb.SUCCESS,
- )
+ return result_pb.RawResult(
+ summary_markdown=f'launched: {links_combined}',
+ status=common_pb.SUCCESS,
+ )
def GenTests(api) -> Generator[recipe_test_api.TestData, None, None]: