Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
repos:
- repo: local
hooks:
- id: local-prepare-commit-msg
name: 'local prepare-commit-msg'
entry: 'Utilities/Hooks/prepare-commit-msg'
language: system
stages: [prepare-commit-msg]
- id: kw-commit-msg
name: 'kw commit-msg'
entry: 'python3 Utilities/Hooks/kw-commit-msg.py'
language: system
stages: [commit-msg]
153 changes: 153 additions & 0 deletions Utilities/Hooks/kw-commit-msg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#!/usr/bin/env python3
# ==========================================================================
#
# Copyright NumFOCUS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0.txt
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ==========================================================================

import os
import re
import subprocess
import sys

from pathlib import Path


DEFAULT_LINE_LENGTH: int = 78


def die(message, commit_msg_path):
print("commit-msg hook failure", file=sys.stderr)
print("-----------------------", file=sys.stderr)
print(message, file=sys.stderr)
print("-----------------------", file=sys.stderr)
print(
f"""
To continue editing, run the command
git commit -e -F "{commit_msg_path}"
(assuming your working directory is at the top).""",
file=sys.stderr,
)
sys.exit(1)


def get_max_length():
try:
result = subprocess.run(
["git", "config", "--get", "hooks.commit-msg.ITKCommitSubjectMaxLength"],
capture_output=True,
text=True,
check=True,
)
return int(result.stdout.strip())
except (subprocess.CalledProcessError, ValueError):
return DEFAULT_LINE_LENGTH


def main():
git_dir_path: Path = Path(os.environ.get("GIT_DIR", ".git")).resolve()
commit_msg_path: Path = git_dir_path / "COMMIT_MSG"

if len(sys.argv) < 2:
die(f"Usage: {sys.argv[0]} <git_commit_message_file>", commit_msg_path)

input_file: Path = Path(sys.argv[1])
if not input_file.exists():
die(
f"Missing input_file {sys.argv[1]} for {sys.argv[0]} processing",
commit_msg_path,
)
max_subjectline_length: int = get_max_length()

original_input_file_lines: list[str] = []
with open(input_file) as f_in:
original_input_file_lines = f_in.readlines()

input_file_lines: list[str] = []
for test_line in original_input_file_lines:
test_line = test_line.strip()
is_empty_line_before_subject: bool = (
len(input_file_lines) == 0 and len(test_line) == 0
)
if test_line.startswith("#") or is_empty_line_before_subject:
continue
input_file_lines.append(f"{test_line}\n")

with open(commit_msg_path, "w") as f_out:
f_out.writelines(input_file_lines)

subject_line: str = input_file_lines[0]

if len(subject_line) < 8:
die(
f"The first line must be at least 8 characters:\n--------\n{subject_line}\n--------",
commit_msg_path,
)
if (
len(subject_line) > max_subjectline_length
and not subject_line.startswith("Merge ")
and not subject_line.startswith("Revert ")
):
die(
f"The first line may be at most {max_subjectline_length} characters:\n"
+ "-" * max_subjectline_length
+ f"\n{subject_line}\n"
+ "-" * max_subjectline_length,
commit_msg_path,
)
if re.match(r"^[ \t]|[ \t]$", subject_line):
die(
f"The first line may not have leading or trailing space:\n[{subject_line}]",
commit_msg_path,
)
if not re.match(
r"^(Merge|Revert|BUG:|COMP:|DOC:|ENH:|PERF:|STYLE:|WIP:)\s", subject_line
):
die(
f"""Start ITK commit messages with a standard prefix (and a space):
BUG: - fix for runtime crash or incorrect result
COMP: - compiler error or warning fix
DOC: - documentation change
ENH: - new functionality
PERF: - performance improvement
STYLE: - no logic impact (indentation, comments)
WIP: - Work In Progress not ready for merge
To reference GitHub issue XXXX, add "Issue #XXXX" to the commit message.
If the issue addresses an open issue, add "Closes #XXXX" to the message.""",
commit_msg_path,
)
if re.match(r"^BUG: [0-9]+\.", subject_line):
die(
f'Do not put a "." after the bug number:\n\n {subject_line}',
commit_msg_path,
)
del subject_line

if len(input_file_lines) > 1:
second_line: str = input_file_lines[
1
].strip() # Remove whitespace at beginning and end
if len(second_line) == 0:
input_file_lines[1] = "\n" # Replace line with only newline
else:
die(
f'The second line of the commit message must be empty:\n"{second_line}" with length {len(second_line)}',
commit_msg_path,
)
del second_line


if __name__ == "__main__":
main()
53 changes: 53 additions & 0 deletions Utilities/Hooks/prepare-commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
#==========================================================================
#
# Copyright NumFOCUS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0.txt
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#==========================================================================

egrep-q() {
egrep "$@" >/dev/null 2>/dev/null
}

# First argument is file containing commit message.
commit_msg="$1"

# Check for our extra instructions.
egrep-q "^# Start ITK commit messages" -- "$commit_msg" && return 0

# Insert our extra instructions.
commit_msg_tmp="$commit_msg.$$"
instructions='#\
# Start ITK commit messages with a standard prefix (and a space):\
# BUG: - fix for runtime crash or incorrect result\
# COMP: - compiler error or warning fix\
# DOC: - documentation change\
# ENH: - new functionality\
# PERF: - performance improvement\
# STYLE: - no logic impact (indentation, comments)\
# WIP: - Work In Progress not ready for merge\
#\
# The first line of the commit message should preferably be 72 characters\
# or less; the maximum allowed is 78 characters.\
#\
# Follow the first line commit summary with an empty line, then a detailed\
# description in one or more paragraphs.\
#' &&
sed '/^# On branch.*$/ a\
'"$instructions"'
/^# Not currently on any branch.*$/ a\
'"$instructions"'
' "$commit_msg" > "$commit_msg_tmp" &&
mv "$commit_msg_tmp" "$commit_msg"