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

150 lines
5.0 KiB
Python
Executable File

""" ***********************************************************************
#
# Copyright (C) 2025 -- The 4D-STAR Collaboration
# File Author: Emily Boudreaux
# Last Modified: March 17, 2025
#
# 4DSSE is free software; you can use it and/or modify
# it under the terms and restrictions the GNU General Library Public
# License version 3 (GPLv3) as published by the Free Software Foundation.
#
# 4DSSE is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public License
# along with this software; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# *********************************************************************** """
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)
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)
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)