Skip to content

Commit 5c1a92b

Browse files
committed
fix: enforce current year in copyright header of new files
When a new file (not yet present in HEAD) already carries an SPDX-FileCopyrightText header, validate that the year matches the current year. Existing files are unchanged - only the annotation path (no prior header) is affected, which already stamps the current year via reuse annotate. The new check covers the gap where a developer manually writes a stale year in a brand-new file.
1 parent d720935 commit 5c1a92b

1 file changed

Lines changed: 47 additions & 1 deletion

File tree

pre-commit-action/reuse-annotate-hook.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,48 @@ def should_ignore(filepath: str, ignore_patterns: list[str]) -> bool:
189189
return False
190190

191191

192+
def is_git_new_file(filepath: str) -> bool:
193+
"""Return True if the file is newly added (not yet in HEAD)."""
194+
result = subprocess.run(
195+
["git", "ls-files", "--error-unmatch", filepath],
196+
capture_output=True,
197+
)
198+
if result.returncode != 0:
199+
return True
200+
result2 = subprocess.run(
201+
["git", "show", f"HEAD:{filepath}"],
202+
capture_output=True,
203+
)
204+
return result2.returncode != 0
205+
206+
207+
def check_copyright_year_for_new_file(filepath: str) -> bool:
208+
"""Fail if a new file's SPDX-FileCopyrightText year is not the current year.
209+
210+
Returns True when the year is valid (or the file has no SPDX header yet).
211+
Returns False and prints an error when the year is wrong.
212+
"""
213+
try:
214+
content = Path(filepath).read_text(errors="replace")
215+
except OSError:
216+
return True
217+
218+
match = re.search(r"SPDX-FileCopyrightText.*?(\d{4})", content)
219+
if not match:
220+
return True
221+
222+
year_in_file = match.group(1)
223+
current = _current_year()
224+
if year_in_file != current:
225+
print(
226+
f"{filepath}: new file has copyright year {year_in_file}, "
227+
f"expected {current}",
228+
file=sys.stderr,
229+
)
230+
return False
231+
return True
232+
233+
192234
def main() -> int:
193235
copyright_text = os.environ.get("REUSE_COPYRIGHT", DEFAULT_COPYRIGHT)
194236
license_id = os.environ.get("REUSE_LICENSE", DEFAULT_LICENSE)
@@ -202,6 +244,8 @@ def main() -> int:
202244
if not files:
203245
return 0
204246

247+
failed = False
248+
205249
# Template flag
206250
tpl_flag: list[str] = []
207251
if Path(f".reuse/templates/{template}.jinja2").exists():
@@ -223,6 +267,8 @@ def main() -> int:
223267
# This avoids overwriting existing (possibly different but valid)
224268
# license/copyright information with the template.
225269
if has_valid_spdx_header(filepath):
270+
if is_git_new_file(filepath) and not check_copyright_year_for_new_file(filepath):
271+
failed = True
226272
continue
227273

228274
# Resolve comment style
@@ -249,7 +295,7 @@ def main() -> int:
249295
]
250296
subprocess.run(cmd, check=False)
251297

252-
return 0
298+
return 1 if failed else 0
253299

254300

255301
if __name__ == "__main__":

0 commit comments

Comments
 (0)