crosvm/tools/health-check
Dennis Kempin abcfc67d8b Add python configs for consistency between IDE and CI
Also adds settings to .vscode/settings.json to use the correct
formatter and type checking settings out of the box.

VSCode uses pyright and health-check uses mypy. They do not always
agree unfortunately, mypy  is a little more lenient.
Unfortunately pyright is hard to get working with our scripts that
have no file extension.

BUG=b:242605601
TEST=./tools/health-check

Change-Id: Iaef4ac6f61613e7dda409d5e717102ea9adefe82
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3833699
Commit-Queue: Dennis Kempin <denniskempin@google.com>
Tested-by: Dennis Kempin <denniskempin@google.com>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
2022-08-16 18:33:23 +00:00

156 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 pathlib import Path
from typing import List
import sys
from impl.common import CROSVM_ROOT, parallel, run_main, cmd, chdir, argh, cwd_context
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")
parallel(*mypy.foreach(context.all_files)).fg()
def check_python_format(context: CheckContext):
black = cmd("black", "--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 --locked").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
)
def check_rust_lockfiles(_: CheckContext):
"Verifies that none of the Cargo.lock files require updates."
lockfiles = [Path("Cargo.lock"), *Path("common").glob("*/Cargo.lock")]
for path in lockfiles:
with cwd_context(path.parent):
if not cmd("cargo update --workspace --locked").success():
print(f"{path} is not up-to-date.")
print()
print("You may need to rebase your changes and run `cargo update --workspace`")
print("(or ./tools/run_tests) to ensure the Cargo.lock file is current.")
raise Exception("Cargo.lock out of date")
# List of all checks and on which files they should run.
CHECKS: List[Check] = [
Check(
check_rust_format,
files=["**.rs"],
),
Check(
check_rust_lockfiles,
files=["**Cargo.toml"],
),
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)
@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(), []],
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)
if not checks:
checks_list = [*CHECKS_DICT.values()]
else:
checks_list = [CHECKS_DICT[check] for check in 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)