fix(scripts): rename_ref 搬整目录不再改脏目录内部内容,并补齐出链层级
2026-10-30 测试说明分层那次(28 个路径、209 文件)实测踩到两条,都是
「把不该改的东西改脏」,当时的补救全靠事后从 git 里捞原始字节:
B1 整目录搬迁会改写目录内部文件的内容。「相对引用文件所在目录」这条
解析规则不检验目标是否存在,于是台架日志里不是路径的斜杠片段被当成
「旧目录下的子路径」并插上目录名:`1/1 PASSED` → `test_data/1/1 PASSED`、
`13639/32768`、日期占位 `[----/--/-- --:--:--]` 全中招,连有效的
`see ../a.md` 也被换成指向不存在的 `see td/a.md`。23 个证据文件是
用 `git show HEAD:<旧路径>` 逐字节还原才救回来的。
B2 目录内部文件指向外部的 `../` 出链不补层级。emit_dir 取的是
`new_abs.parent`,整目录搬迁时少算一层,`../images/x.svg` 搬深一层后
仍指旧层级;图片按文档目录相对(§3.2),check_doc_links 看不见 → 静默断链。
改法(尽量小面):
- resolve_token:按引用文件目录解析时要求目标真存在;根相对写法保持原宽松口径。
- rewrite_text:moved 文件的 emit_dir 改成「本文件搬完实际所在的目录」;
单文件搬迁走原分支,一字未动。
- 整目录搬迁时目录内部互为相对的引用一律不改(两侧一起搬,改了必错);
「旧路径是不是目录」必须在搬迁前记进 old_is_dir_global——--apply 是先
git mv 再改写,那时旧路径已经不存在了。
- glob 形态改成看得见:搬目录时前缀整体跟着搬(本来就该改);搬单文件时
一条 glob 可能同时盖住搬走与没搬走的两批文件,不猜,只点名列出。
分层那次有 4 处这种引用是静默漏改、事后手改的。
回归:新增 `--selftest`(在一次性临时 git 仓里跑本脚本的副本,`.gitignore`
先排除 /scripts/ 免得脚本自己进扫描面)。案例 A 守整目录搬迁(证据文件
逐字节不变、出链补层、别人文件的入链照改、glob 前缀跟着搬),案例 B 守
单文件搬迁的层级换算与 glob 提示,18 项断言全绿。反向对照也做了:同一
夹具在改前版本上 B1/B2 都复现,所以断言不是凭空写的。
文档同步:docs/文档规范.md §3.6 第 4/5 条从「现存危害」改写成「已修 +
验收判据」,新增第 6 条 glob 提示,并加「改本体必须先跑 --selftest」;
docs/测试说明/README.md 第 3/5 节口径同步;scripts/README.md 补 --selftest
与任务树 --no-verify 的用法行。顺带修掉分层那笔里我写错的日期:
2026-11-01 → 2026-10-30(文档规范 4 处、测试说明 README 2 处,真值就是今天)。
树内验证:AST OK;--selftest PASS;改动的 3 个 .md 单文件
check_doc_links --sections-strict RC=0(36 处章节引用、可判定 34、无法判定 0);
字节体检干净。未跑 @build(无 .c/.h 改动)。
This commit is contained in:
+4
-1
@@ -33,7 +33,7 @@
|
||||
| 脚本 | 作用 |
|
||||
|---|---|
|
||||
| `rename_ids.py` | **编号(字面量)批量改名**:映射表 JSON 驱动,`files` 显式 scope(同一裸形字面量可能属多族,实测 `S3` 三义 → 全局替换必造新撞号);默认干跑,`--apply` 落盘并自动写 `id_aliases.json` 的 scoped 旧号→新号条目;`[示例]`/`[计划]` 行与变更记录表不改写;按原字节读写(保 BOM 与行尾)。用法与批次记录 → `scripts/archive/id_gate/编号治理历史.md` §8.3 |
|
||||
| `rename_ref.py` | **改名/挪目录 + 全库引用自动改写**:默认 dry-run 预览,`--apply` 才落盘(`git mv` → 改写引用 → 自动跑 `check_doc_links --all` 复核)。覆盖反引号引用、Markdown 链接、图片 `../` 层级换算、`#include`、`uvprojx`/`CMakeLists.txt` 源列表。固定动作与边界 → `docs/文档规范.md` §3.6 |
|
||||
| `rename_ref.py` | **改名/挪目录 + 全库引用自动改写**:默认 dry-run 预览,`--apply` 才落盘(`git mv` → 改写引用 → 自动跑 `check_doc_links --all` 复核)。覆盖反引号引用、Markdown 链接、图片 `../` 层级换算(**整目录搬迁时目录内部文件的出链按新目录补层**)、`#include`、`uvprojx`/`CMakeLists.txt` 源列表;搬**单文件**时指向旧目录的 glob 不自动改、但会点名列出。**改本脚本必须跑 `--selftest`**(一次性临时 git 仓,守住"搬目录把内部文件改脏"与"出链少补一层"两条实测踩过的坑)。固定动作与边界 → `docs/文档规范.md` §3.6 |
|
||||
|
||||
> `check_doc_links.py` 除链接存在性外,还校验 **`§N.M` 章节引用**是否指向目标文档真实存在的编号标题(口径见 `docs/文档规范.md` §3.5)。
|
||||
|
||||
@@ -77,6 +77,9 @@
|
||||
& '.venv\Scripts\python.exe' scripts/check_doc_links.py --all
|
||||
& '.venv\Scripts\python.exe' scripts/rename_ref.py docs/旧/名.md docs/新/名.md # 预览
|
||||
& '.venv\Scripts\python.exe' scripts/rename_ref.py docs/旧/名.md docs/新/名.md --apply # 落盘
|
||||
& '.venv\Scripts\python.exe' scripts/rename_ref.py --selftest # 改 rename_ref 本体必须跑(一次性临时 git 仓回归)
|
||||
# 任务工作树里落盘请加 --no-verify:树内 simulator/SDL2 是目录联接,收尾的 check_doc_links --all
|
||||
# 会把同一份仓库当第二副本扫出假红;全库 --all 复核留到主工作树跑
|
||||
& '.venv\Scripts\python.exe' scripts/gen_task_table.py --check # 或 --write / --diff
|
||||
& '.venv\Scripts\python.exe' scripts/check_mem_numbers.py --check
|
||||
& '.venv\Scripts\python.exe' scripts/check_test_suite.py # 门禁判定
|
||||
|
||||
+209
-2
@@ -12,6 +12,11 @@
|
||||
python scripts/rename_ref.py <旧路径> <新路径> --apply # 真正落盘(git mv + 改写引用)
|
||||
python scripts/rename_ref.py <旧路径> <新目录/ # 挪进目录(保留文件名)
|
||||
python scripts/rename_ref.py docs/a/ docs/b/ --apply # 整目录搬迁(前缀改写)
|
||||
python scripts/rename_ref.py --selftest # 一次性临时 git 仓回归;**改本脚本必须跑**
|
||||
|
||||
--apply 落盘后会自动跑 `check_doc_links.py --all` 复核;**任务工作树**里有
|
||||
`simulator/SDL2` 这类目录联接,`--all` 会把同一份仓库当第二副本一起扫出假红
|
||||
→ 树内改用 `--no-verify`,全库 `--all` 留到主工作树跑。
|
||||
|
||||
路径一律写**仓库根相对路径**(正斜杠),末尾不带 `/` 也可。
|
||||
|
||||
@@ -35,8 +40,10 @@ import argparse
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib.parse
|
||||
|
||||
# 控制台编码兜底(同 check_ids.py / check_doc_links.py / check_test_traceability.py):
|
||||
@@ -150,8 +157,17 @@ def resolve_token(tok: str, file_dir: pathlib.Path, old_abs: pathlib.Path):
|
||||
rc = pathlib.Path(os.path.normpath(base / b2))
|
||||
except (OSError, ValueError):
|
||||
continue
|
||||
if rc == old_abs or str(rc).startswith(str(old_abs) + os.sep):
|
||||
if rc == old_abs:
|
||||
return rc, var_prefix, via_root
|
||||
if str(rc).startswith(str(old_abs) + os.sep):
|
||||
# 只有「目标真存在」才算引用(根相对写法保持原来的宽松口径)。
|
||||
# 不加这道判断时,整目录搬迁会把目录**内部**文件里的斜杠片段
|
||||
# 当成「旧目录下的子路径」并插上目录名:`1/1 PASSED` →
|
||||
# `test_data/1/1 PASSED`、`13639/32768`、日期占位 `[----/--/-- …]`
|
||||
# 全被改脏,而台架证据文件是不可再生的原始记录
|
||||
# (2026-10-30 实测,23 个文件靠 `git show HEAD:<旧路径>` 逐字节还原)。
|
||||
if via_root or rc.exists():
|
||||
return rc, var_prefix, via_root
|
||||
|
||||
# ${VAR}/name.ext 之类:变量指向的目录未知,按「尾段后缀」匹配
|
||||
parts = re.split(r"[/\\]", body)
|
||||
@@ -210,6 +226,9 @@ def new_spelling(tok: str, resolved: pathlib.Path, var_prefix: str, via_root: bo
|
||||
|
||||
old_abs_global = [None]
|
||||
new_abs_global = [None]
|
||||
# 「旧路径是目录」必须在**搬迁之前**记下来(--apply 先 git mv 再改写,那时旧路径已不存在),
|
||||
# 整目录搬迁与单文件搬迁的语义不同:目录内部互为相对的引用两侧一起搬,不该改写。
|
||||
old_is_dir_global = [False]
|
||||
|
||||
|
||||
def rewrite_text(text: str, file_path: pathlib.Path, changes: list, lineno_map=True):
|
||||
@@ -223,7 +242,15 @@ def rewrite_text(text: str, file_path: pathlib.Path, changes: list, lineno_map=T
|
||||
moved = (file_abs == old_abs
|
||||
or str(file_abs).startswith(str(old_abs) + os.sep))
|
||||
resolve_dir = file_path.parent
|
||||
emit_dir = new_abs.parent if moved else file_path.parent
|
||||
if not moved:
|
||||
emit_dir = file_path.parent
|
||||
elif file_abs == old_abs:
|
||||
emit_dir = new_abs.parent
|
||||
else:
|
||||
# 整目录搬迁时本文件也跟着搬:它的**新目录**是 new_abs/<相对旧目录子路径>
|
||||
# 的父目录。用 new_abs.parent 会少算一层,出链的 ../ 就补不出来
|
||||
# (2026-10-30 实测:文档搬进子目录后 `../images/x.svg` 仍指旧层级)。
|
||||
emit_dir = (new_abs / file_abs.relative_to(old_abs)).parent
|
||||
|
||||
out = []
|
||||
pos = 0
|
||||
@@ -244,6 +271,12 @@ def rewrite_text(text: str, file_path: pathlib.Path, changes: list, lineno_map=T
|
||||
continue
|
||||
if resolved is None:
|
||||
continue
|
||||
if (moved and old_is_dir_global[0] and not via_root and not var_prefix
|
||||
and resolved != old_abs):
|
||||
# 整目录搬迁:目录**内部**互为相对的引用(`./b.md`、`../a.md` 这类)
|
||||
# 两侧一起搬,改写只会把 `./b.md` 抖成 `b.md`、把仍有效的 `../a.md`
|
||||
# 换成错的一层——保持原样才是对的。根相对写法不受此条影响(照常改)。
|
||||
continue
|
||||
rep = new_spelling(tok, resolved, var_prefix, via_root, emit_dir)
|
||||
if rep == tok:
|
||||
continue
|
||||
@@ -284,6 +317,35 @@ def rewrite_text(text: str, file_path: pathlib.Path, changes: list, lineno_map=T
|
||||
return new_text
|
||||
|
||||
|
||||
def glob_shaped_refs(text: str, candidate_prefixes):
|
||||
"""挑出「目录/通配符」形态的引用(`docs/td/*.md`),只列出不改写。
|
||||
|
||||
为什么只提示:一条 glob 可能同时盖住「这次要搬的」和「不搬的」文件
|
||||
(分层那次 `docs/测试说明/*_Test_Guide.md` 只有一部分文件进了 `板级测试/`),
|
||||
自动改等于猜。这类形态此前**静默漏改**(token 匹配到 `*` 前就断了),
|
||||
2026-10-30 实测有 4 处、全靠事后手改,所以这里把它变成看得见的东西。
|
||||
"""
|
||||
hits = []
|
||||
for m in re.finditer(r"[^\s\"'`()<>|,;:,、]+[/\\][^\s\"'`()<>|,;:,、]*[*?]", text):
|
||||
tok = m.group(0)
|
||||
cut = tok.rfind("*")
|
||||
if cut < 0:
|
||||
cut = tok.rfind("?")
|
||||
prefix = tok[:cut].replace("\\", "/").rstrip("/")
|
||||
if not prefix:
|
||||
continue
|
||||
# 把通配符后面的尾巴(`docs/x/*.md` 里的 `.md`)也带上,提示才看得出是哪条 glob
|
||||
end = m.end()
|
||||
while end < len(text) and text[end] not in " \t\r\n\"'`()<>|,;:,、":
|
||||
end += 1
|
||||
shown = tok + text[m.end():end]
|
||||
for cand in candidate_prefixes:
|
||||
if cand and (prefix == cand or prefix.startswith(cand + "/")):
|
||||
hits.append((text.count("\n", 0, m.start()) + 1, shown))
|
||||
break
|
||||
return hits
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
ap = argparse.ArgumentParser(
|
||||
description="改名/挪目录并自动改写全库引用(默认 dry-run)")
|
||||
@@ -318,8 +380,18 @@ def main(argv=None):
|
||||
|
||||
old_abs_global[0] = old_abs
|
||||
new_abs_global[0] = new_abs
|
||||
old_is_dir_global[0] = old_abs.is_dir()
|
||||
# 通配符形态引用的提示面(只提示、不改写):
|
||||
# 搬**目录**:glob 前缀会被 token 改写整体一起搬走(`docs/td/*.md` →
|
||||
# `docs/moved/td/*.md`),目录里的东西全搬 → 那是正确改写,不必提示;
|
||||
# 搬**单个文件**:glob 前缀指向它原来所在的目录,脚本不动它 → 这条 glob
|
||||
# 会**静默漏改**,而它可能同时盖住搬走和没搬走的两拨文件,自动改等于猜,
|
||||
# 只能人工判断 → 列出来(2026-10-30 分层那次实测 4 处,全靠事后手改)。
|
||||
old_parent_rel = old_rel.rsplit("/", 1)[0] if "/" in old_rel else ""
|
||||
glob_cands = set() if old_is_dir_global[0] else {old_parent_rel}
|
||||
|
||||
plan = []
|
||||
glob_hits = []
|
||||
hit_generated = False
|
||||
for f in tracked_files():
|
||||
if is_generated(rel(f)):
|
||||
@@ -329,6 +401,8 @@ def main(argv=None):
|
||||
text, bom, ok = read_text(f)
|
||||
if not ok:
|
||||
continue
|
||||
for lineno, tok in glob_shaped_refs(text, glob_cands):
|
||||
glob_hits.append((rel(f), lineno, tok))
|
||||
changes = []
|
||||
new_text = rewrite_text(text, f, changes)
|
||||
if changes:
|
||||
@@ -345,6 +419,15 @@ def main(argv=None):
|
||||
print(f" … 其余 {len(changes) - 12} 处略")
|
||||
print()
|
||||
|
||||
if glob_hits:
|
||||
print(f"⚠ 通配符形态引用 {len(glob_hits)} 处——本脚本**不改写**(一条 glob 可能同时"
|
||||
f"盖住搬与不搬的文件),请人工确认这些 glob 该指哪一层:")
|
||||
for r, lineno, tok in glob_hits[:20]:
|
||||
print(f" {r}:{lineno} {tok}")
|
||||
if len(glob_hits) > 20:
|
||||
print(f" … 其余 {len(glob_hits) - 20} 处略")
|
||||
print()
|
||||
|
||||
if not args.apply:
|
||||
print("(dry-run:未做任何修改。确认无误后加 --apply)")
|
||||
return 0
|
||||
@@ -399,5 +482,129 @@ def main(argv=None):
|
||||
return 0
|
||||
|
||||
|
||||
def _st_make_repo(root: pathlib.Path, files: dict) -> None:
|
||||
"""在 root 建一个一次性 git 仓并写入 files(仓库根相对 POSIX 路径 → 内容)。
|
||||
|
||||
`.gitignore` 里先排除 `/scripts/`:selftest 会把本脚本的副本放进临时仓,
|
||||
不排除的话脚本自己也会进扫描面,断言就不再只针对夹具了。
|
||||
"""
|
||||
root.mkdir(parents=True, exist_ok=True)
|
||||
(root / ".gitignore").write_text("/scripts/\n", encoding="utf-8")
|
||||
for relp, body in files.items():
|
||||
p = root / relp
|
||||
p.parent.mkdir(parents=True, exist_ok=True)
|
||||
p.write_text(body, encoding="utf-8")
|
||||
git = ["git", "-c", "core.autocrlf=false", "-c", "core.quotepath=off"]
|
||||
for part in (["init", "-q"], ["config", "user.email", "selftest@example.invalid"],
|
||||
["config", "user.name", "selftest"], ["add", "-A"], ["commit", "-qm", "init"]):
|
||||
subprocess.run(git + part, cwd=root, check=True,
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
|
||||
def _st_run(root: pathlib.Path, *argv) -> tuple:
|
||||
"""跑**本脚本的一份副本**(REPO_ROOT 取自 __file__,副本必须落在临时仓里)。"""
|
||||
script = root / "scripts" / "rename_ref.py"
|
||||
script.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(pathlib.Path(__file__), script)
|
||||
r = subprocess.run([sys.executable, str(script)] + list(argv) + ["--no-verify"],
|
||||
cwd=root, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
return r.returncode, r.stdout.decode("utf-8", "replace")
|
||||
|
||||
|
||||
def selftest() -> int:
|
||||
"""在一次性临时 git 仓里复现并回归「整目录搬迁」的两个真 bug。
|
||||
|
||||
判据来自 2026-10-30 测试说明分层那次的实测事故(已写进 docs/文档规范.md §3.6
|
||||
第 4/5 条,那里同时记着事故当时是怎么补救的):
|
||||
B1 整目录搬迁会改写目录**内部**文件的内容——「相对引用文件所在目录」那条
|
||||
解析规则不检验目标存在,于是 `1/1 PASSED`、`13639/32768`、日期占位
|
||||
`[----/--/-- --:--:--]` 这类非路径斜杠片段被解析成「旧目录下的子路径」,
|
||||
再被插上目录名。台架证据是不可再生的原始记录,改脏了就没了。
|
||||
B2 目录内部文件的 `../` **出链**不补层级——emit_dir 取的是 new_abs.parent
|
||||
而不是「本文件搬进新目录后的那一级」,少算一层,图片与相对链接仍指旧层级。
|
||||
另外回归两件旧行为:单文件搬迁的 `../` 换算(B2 的改动不得把它带坏)、
|
||||
以及 glob 形态引用在**搬文件**时只提示不改写。
|
||||
"""
|
||||
fails = []
|
||||
|
||||
def check(name, cond, extra=""):
|
||||
print(f" {'ok ' if cond else 'FAIL'} {name}")
|
||||
if not cond:
|
||||
fails.append(f"{name} :: {extra}")
|
||||
|
||||
junk = "比率 1/1 PASSED 与 0/0 与 13639/32768\n时间戳 [----/--/-- --:--:--]\n"
|
||||
with tempfile.TemporaryDirectory(prefix="rr-selftest-") as td:
|
||||
# ── 案例 A:整目录搬迁(B1 + B2 + 入链 + glob)────────────────────
|
||||
root = pathlib.Path(td) / "a"
|
||||
_st_make_repo(root, {
|
||||
"docs/td/a.md": "# a\n" + junk
|
||||
+ "根相对 `docs/td/data/e1.txt`\n"
|
||||
+ "\n"
|
||||
+ "兄弟 [b](./b.md)\n",
|
||||
"docs/td/b.md": "# b\n\n",
|
||||
"docs/td/data/e1.txt": junk + "see ../a.md\n",
|
||||
"docs/img/x.svg": "<svg/>\n",
|
||||
"docs/other.md": "根相对 `docs/td/a.md` 与 [a](docs/td/a.md)\n"
|
||||
"目录引用 `docs/td`\n通配 `docs/td/*.md`\n",
|
||||
})
|
||||
# 目标**不带**尾斜杠:带斜杠的含义是「挪进该目录并保留名字」,
|
||||
# 那会得到 docs/moved/td/td(多套一层,断言的层级就不对了)
|
||||
rc, out = _st_run(root, "docs/td", "docs/moved/td", "--apply")
|
||||
check("A/rc==0", rc == 0, out[-500:])
|
||||
moved = root / "docs/moved" / "td"
|
||||
check("A/目录确实搬走了", moved.is_dir() and not (root / "docs/td").exists(), out[-300:])
|
||||
ev = (moved / "data" / "e1.txt").read_text(encoding="utf-8")
|
||||
check("A/B1 证据文件逐字节未变", ev == junk + "see ../a.md\n", repr(ev))
|
||||
a = (moved / "a.md").read_text(encoding="utf-8")
|
||||
check("A/B1 内部斜杠片段没被插前缀",
|
||||
"1/1 PASSED" in a and "0/0" in a and "13639/32768" in a
|
||||
and "[----/--/-- --:--:--]" in a and "td/1/1" not in a, repr(a))
|
||||
check("A/内部根相对引用已跟到新位置", "`docs/moved/td/data/e1.txt`" in a, repr(a))
|
||||
check("A/B2 出链 ../ 补了一层", "" in a, repr(a))
|
||||
b = (moved / "b.md").read_text(encoding="utf-8")
|
||||
check("A/B2 目录内每个文件都补", "" in b, repr(b))
|
||||
check("A/同目录相对链接原样保留(两侧一起搬)", "](./b.md)" in a and (moved / "b.md").is_file(), repr(a))
|
||||
o = (root / "docs" / "other.md").read_text(encoding="utf-8")
|
||||
check("A/别人文件的入链已改写",
|
||||
"`docs/moved/td/a.md`" in o and "(docs/moved/td/a.md)" in o
|
||||
and "`docs/moved/td`" in o, repr(o))
|
||||
check("A/搬目录时 glob 前缀跟着搬走", "`docs/moved/td/*.md`" in o, repr(o))
|
||||
check("A/搬目录时不该报 glob 提示", "通配符形态" not in out, out[-400:])
|
||||
|
||||
# ── 案例 B:单文件搬迁(旧行为不得被带坏 + glob 只提示)───────────
|
||||
root2 = pathlib.Path(td) / "b"
|
||||
_st_make_repo(root2, {
|
||||
"docs/x/solo.md": "# solo\n\n比率 3/4 PASSED\n",
|
||||
"docs/x/sib.md": "# sib 见 docs/x/solo.md\n",
|
||||
"docs/img/x.svg": "<svg/>\n",
|
||||
"docs/other.md": "见 `docs/x/solo.md` 与 glob `docs/x/*.md`\n",
|
||||
})
|
||||
rc2, out2 = _st_run(root2, "docs/x/solo.md", "docs/x/sub/", "--apply")
|
||||
check("B/rc==0", rc2 == 0, out2[-500:])
|
||||
solo_p = root2 / "docs" / "x" / "sub" / "solo.md"
|
||||
check("B/文件已搬进子目录", solo_p.is_file() and not (root2 / "docs/x/solo.md").exists(), out2[-300:])
|
||||
solo = solo_p.read_text(encoding="utf-8") if solo_p.is_file() else ""
|
||||
check("B/单文件搬迁的 ../ 换算照旧", "" in solo, repr(solo))
|
||||
check("B/单文件内部片段不被插前缀", "比率 3/4 PASSED" in solo, repr(solo))
|
||||
o2 = (root2 / "docs" / "other.md").read_text(encoding="utf-8")
|
||||
sib = (root2 / "docs" / "x" / "sib.md").read_text(encoding="utf-8")
|
||||
check("B/别人的入链已改写",
|
||||
"`docs/x/sub/solo.md`" in o2 and "docs/x/sub/solo.md" in sib, repr(o2) + repr(sib))
|
||||
check("B/搬文件时 glob 保持原样(不猜)", "`docs/x/*.md`" in o2, repr(o2))
|
||||
check("B/搬文件时 glob 被点名提示",
|
||||
"通配符形态" in out2 and "docs/x/*.md" in out2, out2[-500:])
|
||||
|
||||
print()
|
||||
if fails:
|
||||
print(f"SELFTEST FAIL:{len(fails)} 项")
|
||||
for f in fails:
|
||||
print(" - " + f)
|
||||
return 1
|
||||
print("SELFTEST PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if "--selftest" in sys.argv[1:]:
|
||||
sys.exit(selftest())
|
||||
sys.exit(main())
|
||||
|
||||
Reference in New Issue
Block a user