| # 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" |
| ) |
| |
| # test_ids should use underscore instead of spaces |
| prepared_step = self.m.reported_step.prepare_step( |
| test_id=step_name.replace(" ", "_"), step=step |
| ) |
| try: |
| if step.retcode: |
| step.presentation.status = self.m.step.FAILURE |
| step.presentation.logs["stderr"] = step.stderr |
| prepared_step.add_artifact("stderr", step.stderr) |
| raise self.m.step.StepFailure( |
| "Binary size checks failed: %s\n\nFor next steps, please refer:" |
| "http://go/tq-resilience-home/size-stats/size-check-failure-steps\n" |
| "Contact http://go/fuchsia-size-rotation if help is needed to " |
| "adjust the size budgets." % step.stderr, |
| ) |
| finally: |
| prepared_step.upload() |
| |
| 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: |
| StepData: Step data of the size diff tool. |
| 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 (step, 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 (step, 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) |