blob: 6909c19cd6d81f3da7f8760b9a983a8dbf1733df [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import annotations
import platform
import subprocess
from pathlib import Path
USE_PYTHON3 = True
SOURCE_SKIP_RE = [r"^protoc/gen.*", r"^third_party/.*"]
def GlobalSkipChecks(input_api, file_path: str):
if input_api.fnmatch.fnmatch(file_path, "*protoc/gen/*"):
return True
if input_api.fnmatch.fnmatch(file_path, "*third_party/*"):
return True
return False
def CheckChange(input_api, output_api):
tests = []
results = []
modified_py_files: list[str] | None = ModifiedFiles(
input_api, filename_pattern="*.py")
# ---------------------------------------------------------------------------
# Validate the vpython spec:
# ---------------------------------------------------------------------------
if platform.system() in ("Linux", "Darwin"):
tests += input_api.canned_checks.CheckVPythonSpec(input_api, output_api)
# ---------------------------------------------------------------------------
# License header checks:
# ---------------------------------------------------------------------------
files_to_check = list(input_api.DEFAULT_FILES_TO_CHECK) + [
r".+\.hjson$",
r".+\.sql$",
]
results += input_api.canned_checks.CheckLicense(
input_api,
output_api,
source_file_filter=lambda x: input_api.FilterSourceFile(
x, files_to_check=files_to_check))
# ---------------------------------------------------------------------------
# Pylint:
# ---------------------------------------------------------------------------
tests += input_api.canned_checks.GetPylint(
input_api,
output_api,
files_to_check=[r"^[^\.]+\.py$"],
files_to_skip=SOURCE_SKIP_RE,
pylintrc=".pylintrc",
version="3.2")
# ---------------------------------------------------------------------------
# MyPy:
# ---------------------------------------------------------------------------
mypy_files_to_check: list[str] = MypyFilesToCheck(input_api,
modified_py_files)
tests.append(
input_api.Command(
name="mypy",
cmd=[
input_api.python3_executable,
"-m",
"mypy",
"--check-untyped-defs",
"--pretty",
] + mypy_files_to_check,
message=output_api.PresubmitError,
kwargs={},
python3=True,
))
# ---------------------------------------------------------------------------
# JS eslint:
# ---------------------------------------------------------------------------
modified_js_files: list[str] | None = ModifiedFiles(
input_api, filename_pattern="*.js")
tests.append(
input_api.Command(
name="eslint",
cmd=[
input_api.python3_executable,
str(
Path(input_api.change.RepositoryRoot()) / "tools" /
"eslint.py"),
] + (modified_js_files if modified_js_files else []),
message=output_api.PresubmitError,
kwargs={},
python3=True,
))
# ---------------------------------------------------------------------------
# isort:
# ---------------------------------------------------------------------------
SortImports(input_api, output_api, results, modified_py_files)
# ---------------------------------------------------------------------------
# format all supported files:
# ---------------------------------------------------------------------------
FormatFiles(input_api, output_api, results,
ModifiedFiles(input_api, filename_pattern="*"))
# ---------------------------------------------------------------------------
# crossbench:
# ---------------------------------------------------------------------------
runner_path = str(
Path(input_api.change.RepositoryRoot()) / "cuj" / "crossbench" /
"runner" / "run.py")
tests.append(
input_api.Command(
name="crossbench dry run",
cmd=[
input_api.python3_executable,
runner_path,
"--platform=local",
"--dry-run",
# Loadline does not cooporate with --dry-run, so ignore it.
"--tests",
"^(?!.*loadline).*$"
],
message=output_api.PresubmitError,
kwargs={},
python3=True,
))
# ---------------------------------------------------------------------------
# Run all test
# ---------------------------------------------------------------------------
results += input_api.RunTests(tests)
return results
def ModifiedFiles(input_api, filename_pattern: str) -> list[str] | None:
files = [file.AbsoluteLocalPath() for file in input_api.AffectedFiles()]
files_to_check = []
for file_path in files:
if not input_api.fnmatch.fnmatch(file_path, filename_pattern):
continue
if not input_api.os_path.exists(file_path):
continue
file_path = input_api.os_path.relpath(file_path,
input_api.PresubmitLocalPath())
files_to_check.append(file_path)
return files_to_check
def MypyFilesToCheck(input_api, modified_py_files) -> list[str]:
mypy_files_to_check = {"PRESUBMIT.py"}
mypy_files_to_check.update(modified_py_files)
result = []
for file in mypy_files_to_check:
if GlobalSkipChecks(input_api, file):
continue
result.append(file)
return result
def SortImports(input_api, output_api, results, modified_py_files):
for py_file in (modified_py_files or []):
full_py_path = Path(input_api.change.RepositoryRoot()) / py_file
original_contents = input_api.ReadFile(str(full_py_path), "r")
subprocess.run([input_api.python_executable, "-m", "isort", full_py_path],
check=True)
formatted_contents = input_api.ReadFile(str(full_py_path), "r")
if original_contents != formatted_contents:
results.append(
output_api.PresubmitPromptWarning(
"Unsorted python imports in file:",
items=[str(full_py_path)],
long_text="Please update your commit with the formatted file."))
def FormatFiles(input_api, output_api, results, modified_files):
repo_root = Path(input_api.change.RepositoryRoot())
formatter = repo_root / "tools" / "format_files.py"
for file in (modified_files or []):
full_path = repo_root / file
original_contents = input_api.ReadFile(str(full_path), "r")
try:
subprocess.run(
[input_api.python_executable,
str(formatter),
str(full_path)],
check=True,
capture_output=True)
formatted_contents = input_api.ReadFile(str(full_path), "r")
except subprocess.CalledProcessError as e:
results.append(
output_api.PresubmitPromptWarning(
"Failed to format file:",
items=[str(full_path)],
long_text=str(e)))
continue
if original_contents != formatted_contents:
results.append(
output_api.PresubmitPromptWarning(
"Unformatted file:",
items=[str(full_path)],
long_text="Please update your commit with the formatted file."))
def CheckChangeOnUpload(input_api, output_api):
return CheckChange(input_api, output_api)
def CheckChangeOnCommit(input_api, output_api):
return CheckChange(input_api, output_api)