| # Copyright 2026 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| """Trigger another build from a recipe based on input properties.""" |
| |
| from __future__ import annotations |
| |
| import dataclasses |
| from collections.abc import Sequence |
| |
| from recipe_engine import recipe_api |
| |
| from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb |
| from PB.recipe_modules.pigweed.trigger.options import Options |
| |
| from RECIPE_MODULES.pigweed.checkout.api import CheckoutContext |
| from RECIPE_MODULES.recipe_engine.buildbucket.api import Inherit |
| |
| |
| @dataclasses.dataclass |
| class Change: |
| remote: str | None |
| branch: str | None |
| ref: str | None |
| |
| |
| class TriggerApi(recipe_api.RecipeApi): |
| """Trigger another build from a recipe based on input properties.""" |
| |
| Change = Change |
| |
| def gitiles_commit( |
| self, |
| change: Change, |
| verbose: bool = False, |
| prefer_inherit: bool = True, |
| ) -> common_pb.GitilesCommit | None: |
| """Construct a gitiles commit from the checkout.""" |
| commit = self.m.buildbucket.build.input.gitiles_commit |
| if commit and commit.id: |
| if verbose: |
| pres = self.m.step.empty('gitiles commit present').presentation |
| pres.links[commit.id] = ( |
| f'https://{commit.host}/{commit.project}/+/{commit.id}' |
| ) |
| if prefer_inherit: |
| return Inherit.INHERIT |
| return commit |
| |
| if verbose: |
| pres = self.m.step.empty('change').presentation |
| pres.step_summary_text = repr(vars(change)) |
| |
| if change.remote and change.branch and len(change.ref or '') == 40: |
| gerrit_host = self.m.gerrit.host_from_remote_url(change.remote) |
| gitiles_host = gerrit_host.replace('-review.', '.') |
| gitiles_commit = common_pb.GitilesCommit( |
| host=gitiles_host, |
| project=self.m.gerrit.project_from_remote_url(change.remote), |
| id=change.ref, |
| ref=f'refs/heads/{change.branch}', |
| ) |
| |
| if verbose: |
| pres = self.m.step.empty( |
| 'generated commit reference' |
| ).presentation |
| pres.links[gitiles_commit.id] = ( |
| f'https://{gitiles_commit.host}/{gitiles_commit.project}' |
| f'/+/{gitiles_commit.id}' |
| ) |
| return gitiles_commit |
| |
| if verbose: # pragma: no cover |
| self.m.step.empty('returning none') |
| return None # pragma: no cover |
| |
| def __call__( |
| self, |
| triggers: Sequence[Options], |
| checkout: CheckoutContext, |
| verbose: bool = False, |
| ) -> None: |
| if not triggers: |
| return |
| |
| calculated_gitiles_commit: common_pb.GitilesCommit | None = None |
| |
| with ( |
| self.m.step.nest('trigger builders'), |
| self.m.defer.context() as defer, |
| ): |
| for trigger in triggers: |
| assert trigger.builder |
| combined = [trigger.project, trigger.bucket, trigger.builder] |
| name = '/'.join(x for x in combined if x) |
| |
| with self.m.step.nest(name): |
| build = self.m.buildbucket.build |
| |
| gitiles_commit: common_pb.GitilesCommit | None = None |
| if trigger.inherit_gitiles_commit: |
| if trigger.create_gitiles_commit_if_missing: |
| assert len(checkout.changes) == 1 |
| calculated_gitiles_commit = self.gitiles_commit( |
| checkout.changes[0], |
| verbose=verbose, |
| ) |
| gitiles_commit = calculated_gitiles_commit |
| |
| request = self.m.buildbucket.schedule_request( |
| project=trigger.project or build.builder.project, |
| bucket=trigger.bucket or build.builder.bucket, |
| builder=trigger.builder, |
| gitiles_commit=gitiles_commit, |
| gerrit_changes=( |
| Inherit.INHERIT |
| if trigger.inherit_gerrit_changes |
| else () |
| ), |
| can_outlive_parent=True, |
| properties={'parent_build_id': f'{build.id}'}, |
| ) |
| defer(self.m.buildbucket.schedule, [request]) |