| # Copyright 2020 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Recipe for triggering toolchain buildbucket builds.""" |
| |
| from PB.go.chromium.org.luci.buildbucket.proto import ( |
| builds_service as builds_service_pb2, |
| ) |
| from PB.recipes.fuchsia.contrib.toolchain_trigger import InputProperties |
| |
| DEPS = [ |
| "fuchsia/buildbucket_util", |
| "recipe_engine/buildbucket", |
| "recipe_engine/properties", |
| "recipe_engine/scheduler", |
| "recipe_engine/step", |
| "recipe_engine/swarming", |
| ] |
| |
| PROPERTIES = InputProperties |
| |
| |
| def RunSteps(api, props): |
| # Trigger the builders. |
| builds = api.buildbucket.schedule( |
| [ |
| api.buildbucket.schedule_request( |
| builder, |
| bucket=props.bucket, |
| swarming_parent_run_id=api.swarming.task_id, |
| ) |
| for builder in props.builders |
| ] |
| + [ |
| api.buildbucket.schedule_request( |
| builder, |
| bucket=props.bucket, |
| ) |
| for builder in props.nonblocking_builders |
| ], |
| step_name="schedule builds", |
| ) |
| build_ids = [b.id for b in builds if b.builder.builder in props.builders] |
| build_dict = api.buildbucket.collect_builds( |
| build_ids, |
| interval=props.interval, |
| timeout=props.timeout, |
| step_name="collect builds", |
| ) |
| build_results = [build_dict[id] for id in build_ids] |
| api.buildbucket_util.display_builds( |
| step_name="display builds", builds=build_results, raise_on_failure=True |
| ) |
| |
| properties = {} |
| if props.enforce_output: |
| revision = retrieve_revision(api, build_results) |
| properties = {"version": "git_revision:" + revision} |
| |
| # Trigger next stage builders |
| api.scheduler.emit_trigger( |
| api.scheduler.BuildbucketTrigger(properties=properties), |
| project=api.buildbucket.build.builder.project, |
| jobs=props.triggers, |
| ) |
| |
| |
| def retrieve_revision(api, results): |
| revision = None |
| for result in results: |
| output = result.output.properties |
| if "git_revision" not in output: |
| raise api.step.StepFailure("revision not found in build") |
| output_revision = output["git_revision"] |
| if revision is None: |
| revision = output_revision |
| elif revision != output_revision: |
| raise api.step.StepFailure( |
| "revisions not unified: %s:%s" % (revision, output_revision) |
| ) |
| return revision |
| |
| |
| def GenTests(api): |
| default_properties = api.properties( |
| builders=["clang-linux-x64", "clang-mac-x64"], |
| nonblocking_builders=["clang-windows-x64"], |
| triggers=["clang-trigger"], |
| enforce_output=True, |
| ) |
| |
| test_build = api.buildbucket.ci_build_message( |
| status="SUCCESS", builder="clang-linux-x64" |
| ) |
| test_build.output.properties["git_revision"] = "a" * 40 |
| mock_schedule_data = api.buildbucket.simulated_schedule_output( |
| step_name="schedule builds", |
| batch_response=builds_service_pb2.BatchResponse( |
| responses=[ |
| dict( |
| schedule_build=dict( |
| id=test_build.id, |
| builder=dict(builder=test_build.builder.builder), |
| ) |
| ) |
| ], |
| ), |
| ) |
| mock_collect_data = api.buildbucket.simulated_collect_output( |
| step_name="collect builds", |
| builds=[test_build], |
| ) |
| |
| yield ( |
| api.buildbucket_util.test("default", repo="example", revision="a" * 40) |
| ) + default_properties + mock_schedule_data + mock_collect_data |
| |
| test_build_mismatch = api.buildbucket.ci_build_message( |
| build_id=test_build.id + 1, status="SUCCESS", builder="clang-linux-x64" |
| ) |
| test_build_mismatch.output.properties["git_revision"] = "b" * 40 |
| mock_schedule_data_mismatch = api.buildbucket.simulated_schedule_output( |
| step_name="schedule builds", |
| batch_response=builds_service_pb2.BatchResponse( |
| responses=[ |
| dict( |
| schedule_build=dict( |
| id=test_build.id, |
| builder=dict(builder=test_build.builder.builder), |
| ) |
| ), |
| dict( |
| schedule_build=dict( |
| id=test_build_mismatch.id, |
| builder=dict(builder=test_build_mismatch.builder.builder), |
| ) |
| ), |
| ], |
| ), |
| ) |
| mock_collect_data_mismatch = api.buildbucket.simulated_collect_output( |
| step_name="collect builds", |
| builds=[test_build, test_build_mismatch], |
| ) |
| |
| yield ( |
| api.buildbucket_util.test( |
| "mismatch", repo="example", revision="a" * 40, status="failure" |
| ) |
| ) + default_properties + mock_schedule_data_mismatch + mock_collect_data_mismatch |
| |
| test_build_no_output = api.buildbucket.ci_build_message( |
| status="SUCCESS", builder="clang-linux-x64" |
| ) |
| mock_schedule_data_no_output = api.buildbucket.simulated_schedule_output( |
| step_name="schedule builds", |
| batch_response=builds_service_pb2.BatchResponse( |
| responses=[ |
| dict( |
| schedule_build=dict( |
| id=test_build_no_output.id, |
| builder=dict(builder=test_build.builder.builder), |
| ) |
| ) |
| ], |
| ), |
| ) |
| mock_collect_data_no_output = api.buildbucket.simulated_collect_output( |
| step_name="collect builds", |
| builds=[test_build_no_output], |
| ) |
| |
| no_output_properties = api.properties( |
| builders=["clang-linux-x64", "clang-mac-x64"], |
| triggers=["clang-trigger"], |
| enforce_output=False, |
| ) |
| yield ( |
| api.buildbucket_util.test("no_output", repo="example", revision="a" * 40) |
| ) + no_output_properties + mock_schedule_data_no_output + mock_collect_data_no_output |
| |
| yield ( |
| api.buildbucket_util.test( |
| "no_output_with_enforce", |
| repo="example", |
| revision="a" * 40, |
| status="failure", |
| ) |
| ) + default_properties + mock_schedule_data_no_output + mock_collect_data_no_output |
| |
| test_build_failure = api.buildbucket.ci_build_message( |
| status="FAILURE", builder="clang-linux-x64" |
| ) |
| mock_failure_schedule_data = api.buildbucket.simulated_schedule_output( |
| step_name="schedule builds", |
| batch_response=builds_service_pb2.BatchResponse( |
| responses=[ |
| dict( |
| schedule_build=dict( |
| id=test_build_failure.id, |
| builder=dict(builder=test_build_failure.builder.builder), |
| ) |
| ) |
| ], |
| ), |
| ) |
| mock_failure_collect_data = api.buildbucket.simulated_collect_output( |
| step_name="collect builds", |
| builds=[test_build_failure], |
| ) |
| |
| yield ( |
| api.buildbucket_util.test( |
| "default_fail", repo="example", revision="a" * 40, status="failure" |
| ) |
| ) + default_properties + mock_failure_schedule_data + mock_failure_collect_data |