From 4fd633a7fe77c2891964797a7d3ed50d9cac4d65 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 20 Jan 2026 11:24:56 -0500 Subject: [PATCH 1/4] Update copyright year with a precommit hook --- .pre-commit-config.yaml | 4 ++- toolshed/check_spdx.py | 54 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9727cb2d34..1c4339b948 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 @@ -30,6 +30,7 @@ repos: additional_dependencies: - https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl exclude: '.*pixi\.lock' + args: ["--fix"] - id: no-markdown-in-docs-source name: Prevent markdown files in docs/source directories @@ -89,5 +90,6 @@ repos: args: [--no-pycodestyle] exclude: ^cuda_bindings/ + default_language_version: python: python3 diff --git a/toolshed/check_spdx.py b/toolshed/check_spdx.py index 06072385b7..d6b349f433 100644 --- a/toolshed/check_spdx.py +++ b/toolshed/check_spdx.py @@ -1,7 +1,9 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 +import datetime import os +import re import sys import pathspec @@ -26,29 +28,75 @@ def load_spdx_ignore(): return pathspec.PathSpec.from_lines("gitwildmatch", lines) -def has_spdx_or_is_empty(filepath): +COPYRIGHT_REGEX = ( + rb"Copyright \(c\) (?P[0-9]{4}(-[0-9]{4})?) " + rb"(?PNVIDIA CORPORATION( & AFFILIATES\. All rights reserved\.)?)" +) +COPYRIGHT_SUB = r"Copyright (c) {} \g" +CURRENT_YEAR = str(datetime.datetime.now().year) + + +def find_or_fix_spdx(filepath, fix): with open(filepath, "rb") as f: blob = f.read() if len(blob.strip()) == 0: return True + good = True for expected_bytes in EXPECTED_SPDX_BYTES: if expected_bytes not in blob: print(f"MISSING {expected_bytes.decode()}{filepath!r}") good = False + continue + + match = re.search(COPYRIGHT_REGEX, blob) + if match is None: + print(f"MISSING valid copyright line in {filepath!r}") + good = False + continue + + years = match.group("years").decode() + if "-" in years: + start_year, end_year = years.split("-", 1) + if int(start_year) > int(end_year): + print(f"INVALID copyright years {years!r} in {filepath!r}") + good = False + else: + start_year = end_year = years + + if int(end_year) < int(CURRENT_YEAR): + print(f"OUTDATED copyright end year {years!r} in {filepath!r}") + good = False + + if fix: + new_years = f"{start_year}-{CURRENT_YEAR}" + blob = re.sub( + COPYRIGHT_REGEX, + COPYRIGHT_SUB.format(new_years).encode("ascii"), + blob, + ) + with open(filepath, "wb") as f: + f.write(blob) + return good def main(args): assert args, "filepaths expected to be passed from pre-commit" + if "--fix" in args: + fix = True + del args[args.index("--fix")] + else: + fix = False + ignore_spec = load_spdx_ignore() returncode = 0 for filepath in args: if ignore_spec.match_file(filepath): continue - if not has_spdx_or_is_empty(filepath): + if not find_or_fix_spdx(filepath, fix): returncode = 1 return returncode From 2d79a304d51f624d14e967484267f79cd4ab2b41 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 20 Jan 2026 12:23:09 -0500 Subject: [PATCH 2/4] Handle testing on all files --- toolshed/check_spdx.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/toolshed/check_spdx.py b/toolshed/check_spdx.py index d6b349f433..ceb4e12c32 100644 --- a/toolshed/check_spdx.py +++ b/toolshed/check_spdx.py @@ -4,6 +4,7 @@ import datetime import os import re +import subprocess import sys import pathspec @@ -33,7 +34,27 @@ def load_spdx_ignore(): rb"(?PNVIDIA CORPORATION( & AFFILIATES\. All rights reserved\.)?)" ) COPYRIGHT_SUB = r"Copyright (c) {} \g" -CURRENT_YEAR = str(datetime.datetime.now().year) +CURRENT_YEAR = str(datetime.date.today().year) + + +def get_last_modified_year(filepath): + # If the file is staged, we need to update it to the current year + process = subprocess.run( # noqa: S603 + ["git", "diff", "--staged", "--", filepath], # noqa: S607 + capture_output=True, + text=True, + ) + if process.stdout.strip(): + return CURRENT_YEAR + + # If the file is not staged, get the year of the last commit + process = subprocess.run( # noqa: S603 + ["git", "log", "-1", "--pretty=format:%cs", "--", filepath], # noqa: S607 + capture_output=True, + text=True, + ) + year = process.stdout.strip()[:4] + return year def find_or_fix_spdx(filepath, fix): @@ -61,15 +82,18 @@ def find_or_fix_spdx(filepath, fix): if int(start_year) > int(end_year): print(f"INVALID copyright years {years!r} in {filepath!r}") good = False + continue else: start_year = end_year = years - if int(end_year) < int(CURRENT_YEAR): - print(f"OUTDATED copyright end year {years!r} in {filepath!r}") + modified_year = get_last_modified_year(filepath) + + if int(end_year) < int(modified_year): + print(f"OUTDATED copyright {years!r} (expected {modified_year!r}) in {filepath!r}") good = False if fix: - new_years = f"{start_year}-{CURRENT_YEAR}" + new_years = f"{start_year}-{modified_year}" blob = re.sub( COPYRIGHT_REGEX, COPYRIGHT_SUB.format(new_years).encode("ascii"), From 039146e3320a9718c3daa03ed06c8990a74c409c Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 20 Jan 2026 12:30:42 -0500 Subject: [PATCH 3/4] Declare fewer files as binary --- .gitattributes | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 81f2361d4c..f713b27424 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,7 +6,9 @@ cuda/_version.py export-subst # we do not own any headers checked in, don't touch them *.h binary *.hpp binary -# Exception: headers we own (cuda_core C++ implementation) +# Exception: headers we own +cuda_bindings/cuda/bindings/_bindings/*.h -binary text diff +cuda_bindings/cuda/bindings/_lib/*.h -binary text diff cuda_core/cuda/core/_cpp/*.h -binary text diff cuda_core/cuda/core/_cpp/*.hpp -binary text diff # git should not convert line endings in PNG files From a3f5c7c00c3cadd9c04066ee661b7138bf443096 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Tue, 20 Jan 2026 12:38:52 -0500 Subject: [PATCH 4/4] Only update staged files --- toolshed/check_spdx.py | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/toolshed/check_spdx.py b/toolshed/check_spdx.py index ceb4e12c32..2359a89708 100644 --- a/toolshed/check_spdx.py +++ b/toolshed/check_spdx.py @@ -37,24 +37,14 @@ def load_spdx_ignore(): CURRENT_YEAR = str(datetime.date.today().year) -def get_last_modified_year(filepath): +def is_staged(filepath): # If the file is staged, we need to update it to the current year process = subprocess.run( # noqa: S603 ["git", "diff", "--staged", "--", filepath], # noqa: S607 capture_output=True, text=True, ) - if process.stdout.strip(): - return CURRENT_YEAR - - # If the file is not staged, get the year of the last commit - process = subprocess.run( # noqa: S603 - ["git", "log", "-1", "--pretty=format:%cs", "--", filepath], # noqa: S607 - capture_output=True, - text=True, - ) - year = process.stdout.strip()[:4] - return year + return process.stdout.strip() != "" def find_or_fix_spdx(filepath, fix): @@ -86,14 +76,14 @@ def find_or_fix_spdx(filepath, fix): else: start_year = end_year = years - modified_year = get_last_modified_year(filepath) + staged = is_staged(filepath) - if int(end_year) < int(modified_year): - print(f"OUTDATED copyright {years!r} (expected {modified_year!r}) in {filepath!r}") + if staged and int(end_year) < int(CURRENT_YEAR): + print(f"OUTDATED copyright {years!r} (expected {CURRENT_YEAR!r}) in {filepath!r}") good = False if fix: - new_years = f"{start_year}-{modified_year}" + new_years = f"{start_year}-{CURRENT_YEAR}" blob = re.sub( COPYRIGHT_REGEX, COPYRIGHT_SUB.format(new_years).encode("ascii"),