| # Copyright 2018 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. |
| |
| """Recipe for checks that require a checkout but not a full build.""" |
| |
| |
| from RECIPE_MODULES.fuchsia.utils import pluralize |
| from PB.recipes.fuchsia.static_checks import InputProperties |
| |
| PYTHON_VERSION_COMPATIBILITY = "PY3" |
| |
| DEPS = [ |
| "fuchsia/autocorrelator", |
| "fuchsia/build", |
| "fuchsia/buildbucket_util", |
| "fuchsia/checkout", |
| "fuchsia/utils", |
| "recipe_engine/context", |
| "recipe_engine/path", |
| "recipe_engine/properties", |
| "recipe_engine/raw_io", |
| "recipe_engine/step", |
| ] |
| |
| PROPERTIES = InputProperties |
| |
| |
| def RunSteps(api, props): |
| checkout = api.checkout.fuchsia_with_options( |
| manifest=props.manifest, |
| remote=props.remote, |
| ) |
| |
| with api.autocorrelator.context(ci_base_commit=checkout.integration_revision): |
| build_results = api.build.with_options( |
| checkout=checkout, fint_params_path=props.fint_params_path |
| ) |
| with api.context(cwd=checkout.root_dir): |
| check_docs(api, build_results.tool("doc-checker")) |
| check_gn_formatting(api, checkout, build_results.tool("gn")) |
| check_licenses( |
| api, build_results.tool("check-licenses"), props.check_licenses_configs |
| ) |
| |
| |
| def check_docs(api, doc_checker_tool): |
| """Verify structure and content of documentation files. |
| |
| Args: |
| doc_checker_tool (Path): Path to doc-checker tool. |
| |
| Raises: |
| StepFailure: One or more errors found in documentation. |
| """ |
| step = api.step( |
| "doc check", |
| [doc_checker_tool, "--local-links-only"], |
| stdout=api.raw_io.output_text(), |
| ok_ret="any", |
| ) |
| step.presentation.logs["stdout"] = step.stdout |
| if step.retcode: |
| step.presentation.status = api.step.FAILURE |
| raise api.step.StepFailure( |
| api.buildbucket_util.summary_message( |
| step.stdout, |
| "(failure summary truncated, see `doc check` stdout for full " |
| "failure details)", |
| ) |
| ) |
| |
| |
| def check_gn_formatting(api, checkout, gn_tool): |
| """Check gn formatting. |
| |
| Does not attempt to suggest fixes; the tricium recipe does that. |
| |
| Args: |
| checkout (CheckoutResults): CheckoutResults object. |
| gn_tool (Path): Path to gn tool. |
| |
| Raises: |
| StepFailure: One or more GN files are not formatted properly. |
| """ |
| with api.step.nest("gn format"): |
| # On try jobs, only check the modified files. On CI, check them all. |
| if api.buildbucket_util.is_tryjob: |
| files = checkout.changed_files( |
| test_data=["bar/BUILD.gn", "foo.cc", "third_party/foo/BUILD.gn"], |
| deleted=False, |
| ) |
| else: |
| files = checkout.list_files( |
| file="*.gn*", |
| test_data="bar/BUILD.gn\nthird_party/foo/BUILD.gn", |
| ) |
| files = [str(api.path.relpath(f, checkout.root_dir)) for f in files] |
| files = [ |
| f for f in files if f.endswith((".gn", ".gni")) and "third_party" not in f |
| ] |
| if files: |
| format_step = api.step( |
| "gn format --dry-run", |
| [gn_tool, "format", "--dry-run"] + files, |
| stdout=api.raw_io.output_text(), |
| ok_ret=(0, 2), |
| ) |
| format_step.presentation.logs["stdout"] = format_step.stdout |
| # Retcode 2 indicates that formatting is successful but differs |
| # from disk. See `gn help format` for more information. |
| if format_step.retcode == 2: |
| format_step.presentation.status = api.step.FAILURE |
| unformatted_files = format_step.stdout.strip("\n").split("\n") |
| header = "%s not formatted: \n" % pluralize( |
| "file", len(unformatted_files) |
| ) |
| error_lines = [header] + [ |
| "- %s" % api.path.relpath(f, checkout.root_dir) |
| for f in unformatted_files |
| ] |
| raise api.step.StepFailure( |
| api.buildbucket_util.summary_message( |
| "\n".join(error_lines), |
| "(failure summary truncated, see `gn format --dry-run` " |
| "stdout for full failure details)", |
| escape_markdown=False, |
| ) |
| ) |
| |
| |
| def check_licenses(api, check_licenses_tool, check_licenses_configs): |
| """Check the validity of licenses. |
| |
| Args: |
| check_licenses_tool (Path): Path to gn tool. |
| check_licenses_configs (seq(str)): See corresponding recipe property |
| docstring. |
| |
| Raises: |
| StepFailure: One or more errors found in licenses. |
| """ |
| check_licenses_command = [ |
| check_licenses_tool, |
| "-output_license_file=false", |
| ] |
| if check_licenses_configs: |
| check_licenses_command.append( |
| "-config_file=%s" % ",".join(check_licenses_configs) |
| ) |
| step = api.step( |
| "check licenses", |
| check_licenses_command, |
| stderr=api.raw_io.output_text(), |
| ok_ret="any", |
| ) |
| step.presentation.logs["stderr"] = step.stderr |
| if step.retcode: |
| step.presentation.status = api.step.FAILURE |
| raise api.step.StepFailure( |
| api.buildbucket_util.summary_message( |
| step.stderr, |
| "(failure summary truncated, see `check licenses` stderr for " |
| "full failure details)", |
| ) |
| ) |
| |
| |
| def GenTests(api): |
| source_info = [ |
| { |
| "name": "integration", |
| "remote": "https://fuchsia.googlesource.com/integration", |
| "revision": "a491082dc1b632bbcd60ba3618d20b503c2de738", |
| "relativePath": "integration", |
| }, |
| { |
| "name": "fuchsia", |
| "remote": "https://fuchsia.googlesource.com/fuchsia", |
| "revision": "a491082dc1b632bbcd60ba3618d20b503c2de738", |
| "relativePath": ".", |
| }, |
| ] |
| |
| def props(**kwargs): |
| return api.properties( |
| manifest="fuchsia", |
| remote="https://fuchsia.googlesource.com/integration", |
| fint_params_path="specs/static-checks.textproto", |
| **kwargs |
| ) |
| |
| yield ( |
| api.buildbucket_util.test("default_ci", tryjob=False) |
| + props(check_licenses_configs=["config1.json", "config2.json"]) |
| + api.checkout.source_info(source_info) |
| ) |
| |
| yield ( |
| api.buildbucket_util.test("default_cq", tryjob=True) |
| + props() |
| + api.checkout.source_info(source_info) |
| ) |
| |
| yield ( |
| api.buildbucket_util.test("failed_build", tryjob=True, status="failure") |
| + props() |
| + api.checkout.source_info(source_info) |
| + api.step_data("build.ninja", retcode=1) |
| ) |
| |
| yield ( |
| api.buildbucket_util.test("failed_doc_check", tryjob=True, status="failure") |
| + props() |
| + api.checkout.source_info(source_info) |
| + api.step_data( |
| "doc check", api.raw_io.stream_output_text("Found 2 errors."), retcode=1 |
| ) |
| ) |
| |
| yield ( |
| api.buildbucket_util.test("failed_gn_format", tryjob=True, status="failure") |
| + props() |
| + api.checkout.source_info(source_info) |
| + api.step_data( |
| "gn format.gn format --dry-run", |
| api.raw_io.stream_output_text("foo/BUILD.gn\nbar/BUILD.gn\n"), |
| retcode=2, |
| ) |
| ) |
| |
| yield ( |
| api.buildbucket_util.test( |
| "failed_licenses_check", tryjob=True, status="failure" |
| ) |
| + props() |
| + api.checkout.source_info(source_info) |
| + api.step_data( |
| "check licenses", |
| stderr=api.raw_io.output_text("Encountered prohibited license types."), |
| retcode=1, |
| ) |
| ) |