aboutsummaryrefslogtreecommitdiff
path: root/scripts/conventional_commits.py
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-02 21:48:02 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-02 21:48:02 +0300
commitb751d102f158968aab681cb663d27b4c32197f62 (patch)
tree85d03609314559df7705c2cb0ab4e37ab6d9715f /scripts/conventional_commits.py
parent295b09ca25edbccac65401ee84fa420dd3888aad (diff)
feat: add preview update channel
Diffstat (limited to 'scripts/conventional_commits.py')
-rw-r--r--scripts/conventional_commits.py74
1 files changed, 74 insertions, 0 deletions
diff --git a/scripts/conventional_commits.py b/scripts/conventional_commits.py
new file mode 100644
index 00000000..3c61d49b
--- /dev/null
+++ b/scripts/conventional_commits.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+from __future__ import annotations
+
+import argparse
+import re
+import subprocess
+from pathlib import Path
+
+ALLOWED_TYPES = {
+ "feat",
+ "fix",
+ "perf",
+ "docs",
+ "ci",
+ "test",
+ "refactor",
+ "chore",
+ "release",
+}
+SUBJECT_RE = re.compile(r"^(?P<kind>[a-z]+)(?:\([^)]+\))?!?:\s+\S")
+
+
+def git_subjects(rev_range: str) -> list[str]:
+ output = subprocess.check_output(
+ ["git", "log", "--pretty=format:%s", rev_range], text=True
+ ).strip()
+ return [line.strip() for line in output.splitlines() if line.strip()]
+
+
+def valid_subject(subject: str) -> bool:
+ match = SUBJECT_RE.match(subject)
+ return bool(match and match.group("kind") in ALLOWED_TYPES)
+
+
+def commit_message_subject(path: Path) -> str | None:
+ for line in path.read_text(encoding="utf-8").splitlines():
+ subject = line.strip()
+ if subject and not subject.startswith("#"):
+ return subject
+ return None
+
+
+def main() -> int:
+ parser = argparse.ArgumentParser(description="Validate conventional commit subjects")
+ parser.add_argument("subjects", nargs="*")
+ parser.add_argument("--range", dest="rev_range")
+ parser.add_argument("--message-file")
+ args = parser.parse_args()
+
+ subjects = list(args.subjects)
+ if args.rev_range:
+ subjects.extend(git_subjects(args.rev_range))
+ if args.message_file:
+ subject = commit_message_subject(Path(args.message_file))
+ if subject:
+ subjects.append(subject)
+
+ invalid = [subject for subject in subjects if not valid_subject(subject)]
+ if invalid:
+ print("invalid commit subject(s):")
+ for subject in invalid:
+ print(f" {subject}")
+ print(
+ "commit subjects must use conventional commits because preview notes are generated from them."
+ )
+ print("example: fix(update): install selected channel")
+ print("expected: type(optional-scope): subject")
+ print("allowed types: " + ", ".join(sorted(ALLOWED_TYPES)))
+ return 1
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())