| # Copyright 2021 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. |
| |
| """Ensure the MILESTONE file in an integration-like repository is up-to-date.""" |
| |
| from PB.go.chromium.org.luci.buildbucket.proto import build as build_pb2 |
| from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb2 |
| from PB.recipe_engine.result import RawResult |
| from PB.recipes.fuchsia.release.milestone_roller import InputProperties |
| |
| DEPS = [ |
| "fuchsia/auto_roller", |
| "fuchsia/checkout", |
| "fuchsia/release", |
| "recipe_engine/file", |
| "recipe_engine/properties", |
| "recipe_engine/step", |
| ] |
| |
| PROPERTIES = InputProperties |
| COMMIT_MESSAGE = """[roll] Roll MILESTONE from {current} to {target}""" |
| |
| |
| def RunSteps(api, props): |
| checkout = api.checkout.fuchsia_with_options( |
| manifest=props.manifest, |
| remote=props.remote, |
| project=props.project, |
| # Ignore the build input; we should always check out the manifest |
| # repository at HEAD before updating the manifest to reduce the |
| # likelihood of merge conflicts. |
| build_input=build_pb2.Build.Input(), |
| fetch_packages=False, |
| use_lock_file=True, |
| ) |
| project_path = checkout.project_path(props.project) |
| |
| with api.step.nest("resolve target milestone"): |
| branches = api.release.get_branches(repo_path=project_path, remotes=True) |
| if not branches: |
| return RawResult( |
| summary_markdown=f"{props.remote} does not have any release branches", |
| status=common_pb2.FAILURE, |
| ) |
| # The target milestone is always 1 ahead of the latest milestone branch. |
| target_milestone = 1 + ( |
| max( |
| api.release.get_milestone_from_branch(b, prefix=props.branch_prefix) |
| for b in branches |
| ) |
| ) |
| |
| current_milestone = api.release.get_major_number(repo_path=project_path) |
| if current_milestone == target_milestone: |
| api.step.empty("nothing to roll", step_text="milestone up-to-date") |
| return api.auto_roller.nothing_to_roll() |
| |
| # If the target milestone isn't equal to or exactly 1 greater than the |
| # current milestone, either the MILESTONE file and/or the latest |
| # milestone branch are invalid. Raise an exception and do not roll. |
| if current_milestone + 1 != target_milestone: |
| return RawResult( |
| summary_markdown="invalid milestone numbers: target milestone " |
| f"{int(target_milestone)} is not an increment of current milestone {int(current_milestone)}", |
| status=common_pb2.INFRA_FAILURE, |
| ) |
| |
| api.release.write_major_number(target_milestone, repo_path=project_path) |
| |
| if props.milestone_branches_path: |
| configured_branches = api.file.read_json( |
| "read milestone branch config", |
| project_path / props.milestone_branches_path, |
| test_data=[f"{props.branch_prefix}1"], |
| ) |
| # If the milestone branch config file doesn't already contain the |
| # current milestone branch, append it to the file. |
| current_milestone_branch = f"{props.branch_prefix}{current_milestone}" |
| if current_milestone_branch not in configured_branches: |
| api.file.write_json( |
| "write milestone branch config", |
| project_path / props.milestone_branches_path, |
| configured_branches + [current_milestone_branch], |
| ) |
| # If presubmit is already set up for this branch, the script will |
| # print a message and exit (0). |
| api.step( |
| "add presubmit configs for the current milestone branch", |
| [ |
| project_path / props.branch_presubmit_script_path, |
| current_milestone_branch, |
| ], |
| ) |
| api.step( |
| "generate configs", |
| [project_path / props.config_generator_path], |
| ) |
| |
| change = api.auto_roller.attempt_roll( |
| props.roll_options, |
| repo_dir=project_path, |
| commit_message=COMMIT_MESSAGE.format( |
| current=current_milestone, target=target_milestone |
| ), |
| ) |
| return api.auto_roller.raw_result(change) |
| |
| |
| def GenTests(api): |
| default_props = api.properties( |
| project="integration", |
| manifest="minimal", |
| remote="https://fuchsia.googlesource.com/integration", |
| branch_prefix="f", |
| branch_presubmit_script_path="config/setup_branch_cq.sh", |
| milestone_branches_path="config/milestone_branches.json", |
| config_generator_path="config/generator.sh", |
| roll_options=api.auto_roller.Options( |
| remote="https://fuchsia.googlesource.com/integration", |
| ), |
| ) |
| yield api.test("no_branches", status="FAILURE") + default_props |
| |
| yield api.test("roll") + api.release.get_branches( |
| ["releases/foo", "releases/f1", "releases/f2"], |
| nesting="resolve target milestone", |
| ) + api.release.get_major_number( |
| 2, nesting="resolve target milestone" |
| ) + api.auto_roller.success() + default_props |
| |
| yield api.test("nothing_to_roll") + api.release.get_branches( |
| ["releases/foo", "releases/f1"], |
| nesting="resolve target milestone", |
| ) + api.release.get_major_number( |
| 2, nesting="resolve target milestone" |
| ) + default_props |
| |
| yield api.test( |
| "invalid_milestone_numbers", status="INFRA_FAILURE" |
| ) + api.release.get_branches( |
| ["releases/foo", "releases/f1"], |
| nesting="resolve target milestone", |
| ) + api.release.get_major_number( |
| 5, nesting="resolve target milestone" |
| ) + default_props |