Files
SERiF/commit-config/hooks/formatHeader.py

154 lines
5.4 KiB
Python
Executable File

from git import Repo
import datetime
import os
import re
from typing import Tuple
import sys
REDCOLOR = "\033[1;31m"
GREENCOLOR = "\033[1;32m"
YELLOWCOLOR = "\033[1;33m"
BLUECOLOR = "\033[1;34m"
RESETCOLOR = "\033[0m"
HEADER_PATTERN = re.compile(r"^.*\*{71}.*$(?:\n.*)*?^.*\*{71}.*\n", re.MULTILINE)
def get_source_dir(envVar):
sd = os.environ.get(envVar, None)
if sd is None:
print(f"{REDCOLOR}ERROR! Enviromental variable {envVar} is not set. Perhapse you forgot to set it in your shell profile?{RESETCOLOR}")
exit(1)
return sd
def get_git_log(repoPath, filePath):
"""
Retrieves the git commit history for a specific file.
:param repoPath: Path to the Git repository
:param filePath: Path to the file relative to the repo root
:return: List of commit messages with commit hashes
"""
repo = Repo(repoPath)
commits = list(repo.iter_commits(paths=filePath)) # Get commits affecting the file
logEntries = []
for commit in commits:
logEntries.append(f"{commit.hexsha[:7]} - {commit.author.name} - {commit.committed_date}")
return logEntries
def get_all_file_authors(logEntries):
"""
Retrieves the authors of the commits in a list of log entries.
:param logEntries: List of log entries
:return: List of authors
"""
authors = []
for entry in logEntries:
authors.append(entry.split(" - ")[1])
return set(authors)
def get_last_modification_date(logEntries):
"""
Retrieves the last modification date of the file.
:param logEntries: List of log entries
:return: Last modification date
"""
return datetime.datetime.fromtimestamp(int(logEntries[0].split(" - ")[2]))
def select_comment_chars(filename) -> Tuple[str, str, str]:
fileExtension = filename.split(".")[-1]
#switch statement based on file extension
match fileExtension:
case "py":
return ("\"\"\"", "\"\"\"", "#")
case "c" | "cpp" | "h" | "hpp":
return ("/*", "*/", "//")
case "f90" | "f":
return ("!", "!", "!")
case "sh":
return ("#", "#", "#")
case "cmake":
return ("#", "#", "#")
case "build":
return ("#", "#", "#")
case _:
return ("#", "", "#")
def make_header(authors, date, filename, projectName):
authorSuffix = "s" if len(authors) > 1 else ""
authorsString = ', '.join(authors)
bs, be, c = select_comment_chars(filename)
defaultHeader = f"""{bs} ***********************************************************************
{c}
{c} Copyright (C) {date.year} -- The 4D-STAR Collaboration
{c} File Author{authorSuffix}: {authorsString}
{c} Last Modified: {date.date().strftime("%B %d, %Y")}
{c}
{c} {projectName} is free software; you can use it and/or modify
{c} it under the terms and restrictions the GNU General Library Public
{c} License version 3 (GPLv3) as published by the Free Software Foundation.
{c}
{c} {projectName} is distributed in the hope that it will be useful,
{c} but WITHOUT ANY WARRANTY; without even the implied warranty of
{c} MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
{c} See the GNU Library General Public License for more details.
{c}
{c} You should have received a copy of the GNU Library General Public License
{c} along with this software; if not, write to the Free Software
{c} Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
{c}
{c} *********************************************************************** {be}\n"""
return defaultHeader
if __name__ == "__main__":
# Ensure python version at least 3.10
if sys.version_info < (3, 10):
print(f"{REDCOLOR}ERROR! Python version 3.10 or higher is required{RESETCOLOR}")
import argparse
parser = argparse.ArgumentParser(description="Format the header of a file")
parser.add_argument("file", type=str, help="The file to format")
parser.add_argument("--root", type=str, default="_4DSSE_ROOT", help="The name of the environmental variable that points to the root of the project")
parser.add_argument("--project", type=str, default="4DSSE", help="The name of the project")
args = parser.parse_args()
absPath = os.path.abspath(args.file)
fileName = os.path.basename(args.file)
if fileName == "formatHeader.py":
print(f"{BLUECOLOR}NOTE! Cannot format the formatHeader script{RESETCOLOR}")
exit(0)
assert os.path.exists(absPath), f"{REDCOLOR}ERROR! The file {absPath} does not exist{RESETCOLOR}"
sd = get_source_dir(args.root)
if not absPath.startswith(sd):
print(f"{REDCOLOR}ERROR! The file {absPath} is not in the source directory {sd}{RESETCOLOR}")
exit(1)
logEntry = get_git_log(sd, absPath)
authors = get_all_file_authors(logEntry)
lastModificationDate = get_last_modification_date(logEntry)
header = make_header(authors, lastModificationDate, args.file, args.project)
headerNumLines = header.count("\n")
# check if the first lines of the file are the header
with open(args.file, "r") as f:
# Add the header to the file
with open(args.file, "r") as f:
fileContent = f.read()
# remove any old version of the header
fileContent = HEADER_PATTERN.sub("", fileContent)
with open(args.file, "w") as f:
f.write(header)
f.write(fileContent)