crosvm/tools/health-check
Dennis Kempin 92f804e3f8 tools/health-check: Revamp script to run on git delta only
The updated health-check will by default only run on modified
files. If you do not make changes to python code, python checks
won't run, etc.

The script also simplifies the writing of those checks so we can
start adding more of them.

Luci will be updated to make use of the --list-checks function to
run each check in a separate luci step. In the meantime, we
keep a compatibility layer to translate the old arguments
to the new style.

BUG=b:239255137
TEST=./tools/health-check in all it's variations

Change-Id: I21b986b46c7cfccf3d13f4c76bbd3d0ec7240c26
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3827174
Tested-by: Dennis Kempin <denniskempin@google.com>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Commit-Queue: Dennis Kempin <denniskempin@google.com>
2022-08-15 18:52:02 +00:00

164 lines
4.8 KiB
Python
Executable file

#!/usr/bin/env python3
# Copyright 2022 The Chromium OS 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 typing import List
import sys
from impl.common import (
CROSVM_ROOT,
parallel,
run_main,
cmd,
chdir,
argh,
)
from impl.check_code_hygiene import has_crlf_line_endings
from impl.health_check import Check, CheckContext, run_checks
def check_python_tests(context: CheckContext):
"Run all non-main python files to execute their unit tests."
parallel(*cmd("python3").foreach(context.all_files)).fg()
def check_python_types(context: CheckContext):
"Run mypy on all python files to type-check."
mypy = cmd("mypy --pretty --color-output").env("MYPY_FORCE_COLOR", "1").env("MYPYPATH", "tools")
parallel(*mypy.foreach(context.all_files)).fg()
def check_python_format(context: CheckContext):
black = cmd("black", "--line-length 100", "--check" if not context.fix else "")
parallel(*black.foreach(context.modified_files)).fg()
def check_crlf_line_endings(_: CheckContext):
"Checks for crlf line endingings."
crlf_endings = has_crlf_line_endings()
if crlf_endings:
print("Error: Following files have crlf(dos) line encodings")
print(*crlf_endings)
raise Exception("Files with crlf line endings.")
def check_markdown_format(context: CheckContext):
"Runs mdformat on all markdown files."
mdformat = cmd("mdformat --wrap 100", "--check" if not context.fix else "")
parallel(*mdformat.foreach(context.modified_files)).fg()
def check_rust_clippy(_: CheckContext):
"Runs clippy on the whole project, no matter which rs files were touched."
cmd("./tools/clippy").fg()
def check_rust_format(context: CheckContext):
"Runs rustfmt on all modified files."
if context.nightly:
rustfmt = cmd(
cmd("rustup +nightly which rustfmt"),
"--config imports_granularity=item,group_imports=StdExternalCrate",
)
else:
rustfmt = cmd(cmd("rustup which rustfmt"))
parallel(*rustfmt("--check" if not context.fix else "").foreach(context.modified_files)).fg(
quiet=True
)
# List of all checks and on which files they should run.
CHECKS: List[Check] = [
Check(
check_rust_format,
files=["**.rs"],
),
Check(
check_rust_clippy,
files=["**.rs", "**Cargo.toml"],
),
Check(
check_python_tests,
files=["tools/impl/common.py"],
),
Check(
check_python_types,
files=["tools/**.py"],
exclude=["tools/windows/*"],
python_tools=True,
),
Check(
check_python_format,
files=["**.py"],
python_tools=True,
exclude=["infra/recipes.py"],
),
Check(
check_markdown_format,
files=["**.md"],
exclude=[
"infra/README.recipes.md",
"docs/book/src/appendix/memory_layout.md",
],
),
Check(check_crlf_line_endings),
]
CHECKS_DICT = dict((c.name, c) for c in CHECKS)
# TODO(b/239255137): Remove after transition
LEGACY_STEPS = ["python", "misc", "fmt", "clippy"]
@argh.arg("--list-checks", default=False, help="List names of available checks and exit.")
@argh.arg("--fix", default=False, help="Asks checks to fix problems where possible.")
@argh.arg("--all", default=False, help="Run on all files instead of just modified files.")
@argh.arg(
"checks",
choices=[*CHECKS_DICT.keys(), *LEGACY_STEPS, []],
help="Optional list of checks to run. Defaults to run all checks.",
)
def main(
list_checks: bool = False,
fix: bool = False,
all: bool = False,
nightly: bool = False,
*checks: str,
):
"""
Run health checks on crosvm. This includes formatting, linters and other various checks.
"""
chdir(CROSVM_ROOT)
# We need to support the old arguments while Luci is transitioning.
# TODO(b/239255137): Remove after transition
no_legacy_checks = [check for check in checks if check not in LEGACY_STEPS]
if "python" in checks:
no_legacy_checks += ["python_tests", "python_types"]
all = True
if "clippy" in checks:
no_legacy_checks += ["rust_clippy"]
all = True
if "misc" in checks:
no_legacy_checks += ["crlf_line_endings"]
all = True
if "fmt" in checks:
no_legacy_checks += ["python_format", "markdown_format", "rust_format"]
all = True
if not no_legacy_checks:
checks_list = [*CHECKS_DICT.values()]
else:
checks_list = [CHECKS_DICT[check] for check in no_legacy_checks]
if list_checks:
for check in checks_list:
print(check.name)
return
success = run_checks(checks_list, fix=fix, run_on_all_files=all, nightly=nightly)
sys.exit(0 if success else 1)
if __name__ == "__main__":
run_main(main)