1+ #!/usr/bin/env python3
2+ """
3+ Update copyright year in source files to the current year.
4+ """
5+ import re
6+ import sys
7+ from datetime import datetime
8+ from pathlib import Path
9+
10+ # Set current year
11+ YEAR = str (datetime .now ().year )
12+
13+ EXTENSIONS = {".py" , ".md" }
14+
15+ # Directories to exclude
16+ EXCLUDE_DIRS = {
17+ ".venv" ,
18+ "venv" ,
19+ "node_modules" ,
20+ ".git" ,
21+ "__pycache__" ,
22+ ".tox" ,
23+ "dist" ,
24+ "build" ,
25+ ".egg-info" ,
26+ }
27+
28+ # Regex patterns to match copyright lines
29+ COPYRIGHT_PATTERNS = [
30+ re .compile (r"(# Copyright )(\d{4})( The HuggingFace Team\.)" ),
31+ re .compile (r"(# Copyright \(c\) )(\d{4})( The HuggingFace Team\.)" ),
32+ re .compile (r"(Copyright )(\d{4})( The HuggingFace Team\.)" ),
33+ re .compile (r"(Copyright \(c\) )(\d{4})( The HuggingFace Team\.)" ),
34+ ]
35+
36+
37+ def should_exclude (path : Path ) -> bool :
38+ """Check if path should be excluded."""
39+ return any (excluded in path .parts for excluded in EXCLUDE_DIRS )
40+
41+
42+ def update_file (file_path : Path ) -> int :
43+ """Update copyright year in a single file. Returns number of updates."""
44+ try :
45+ content = file_path .read_text (encoding = "utf-8" )
46+ except Exception as e :
47+ print (f"Warning: Could not read { file_path } : { e } " , file = sys .stderr )
48+ return 0
49+
50+ new_content = content
51+ total_count = 0
52+
53+ for pattern in COPYRIGHT_PATTERNS :
54+ new_content , count = pattern .subn (rf"\g<1>{ YEAR } \g<3>" , new_content )
55+ total_count += count
56+
57+ if total_count > 0 :
58+ try :
59+ file_path .write_text (new_content , encoding = "utf-8" )
60+ print (f"✓ Updated { total_count } line(s) in { file_path } " )
61+ except Exception as e :
62+ print (f"Error: Could not write { file_path } : { e } " , file = sys .stderr )
63+ return 0
64+
65+ return total_count
66+
67+
68+ def main ():
69+ repo_root = Path ("." ).resolve ()
70+
71+ print (f"Updating copyright to { YEAR } ..." )
72+
73+ total_files = 0
74+ total_updates = 0
75+
76+ for file_path in repo_root .rglob ("*" ):
77+ if not file_path .is_file () or should_exclude (file_path ):
78+ continue
79+
80+ if file_path .suffix in EXTENSIONS :
81+ updates = update_file (file_path )
82+ if updates > 0 :
83+ total_files += 1
84+ total_updates += updates
85+
86+ print (f"\n Summary: Updated { total_updates } line(s) in { total_files } file(s)" )
87+
88+ # Exit with 0 if updates were made, 1 if no updates needed
89+ return 0 if total_updates > 0 else 1
90+
91+
92+ if __name__ == "__main__" :
93+ sys .exit (main ())
0 commit comments