Last active
March 29, 2022 21:20
-
-
Save gabrielfeo/7bc26bfdd4d4679a3e74962f4958bfec to your computer and use it in GitHub Desktop.
Add the Mozilla Public License (MPL) v2.0 header to every source file in a directory. Apache's license-eye does this already, but its formatting of comments isn't perfect, e.g. no newline after the header comment.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
from collections import namedtuple | |
from ctypes import Union | |
import os | |
import argparse | |
from fileinput import FileInput | |
import textwrap | |
parser = argparse.ArgumentParser() | |
parser.add_argument("--holder", required=True) | |
parser.add_argument("--year", required=True) | |
parser.add_argument("dir") | |
args = parser.parse_args() | |
excluded_dirnames = [ | |
'build', | |
] | |
Header = namedtuple('Header', ['comment', 'file_extensions']) | |
# DISCLAIMER: These are standard Exhibit A headers, but with https URLs | |
headers = [ | |
Header( | |
comment= | |
f"""\ | |
# Copyright (c) {args.year} {args.holder} | |
# | |
# This Source Code Form is subject to the terms of the Mozilla Public | |
# License, v. 2.0. If a copy of the MPL was not distributed with this | |
# file, You can obtain one at https://mozilla.org/MPL/2.0/.\ | |
""", | |
file_extensions=[ | |
'py', | |
'yaml', | |
'yml', | |
'sh', | |
'Dockerfile', | |
], | |
), | |
Header( | |
comment= | |
f"""\ | |
/* | |
* Copyright (c) {args.year} {args.holder} | |
* | |
* This Source Code Form is subject to the terms of the Mozilla Public | |
* License, v. 2.0. If a copy of the MPL was not distributed with this | |
* file, You can obtain one at https://mozilla.org/MPL/2.0/. | |
*/\ | |
""", | |
file_extensions=[ | |
'java', | |
'kt', | |
'kts', | |
'gradle', | |
'groovy', | |
'gvy', | |
], | |
), | |
] | |
def prepend_comment(comment, filepath): | |
# Trim common indents in multi-line string | |
comment = textwrap.dedent(comment) | |
if os.stat(filepath).st_size == 0: | |
with open(filepath, 'w') as file: | |
file.write(f"{comment}\n") | |
return | |
with FileInput(filepath, inplace=True) as file: | |
header_line = 1 | |
for line in file: | |
if file.lineno() == header_line: | |
# Always scan line before deciding to print comment | |
if line.startswith("#!"): | |
# Print shebang and leave comment for the next round | |
print(line) | |
header_line = 2 | |
continue | |
else: | |
print(comment) | |
if not line.startswith('\n'): | |
# Ensure there's 1 blank line between comment and first source line | |
print('') | |
print(line, end='') | |
def decide_header_comment(filename: str) -> str: | |
if filename in __file__: | |
# In case this script is editing its own directory | |
return None | |
file_extension = filename.split('.')[-1] | |
for header in headers: | |
if file_extension in header.file_extensions: | |
return header.comment | |
return None | |
for root, dirnames, filenames in os.walk(args.dir): | |
for exclude in [d for d in dirnames if d in excluded_dirnames]: | |
dirnames.remove(exclude) | |
for name in filenames: | |
comment = decide_header_comment(name) | |
if comment is None: | |
continue | |
path = os.path.join(root, name) | |
print("Editing", path) | |
prepend_comment(comment, path) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment