154 lines
5.4 KiB
Python
Executable File
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)
|