mirror of
https://git.openapi.site/https://github.com/desirecore/market.git
synced 2026-06-06 05:50:41 +08:00
## 概述 / Summary 把 docx 技能对"客户端预装运行时依赖"的复用方式从 **bash 包装脚本**改为**跨平台 runtime 启动器**,实现 Win/macOS/Linux 一致、不依赖 Git Bash,并修复若干 POSIX 硬编码导致的 Windows 崩溃点。 Switch the docx skill's reuse of client-preinstalled runtime deps from a **bash wrapper** to **cross-platform runtime launchers**, so it behaves identically on Win/macOS/Linux without Git Bash, and fix several POSIX-hardcoded crashes on Windows. ## 改动 / Changes - **新增 / Add** `scripts/preload-deps.cjs`(Node 预加载,注入 `NODE_PATH`)与 `scripts/with-deps.py`(Python 启动器,按需切换到内置含 lxml 的 Python);**删除** bash 版 `with-deps.sh`。 - 生成走 `node -r preload-deps.cjs`,office 脚本走 `python with-deps.py` —— 离线复用预装的 docx-js / defusedxml / lxml,免每次 `npm`/`pip install`,且**不依赖 bash**。 - `comment.py` 补 defusedxml sys.path shim;`validate.py` 修临时目录泄漏(atexit 清理)。 - `accept_changes.py` 去除 `/tmp` 硬编码(`tempfile.gettempdir` + `Path.as_uri`);`soffice.py` 仅 Linux 启用 AF_UNIX shim,避免 Windows 崩溃。 - `SKILL.md` / `SKILL.zh-CN.md` 同步命令形式、加 ESM 警告与外部工具(pandoc/LibreOffice/poppler)跨平台安装指引,`source_hash` 重算。 ## 测试 / Testing - 真实 dev 根目录端到端:生成 docx(免安装)+ 完整 XSD 校验(含 lxml)+ unpack/pack 往返均通过。 - 仓库 `validate-i18n.py` 校验通过;全 py 脚本 `py_compile` + `preload-deps.cjs` `node --check` 通过。 --- - [x] 我已阅读并同意 CLA / I have read and agree to the CLA Co-authored-by: 张馨元 <zhangxy@iynss.com> Co-authored-by: Yige <a@wyr.me>
75 lines
3.1 KiB
Python
75 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
||
"""with-deps.py —— 跨平台 Python 启动器(无需 bash,对应已废弃的 with-deps.sh)
|
||
|
||
让 office 脚本复用客户端预装的共享依赖,免去运行时 pip install:
|
||
- defusedxml:纯 Python,注入 PYTHONPATH(<ROOT>/runtime-deps/python-libs)
|
||
- lxml:编译型扩展,绑定具体解释器 → 若存在受控 Python(<ROOT>/runtime-deps/
|
||
python-runtime,已装 lxml),用它运行目标脚本,从而离线启用完整 XSD 校验;
|
||
受控 Python 不存在 / 无法执行(如 macOS 公证拦截)→ 自动退回当前 Python
|
||
(此时 lxml 缺失,校验会优雅降级跳过,不会崩)。
|
||
|
||
纯 Python 实现,在 Windows / macOS / Linux 上用同一条命令运行,不依赖 bash:
|
||
|
||
python "<skill-dir>/scripts/with-deps.py" office/unpack.py document.docx unpacked/
|
||
python "<skill-dir>/scripts/with-deps.py" office/validate.py doc.docx
|
||
|
||
目标脚本以 [解释器, 目标, *参数] 直接拉起 —— 等价于 `python <目标>`,因此脚本目录
|
||
会被 Python 自动加入 sys.path、__name__ == "__main__"、argv 与直接运行完全一致。
|
||
"""
|
||
|
||
import os
|
||
import subprocess
|
||
import sys
|
||
|
||
_HERE = os.path.dirname(os.path.abspath(__file__)) # .../skills/docx/scripts
|
||
# scripts → docx → skills → <ROOT>
|
||
_ROOT = os.path.abspath(os.path.join(_HERE, "..", "..", ".."))
|
||
_DEPS = os.path.join(_ROOT, "runtime-deps")
|
||
_PYLIBS = os.path.join(_DEPS, "python-libs")
|
||
_BUNDLED = os.path.join(
|
||
_DEPS,
|
||
"python-runtime",
|
||
"python.exe" if os.name == "nt" else os.path.join("bin", "python3"),
|
||
)
|
||
|
||
|
||
def main() -> int:
|
||
if len(sys.argv) < 2:
|
||
sys.stderr.write("usage: with-deps.py <script.py> [args...]\n")
|
||
return 2
|
||
|
||
# 目标脚本相对 scripts/ 解析(如 office/validate.py),也支持绝对路径
|
||
arg = sys.argv[1]
|
||
target = arg if os.path.isabs(arg) else os.path.join(_HERE, arg)
|
||
if not os.path.isfile(target):
|
||
sys.stderr.write(f"with-deps.py: target not found: {target}\n")
|
||
return 2
|
||
|
||
# 选解释器:有受控 Python(含 lxml)且当前不是它 → 用它;否则用当前/系统 Python
|
||
interp = sys.executable
|
||
if os.path.isfile(_BUNDLED) and os.path.realpath(_BUNDLED) != os.path.realpath(sys.executable):
|
||
interp = _BUNDLED
|
||
|
||
# 注入 defusedxml(os.pathsep 跨平台自动 ';' / ':')
|
||
env = dict(os.environ)
|
||
if os.path.isdir(_PYLIBS):
|
||
existing = env.get("PYTHONPATH")
|
||
env["PYTHONPATH"] = _PYLIBS + (os.pathsep + existing if existing else "")
|
||
|
||
cmd = [interp, target, *sys.argv[2:]]
|
||
try:
|
||
rc = subprocess.run(cmd, env=env).returncode
|
||
except OSError:
|
||
rc = 126 # 受控 Python 无法启动
|
||
|
||
# 受控 Python 跑不了(rc<0=被信号杀,如 macOS Gatekeeper;126/127=无法执行)
|
||
# → 退回系统 Python,让脚本在缺 lxml 时优雅降级,而不是把整条命令判为失败
|
||
if interp != sys.executable and (rc < 0 or rc in (126, 127)):
|
||
rc = subprocess.run([sys.executable, target, *sys.argv[2:]], env=env).returncode
|
||
|
||
return rc
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|