100 lines
3.2 KiB
Python
100 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
mc 笔记库结构检查 — 2026-09-10 建(全面整理后防再犯)
|
||
|
||
检测 5 类问题:
|
||
1. 编码变体目录(同一目录被不同编码创建,如 织忆同步/ZhiyiSync/zhiyi-sync)
|
||
2. 非笔记混入(项目特征文件:Dockerfile/go.mod/package.json...)
|
||
3. 凭证泄露(文件名含 sk-/tskey-/nvapi-/ghp_)
|
||
4. 根目录散落文件
|
||
5. 空目录
|
||
|
||
用法: python3 mc-structure-check.py (有问题 exit 1,干净 exit 0)
|
||
"""
|
||
import os
|
||
import re
|
||
import sys
|
||
import urllib.parse
|
||
from collections import defaultdict
|
||
|
||
MC = os.path.expanduser('~/mc')
|
||
EXCL = ('/.git/', '.obsidian', '.smart-env', 'node_modules', '/.trash/', '/.openclaw-wiki/', '/_views/')
|
||
issues = []
|
||
|
||
|
||
def norm(n: str) -> str:
|
||
"""把编码变体还原为统一形式(URL 编码 / unicode 转义 / latin1 误读)"""
|
||
x = n
|
||
if '%' in x:
|
||
try:
|
||
x = urllib.parse.unquote(x)
|
||
except Exception:
|
||
pass
|
||
if '\\u' in x:
|
||
try:
|
||
x = x.encode('utf-8').decode('unicode_escape')
|
||
except Exception:
|
||
pass
|
||
try:
|
||
y = x.encode('latin1').decode('utf-8')
|
||
if y != x:
|
||
x = y
|
||
except Exception:
|
||
pass
|
||
return x
|
||
|
||
|
||
# 1. 编码变体
|
||
names = defaultdict(set)
|
||
for root, dirs, files in os.walk(MC):
|
||
if any(e in root for e in EXCL):
|
||
continue
|
||
for d in dirs:
|
||
names[(root, norm(d))].add(d)
|
||
for (root, n), vals in names.items():
|
||
if len(vals) > 1:
|
||
issues.append(f"编码变体: {root.replace(MC + '/', '')} → {sorted(vals)}")
|
||
|
||
# 2. 非笔记混入(项目特征文件)
|
||
PROJ = {'Dockerfile', 'Makefile', 'go.mod', 'Cargo.toml', 'package.json',
|
||
'requirements.txt', 'pyproject.toml', 'pom.xml', 'docker-compose.yml'}
|
||
for root, dirs, files in os.walk(MC):
|
||
if any(e in root for e in EXCL):
|
||
continue
|
||
hit = PROJ & set(files)
|
||
if hit:
|
||
issues.append(f"疑似项目: {root.replace(MC + '/', '')} ({', '.join(sorted(hit))})")
|
||
|
||
# 3. 凭证泄露(文件名)
|
||
for root, dirs, files in os.walk(MC):
|
||
if any(e in root for e in EXCL):
|
||
continue
|
||
for f in files:
|
||
if re.search(r'(sk-[A-Za-z0-9]{20,}|tskey-|nvapi-|ghp_|github_pat_)', f):
|
||
issues.append(f"文件名含凭证: {os.path.join(root, f).replace(MC + '/', '')}")
|
||
|
||
# 4. 根目录散落
|
||
ALLOW_ROOT = {'AGENTS.md', 'WIKI.md', 'index.md', 'inbox.md', 'tracked_files.json', 'SOUL.md'}
|
||
for f in os.listdir(MC):
|
||
p = os.path.join(MC, f)
|
||
if os.path.isfile(p) and not f.startswith('.') and f not in ALLOW_ROOT:
|
||
issues.append(f"根目录散落: {f}")
|
||
|
||
# 5. 空目录
|
||
for root, dirs, files in os.walk(MC):
|
||
if any(e in root for e in EXCL):
|
||
continue
|
||
if not os.listdir(root) and root != MC:
|
||
issues.append(f"空目录: {root.replace(MC + '/', '')}")
|
||
|
||
if issues:
|
||
print(f"⚠️ mc 结构检查: {len(issues)} 个问题")
|
||
for i in issues[:30]:
|
||
print(f" • {i}")
|
||
if len(issues) > 30:
|
||
print(f" ... 还有 {len(issues) - 30} 个")
|
||
sys.exit(1)
|
||
# 干净时静默(cron watchdog 模式:无输出=不打扰)
|
||
if '--verbose' in sys.argv:
|
||
print("✅ mc 结构检查通过(无编码变体 / 项目混入 / 凭证泄露 / 散落 / 空目录)")
|