Files
pluginBase/scripts/pre-commit
T

136 lines
4.3 KiB
Python
Executable File

#!/usr/bin/env python3
import json
import re
import subprocess
import sys
from pathlib import Path
REPO_ROOT = Path(subprocess.check_output(["git", "rev-parse", "--show-toplevel"], text=True).strip())
PLUGIN_JSON_PATH = REPO_ROOT / "plugin.json"
STOP_PATTERNS = [
r"\[no[-_ ]?bump\]",
r"\[skip[-_ ]?bump\]",
r"\[no[-_ ]?version[-_ ]?bump\]",
r"\[skip[-_ ]?version\]",
r"\bno[-_ ]bump\b",
r"\bskip[-_ ]bump\b",
r"\bno[-_ ]version[-_ ]bump\b",
]
def increment_patch_version(version_str: str) -> str:
match = re.fullmatch(r"^(\d+)\.(\d+)\.(\d+)(.*)$", version_str.strip())
if not match:
raise ValueError(f"Version '{version_str}' does not match x.y.z format")
major, minor, patch, suffix = match.groups()
new_patch = int(patch) + 1
return f"{major}.{minor}.{new_patch}{suffix}"
def get_head_version() -> str | None:
res = subprocess.run(
["git", "rev-parse", "--verify", "HEAD"],
cwd=REPO_ROOT,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
if res.returncode != 0:
return None
show_res = subprocess.run(
["git", "show", "HEAD:plugin.json"],
cwd=REPO_ROOT,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
text=True,
)
if show_res.returncode != 0:
return None
try:
head_data = json.loads(show_res.stdout)
return str(head_data.get("version")) if "version" in head_data else None
except json.JSONDecodeError:
return None
def should_skip_from_commit_msg() -> bool:
# 1. Check GIT_PARAMS / COMMIT_EDITMSG / MERGE_MSG
candidates = [
REPO_ROOT / ".git" / "COMMIT_EDITMSG",
REPO_ROOT / ".git" / "MERGE_MSG",
]
for p in candidates:
if p.exists():
content = p.read_text(encoding="utf-8", errors="replace")
lines = [line for line in content.splitlines() if not line.strip().startswith("#")]
text = " ".join(lines)
for pattern in STOP_PATTERNS:
if re.search(pattern, text, re.IGNORECASE):
return True
# 2. Check process command-line arguments (e.g. git commit -m "...")
try:
# On Linux, inspect parent process /proc cmdline
ppid = subprocess.check_output(["ps", "-o", "ppid=", "-p", str(Path("/proc/self").resolve().name)], text=True).strip()
cmdline_path = Path(f"/proc/{ppid}/cmdline")
if cmdline_path.exists():
cmd_args = cmdline_path.read_bytes().decode("utf-8", errors="replace")
for pattern in STOP_PATTERNS:
if re.search(pattern, cmd_args, re.IGNORECASE):
return True
except Exception:
pass
return False
def main() -> int:
# Don't auto-bump during merge/rebase in progress
if (REPO_ROOT / ".git" / "MERGE_HEAD").exists() or (REPO_ROOT / ".git" / "rebase-merge").exists():
return 0
if should_skip_from_commit_msg():
print("pre-commit: stop word detected in commit message, skipping version bump")
return 0
if not PLUGIN_JSON_PATH.exists():
return 0
try:
data = json.loads(PLUGIN_JSON_PATH.read_text(encoding="utf-8"))
current_version = str(data.get("version", ""))
except Exception as e:
print(f"pre-commit: failed to parse {PLUGIN_JSON_PATH}: {e}", file=sys.stderr)
return 1
head_version = get_head_version()
# Skip if version was manually changed (differs from HEAD)
if head_version is not None and current_version != head_version:
print(
f"pre-commit: version already modified ({head_version} -> {current_version}), skipping auto-increment"
)
return 0
try:
new_version = increment_patch_version(current_version)
data["version"] = new_version
PLUGIN_JSON_PATH.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
except Exception as e:
print(f"pre-commit: failed to bump version in {PLUGIN_JSON_PATH}: {e}", file=sys.stderr)
return 1
res = subprocess.run(["git", "add", str(PLUGIN_JSON_PATH)], cwd=REPO_ROOT)
if res.returncode != 0:
print("pre-commit: failed to stage plugin.json", file=sys.stderr)
return res.returncode
print(f"pre-commit: bumped version {current_version} -> {new_version}")
return 0
if __name__ == "__main__":
sys.exit(main())