Files
gplan-2/tools/gdstyle_gate.py
T

118 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""gdstyle_gate.py — S13 第三方质量规则闸件(game/tests/static_scan.sh 调用)
跑 gdstyle(github.com/atelico/gdstyle,Rust 单二进制,版本钉 v0.4.0)质量规则扫描,
与存量基线 tools/gdstyle_baseline.txt 做差集——只有基线外的新违规才红。
规则口径见 tools/gdstyle.toml 头注(只开 quality/*+syntax/reserved-identifier,
format/naming/order 全关)。扫描面=产品码三目录(game/core、game/data、game/presentation,
与 S9 类型闸同口径——probe/sim/tests 测量面直构惯例不在面)。
基线键=文件|规则|消息 三元组:消息含违规对象标识(如 duplicated load 的路径),行漂移稳定
(gdstyle 的 JSON 输出不含源码行文本,行号键会随编辑成片假红)。基线重生成=人工专用
(--refresh),禁在闸内自动重生成(防清基线换绿)。
二进制:本机固定路径 D:/code/gdstyle/gdstyle.exe(不入库不入包),GDSTYLE_BIN 环境变量覆盖;
版本与钉定版不符即红——升级工具须重测语法兼容性并人工重生成基线。
退出码:0=无基线外新违规;1=有新违规/二进制缺失或版本不符/扫描面为空/输出不可解析;2=用法错。
用法:python tools/gdstyle_gate.py # 闸判定
python tools/gdstyle_gate.py --refresh # 人工重生成基线(覆盖旧文件)
"""
import json
import os
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
CONFIG = ROOT / "tools" / "gdstyle.toml"
BASELINE = ROOT / "tools" / "gdstyle_baseline.txt"
SCAN_DIRS = ["game/core", "game/data", "game/presentation"]
PINNED_VERSION = "0.4.0"
DEFAULT_BIN = "D:/code/gdstyle/gdstyle.exe"
MAX_REPORT = 50
def fail(msg: str) -> int:
print("[gdstyle_gate] FAIL — %s" % msg)
return 1
def run_gate() -> int:
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
refresh = "--refresh" in sys.argv[1:]
if sys.argv[1:] and not refresh:
print("[gdstyle_gate] 用法: python tools/gdstyle_gate.py [--refresh]")
return 2
binary = os.environ.get("GDSTYLE_BIN", DEFAULT_BIN)
try:
ver = subprocess.run([binary, "--version"], capture_output=True, text=True, timeout=30)
except (FileNotFoundError, OSError):
return fail("gdstyle 二进制不可用:%s(本机固定路径,GDSTYLE_BIN 可覆盖;"
"v%s Windows 版见 github.com/atelico/gdstyle releases)" % (binary, PINNED_VERSION))
if PINNED_VERSION not in (ver.stdout or ""):
return fail("gdstyle 版本不符:钉定 v%s,实得 %r——升级工具须重测语法兼容性并人工重生成基线"
% (PINNED_VERSION, (ver.stdout or "").strip()))
face = [p for d in SCAN_DIRS for p in Path(ROOT, d).rglob("*.gd")]
if not face:
return fail("扫描面为空——%s 下零个 .gd(路径写错导致的静默空转)" % "、".join(SCAN_DIRS))
try:
proc = subprocess.run(
[binary, "check", "--format", "json", "--no-color", "-c", str(CONFIG)] + SCAN_DIRS,
capture_output=True, text=True, timeout=300, cwd=str(ROOT), encoding="utf-8",
)
except subprocess.TimeoutExpired:
return fail("gdstyle 扫描超时(300s)")
try:
findings = json.loads(proc.stdout or "[]")
except json.JSONDecodeError:
return fail("gdstyle 输出不可解析为 JSON(stderr 头部:%r)" % (proc.stderr or "")[:200])
keys = set()
for f in findings:
if not isinstance(f, dict) or "rule" not in f:
continue
file_path = str(f.get("file", "")).replace("\\", "/")
keys.add("%s|%s|%s" % (file_path, f["rule"], f.get("message", "")))
if refresh:
lines = [
"# gdstyle 存量基线(S13)——文件|规则|消息 三元组,每行一条;只记基线不记豁免理由。",
"# 重生成=人工专用:python tools/gdstyle_gate.py --refresh(禁清基线换绿;改规则口径/工具升级须随批重生成)。",
]
lines += sorted(keys)
BASELINE.write_text("\n".join(lines) + "\n", encoding="utf-8")
print("[gdstyle_gate] 基线已重生成:%s 条(%s)" % (len(keys), BASELINE.name))
return 0
base = set()
if BASELINE.exists():
for line in BASELINE.read_text(encoding="utf-8").splitlines():
s = line.strip()
if s and not s.startswith("#"):
base.add(s)
else:
return fail("基线文件不存在:%s(首次落位跑 python tools/gdstyle_gate.py --refresh,须经用户批准)" % BASELINE.name)
new = sorted(keys - base)
print("[gdstyle_gate] 扫描面 %d 个 .gd;命中 %d 条,基线内 %d 条,基线外新违规 %d 条"
% (len(face), len(keys), len(keys & base), len(new)))
if new:
for k in new[:MAX_REPORT]:
print(" NEW " + k)
if len(new) > MAX_REPORT:
print(" ...(截断,共 %d 条)" % len(new))
return fail("基线外新违规 %d 条——新码须修到基线口径内,或经用户批准随批重生成基线" % len(new))
print("[gdstyle_gate] ok")
return 0
if __name__ == "__main__":
sys.exit(run_gate())