Skip to content

Commit dc30b3d

Browse files
authored
Update copyright year with a precommit hook (#1514)
* Update copyright year with a precommit hook * Handle testing on all files * Declare fewer files as binary * Only update staged files
1 parent 3257477 commit dc30b3d

3 files changed

Lines changed: 71 additions & 5 deletions

File tree

.gitattributes

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ cuda/_version.py export-subst
66
# we do not own any headers checked in, don't touch them
77
*.h binary
88
*.hpp binary
9-
# Exception: headers we own (cuda_core C++ implementation)
9+
# Exception: headers we own
10+
cuda_bindings/cuda/bindings/_bindings/*.h -binary text diff
11+
cuda_bindings/cuda/bindings/_lib/*.h -binary text diff
1012
cuda_core/cuda/core/_cpp/*.h -binary text diff
1113
cuda_core/cuda/core/_cpp/*.hpp -binary text diff
1214
# git should not convert line endings in PNG files

.pre-commit-config.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
#
33
# SPDX-License-Identifier: Apache-2.0
44

@@ -30,6 +30,7 @@ repos:
3030
additional_dependencies:
3131
- https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl
3232
exclude: '.*pixi\.lock'
33+
args: ["--fix"]
3334

3435
- id: no-markdown-in-docs-source
3536
name: Prevent markdown files in docs/source directories
@@ -89,5 +90,6 @@ repos:
8990
args: [--no-pycodestyle]
9091
exclude: ^cuda_bindings/
9192

93+
9294
default_language_version:
9395
python: python3

toolshed/check_spdx.py

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
# SPDX-License-Identifier: Apache-2.0
33

4+
import datetime
45
import os
6+
import re
7+
import subprocess
58
import sys
69

710
import pathspec
@@ -26,29 +29,88 @@ def load_spdx_ignore():
2629
return pathspec.PathSpec.from_lines("gitwildmatch", lines)
2730

2831

29-
def has_spdx_or_is_empty(filepath):
32+
COPYRIGHT_REGEX = (
33+
rb"Copyright \(c\) (?P<years>[0-9]{4}(-[0-9]{4})?) "
34+
rb"(?P<affiliation>NVIDIA CORPORATION( & AFFILIATES\. All rights reserved\.)?)"
35+
)
36+
COPYRIGHT_SUB = r"Copyright (c) {} \g<affiliation>"
37+
CURRENT_YEAR = str(datetime.date.today().year)
38+
39+
40+
def is_staged(filepath):
41+
# If the file is staged, we need to update it to the current year
42+
process = subprocess.run( # noqa: S603
43+
["git", "diff", "--staged", "--", filepath], # noqa: S607
44+
capture_output=True,
45+
text=True,
46+
)
47+
return process.stdout.strip() != ""
48+
49+
50+
def find_or_fix_spdx(filepath, fix):
3051
with open(filepath, "rb") as f:
3152
blob = f.read()
3253
if len(blob.strip()) == 0:
3354
return True
55+
3456
good = True
3557
for expected_bytes in EXPECTED_SPDX_BYTES:
3658
if expected_bytes not in blob:
3759
print(f"MISSING {expected_bytes.decode()}{filepath!r}")
3860
good = False
61+
continue
62+
63+
match = re.search(COPYRIGHT_REGEX, blob)
64+
if match is None:
65+
print(f"MISSING valid copyright line in {filepath!r}")
66+
good = False
67+
continue
68+
69+
years = match.group("years").decode()
70+
if "-" in years:
71+
start_year, end_year = years.split("-", 1)
72+
if int(start_year) > int(end_year):
73+
print(f"INVALID copyright years {years!r} in {filepath!r}")
74+
good = False
75+
continue
76+
else:
77+
start_year = end_year = years
78+
79+
staged = is_staged(filepath)
80+
81+
if staged and int(end_year) < int(CURRENT_YEAR):
82+
print(f"OUTDATED copyright {years!r} (expected {CURRENT_YEAR!r}) in {filepath!r}")
83+
good = False
84+
85+
if fix:
86+
new_years = f"{start_year}-{CURRENT_YEAR}"
87+
blob = re.sub(
88+
COPYRIGHT_REGEX,
89+
COPYRIGHT_SUB.format(new_years).encode("ascii"),
90+
blob,
91+
)
92+
with open(filepath, "wb") as f:
93+
f.write(blob)
94+
3995
return good
4096

4197

4298
def main(args):
4399
assert args, "filepaths expected to be passed from pre-commit"
44100

101+
if "--fix" in args:
102+
fix = True
103+
del args[args.index("--fix")]
104+
else:
105+
fix = False
106+
45107
ignore_spec = load_spdx_ignore()
46108

47109
returncode = 0
48110
for filepath in args:
49111
if ignore_spec.match_file(filepath):
50112
continue
51-
if not has_spdx_or_is_empty(filepath):
113+
if not find_or_fix_spdx(filepath, fix):
52114
returncode = 1
53115
return returncode
54116

0 commit comments

Comments
 (0)