From 65490b1aaa4e1b5acc87cd1bdab48d982da936ec Mon Sep 17 00:00:00 2001 From: zheromiku <836283459@qq.com> Date: Mon, 28 Sep 2026 18:38:34 +0800 Subject: [PATCH] =?UTF-8?q?ci:=20=E6=8E=A5=E5=85=A5=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E8=A7=84=E8=8C=83=E9=97=B8=E9=97=A8=E4=B8=8E=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E6=B5=81=E6=B0=B4=E7=BA=BF(=E6=9C=AC?= =?UTF-8?q?=E6=9C=BARunner+=E5=BE=AE=E4=BF=A1=E9=80=9A=E7=9F=A5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/scripts/ci_gate.py | 104 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 .gitea/scripts/ci_gate.py diff --git a/.gitea/scripts/ci_gate.py b/.gitea/scripts/ci_gate.py new file mode 100644 index 0000000..6cb8043 --- /dev/null +++ b/.gitea/scripts/ci_gate.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +"""提交信息规范校验(CI 侧权威闸门)。 + +规则与本机 ~/.githooks/commit-msg 完全一致,防止 --no-verify 绕过。 +放行:Merge / Revert " / fixup! / squash! / amend! 开头的自动生成提交。 +""" +import json +import os +import re +import subprocess +import sys + +TYPES = "feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert" +RE = re.compile(r"^(?:" + TYPES + r")(?:\([a-zA-Z0-9._/-]+\))?!?: .{3,}$") +ALLOW = re.compile(r'^(?:Merge |Revert "|fixup! |squash! |amend! )') + +HELP = """ +\u274c 提交信息不合规,已拦截。 + + 规范 : <类型>(<范围>): <简短描述> + 类型 : feat 新功能 | fix 修缺陷 | docs 文档 | style 格式(不改逻辑) + refactor 重构 | perf 性能 | test 测试 | build 构建/依赖 + ci CI 配置 | chore 杂务 | revert 回滚 + 示例 : feat(asset): 新增按歌曲生成应援序列 + fix(parser): 修复表格解析越界导致的 500 + docs: 补充 OSS 短签名接口说明 + + 要点 : 类型小写、冒号后一个空格、描述 \u22653 字符 + 标题 \u226472 字符、结尾不加句号 + 破坏性变更类型后加 !,正文写 BREAKING CHANGE: + 详见 : docs/提交规范.md +""" + + +def collect(): + """优先用 Gitea 事件负载里的本次推送提交,取不到则退回 git log。""" + msgs = [] + ev = os.environ.get("GITHUB_EVENT_PATH") or "" + if ev and os.path.exists(ev): + try: + with open(ev, encoding="utf-8") as fh: + d = json.load(fh) + for c in (d.get("commits") or []): + m = (c.get("message") or "").strip() + if m: + msgs.append(m) + except Exception as e: + print(" 事件负载解析失败(%s),回退到 git log" % e) + if not msgs: + out = subprocess.run(["git", "log", "-n", "5", "--format=%B%x00"], + capture_output=True, text=True).stdout + msgs = [x.strip() for x in out.split("\x00") if x.strip()] + return msgs + + +def subject_of(m): + for line in m.splitlines(): + s = line.strip() + if not s or s.startswith("#"): + continue + return s + return "" + + +def judge(subj): + if not subj: + return "空提交信息" + if ALLOW.match(subj): + return "" + if len(subj) > 72: + return "标题 %d 字符 > 72" % len(subj) + if not RE.match(subj): + return "不符合 <类型>(<范围>): <描述>" + if subj.endswith(".") or subj.endswith("\u3002"): + return "标题结尾不要加句号" + return "" + + +def main(): + msgs = collect() + bad = [] + print("待校验提交 %d 条:" % len(msgs)) + for m in msgs: + subj = subject_of(m) + why = judge(subj) + if why: + bad.append((subj, why)) + print(" \u274c [%s] %s" % (why, subj)) + else: + print(" \u2705 %s" % subj) + try: + with open("/tmp/ci-gate-status", "w") as fh: + fh.write("failure" if bad else "success") + except Exception: + pass + if bad: + print(HELP) + return 1 + print("\u2705 提交信息全部合规") + return 0 + + +if __name__ == "__main__": + sys.exit(main())