Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -89,5 +90,6 @@ repos:
args: [--no-pycodestyle]
exclude: ^cuda_bindings/


default_language_version:
python: python3
68 changes: 65 additions & 3 deletions toolshed/check_spdx.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# 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 subprocess
import sys

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


def has_spdx_or_is_empty(filepath):
COPYRIGHT_REGEX = (
rb"Copyright \(c\) (?P<years>[0-9]{4}(-[0-9]{4})?) "
rb"(?P<affiliation>NVIDIA CORPORATION( & AFFILIATES\. All rights reserved\.)?)"
)
COPYRIGHT_SUB = r"Copyright (c) {} \g<affiliation>"
CURRENT_YEAR = str(datetime.date.today().year)


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,
)
return process.stdout.strip() != ""


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):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To catch typos, WDYT about adding

or int(end_year) > int(CURRENT_YEAR)

here?

I'd probably make this

        if int(start_year) >= int(end_year) or int(end_year) > int(CURRENT_YEAR):

to catch things like 2026-2026 as well (when someone maybe wanted to write 2025-2026 but got mixed up).

print(f"INVALID copyright years {years!r} in {filepath!r}")
good = False
continue
else:
start_year = end_year = years

staged = is_staged(filepath)

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}-{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

Expand Down
Loading