health-check: Add copyright header check

New files will require a copyright header, which is checked by CI
and locally.

BUG=b:242605601
TEST=touch foo.rs && ./tools/health-check; ./tools/health-check --fix

Change-Id: I31bf299bd636a5da4f806c32ca8bdf9cfd4c01f3
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3832787
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: Dennis Kempin <denniskempin@google.com>
Commit-Queue: Dennis Kempin <denniskempin@google.com>
This commit is contained in:
Dennis Kempin 2022-08-16 19:57:01 +00:00 committed by crosvm LUCI
parent 7b5f6b198f
commit 34a4ee662f
2 changed files with 76 additions and 9 deletions

View file

@ -3,11 +3,22 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import re
import sys
from datetime import datetime
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.common import (
CROSVM_ROOT,
argh,
chdir,
cmd,
cwd_context,
parallel,
run_main,
)
from impl.health_check import Check, CheckContext, run_checks
@ -74,8 +85,55 @@ def check_rust_lockfiles(_: CheckContext):
raise Exception("Cargo.lock out of date")
LICENSE_HEADER_RE = (
# Line 1 - copyright.
r".*Copyright(?P<copyright> \(c\))? (?P<year>20[0-9]{2})(?:-20[0-9]{2})? "
r"The Chromium(?P<chromium_space_os> )?OS Authors\."
r"(?P<rights_reserved> All rights reserved\.)?\n"
# Line 2 - License.
r".*Use of this source code is governed by a BSD-style license that can "
r"be\n"
# Line 3 - License continuation.
r".*found in the LICENSE file\.\n"
)
NEW_LICENSE_HEADER = [
f"Copyright {datetime.now().year} The ChromiumOS Authors.",
"Use of this source code is governed by a BSD-style license that can be",
"found in the LICENSE file.",
]
def new_licence_header(file_suffix: str):
if file_suffix == ".py" or file_suffix == "":
prefix = "#"
else:
prefix = "//"
return "\n".join(f"{prefix} {line}" for line in NEW_LICENSE_HEADER) + "\n\n"
def check_copyright_header(context: CheckContext):
"Checks copyright header on new files only. Can 'fix' them if needed."
license_re = re.compile(LICENSE_HEADER_RE, re.MULTILINE)
for file in context.new_files:
header = file.open("r").read(256)
license_match = license_re.search(header)
if license_match:
continue
if context.fix:
contents = file.read_text()
file.write_text(new_licence_header(file.suffix) + contents)
else:
raise Exception(f"Bad copyright header: {file}")
# List of all checks and on which files they should run.
CHECKS: List[Check] = [
Check(
check_copyright_header,
files=["**.rs", "**.py"],
python_tools=True,
),
Check(
check_rust_format,
files=["**.rs"],

View file

@ -3,13 +3,13 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from dataclasses import dataclass
from fnmatch import fnmatch
from pathlib import Path
from time import time
from typing import Callable, List, NamedTuple
from pathlib import Path
from impl.common import all_tracked_files, cmd, verbose
from dataclasses import dataclass
from impl.common import all_tracked_files, cmd, verbose
git = cmd("git")
@ -30,6 +30,9 @@ class CheckContext(object):
# Those files of all_files that were modified locally.
modified_files: List[Path]
# Files that do not exist upstream and have been added locally.
new_files: List[Path]
class Check(NamedTuple):
"Metadata for each check, definining on which files it should run."
@ -53,7 +56,7 @@ class Check(NamedTuple):
return name
def list_modified_files():
def list_file_diff():
"""
Lists files there were modified compared to the upstream branch.
@ -61,10 +64,13 @@ def list_modified_files():
"""
upstream = git("rev-parse @{u}").stdout(check=False)
if upstream:
return (Path(f) for f in git("diff --name-only", upstream).lines())
for line in git("diff --name-status", upstream).lines():
parts = line.split("\t", 1)
yield (parts[0].strip(), Path(parts[1].strip()))
else:
print("WARNING: Not tracking a branch. Checking all files.")
return all_tracked_files()
for file in all_tracked_files():
yield ("M", file)
def should_run_check_on_file(check: Check, file: Path):
@ -129,10 +135,12 @@ def run_checks(
nightly: Use nightly version of rust tooling.
"""
all_files = [*all_tracked_files()]
file_diff = [*list_file_diff()]
new_files = [f for (s, f) in file_diff if s == "A"]
if run_on_all_files:
modified_files = all_files
else:
modified_files = [*list_modified_files()]
modified_files = [f for (s, f) in file_diff if s in ("M", "A")]
success = True
for check in checks_list:
@ -141,6 +149,7 @@ def run_checks(
nightly=nightly,
all_files=[f for f in all_files if should_run_check_on_file(check, f)],
modified_files=[f for f in modified_files if should_run_check_on_file(check, f)],
new_files=[f for f in new_files if should_run_check_on_file(check, f)],
)
if context.modified_files:
if not run_check(check, context):