blob: 7280789719da20148ab4891b63bfbe3e8022abca [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.
from recipe_engine import recipe_api
class BinarySizeApi(recipe_api.RecipeApi):
"""APIs for checking and diffing binary sizes."""
# This exit code indicates that the CI build was not successful and thus the
# the size diff could not be computed.
# Keep in sync with
# https://fuchsia.googlesource.com/infra/infra/+/main/cmd/size_diff/ci.go.
CI_BUILD_NOT_SUCCESSFUL_EXIT_CODE = 2
def check_budgets(self, step_name, binary_sizes_json_input):
"""Check if the input binary sizes object exceeds one or more budgets.
Args:
step_name (str): Name of the step.
binary_sizes_json_input (Path): Path for input binary sizes object
as JSON.
Raises:
StepFailure: one or more budgets were exceeded.
"""
args = [
"budgets",
"-binary-sizes-json-input",
binary_sizes_json_input,
]
step = self._run_size_check(
step_name, args, stderr=self.m.raw_io.output_text(), ok_ret="any"
)
if step.retcode:
step.presentation.status = self.m.step.FAILURE
step.presentation.logs["stderr"] = step.stderr
raise self.m.step.StepFailure(
"Binary size checks failed: %s\n\nContact "
"http://go/fuchsia-size-rotation if help is needed to adjust "
"the size budgets." % step.stderr,
)
def diff_ci(
self,
step_name,
gitiles_remote,
base_commit,
ci_builder,
binary_sizes_json_input,
):
"""Compute diff of the input binary sizes object against a binary sizes
object from CI.
Args:
step_name (str): Name of the step.
gitiles_remote (str): Gitiles remote for base commit.
base_commit (str): Base commit as sha1.
ci_builder (builder_pb2.BuilderID): CI builder to inspect.
binary_sizes_json_input (Path): Path for input binary sizes object
as JSON.
Returns:
dict: Binary size diff with keys:
component_diffs (seq(dict)): Per-component diffs, each with keys:
name (str): Name of the component.
baseline_size (int): Baseline size of the component in bytes.
size (int): Size of the component in bytes.
size_diff (int): Size diff in bytes.
budget (int): Budget of the component in bytes.
creep_budget (int): Creep budget of the component in bytes.
budget_exceeded (bool): Whether the budget is exceeded.
creep_budget_exceeded (bool): Whether the creep budget is
exceeded.
budget_exceeded (bool): Whether one or more budgets are exceeded.
creep_budget_exceeded (bool): Whether one or more creep budgets
are exceeded.
baseline_build_id (int): The baseline build ID that was used.
"""
args = [
"ci",
"-gitiles-remote",
gitiles_remote,
"-base-commit",
base_commit,
"-builder",
self.m.buildbucket_util.full_builder_name(ci_builder),
"-binary-sizes-json-input",
binary_sizes_json_input,
"-json-output",
self.m.json.output(),
]
step = self._run_size_diff(
step_name, args, ok_ret=(0, self.CI_BUILD_NOT_SUCCESSFUL_EXIT_CODE)
)
# If the size diff could not be computed, keep track of this as a
# property, but do not raise an error. Computing the size diff is
# best-effort as of now.
step.presentation.properties["size_ci_build_not_successful"] = step.retcode == 2
if step.retcode:
return None
diff = step.json.output
step.presentation.properties["size_creep_budget_exceeded"] = diff[
"creep_budget_exceeded"
]
step.presentation.links["ci_build"] = self.m.buildbucket.build_url(
build_id=diff["baseline_build_id"]
)
return diff
@property
def _size_check_tool(self):
return self.m.ensure_tool(
"size_check", self.resource("size_check/tool_manifest.json")
)
def _run_size_check(self, step_name, args, **kwargs):
return self.m.step(step_name, [self._size_check_tool] + args, **kwargs)
@property
def _size_diff_tool(self):
return self.m.ensure_tool(
"size_diff", self.resource("size_diff/tool_manifest.json")
)
def _run_size_diff(self, step_name, args, **kwargs):
return self.m.step(step_name, [self._size_diff_tool] + args, **kwargs)