blob: a5ff5fbc1316e26f2818cc59461f90ebd41abb74 [file] [log] [blame]
# 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/json",
"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_text(
"write milestone branch config",
project_path / props.milestone_branches_path,
api.json.dumps(
configured_branches + [current_milestone_branch],
indent=2,
)
+ "\n",
)
# 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