151 lines
5.9 KiB
Python
151 lines
5.9 KiB
Python
#!/usr/bin/env python3
|
||
"""Check runtime capabilities at the beginning of the workflow."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import importlib.util
|
||
import shutil
|
||
import subprocess
|
||
from pathlib import Path
|
||
from typing import Any
|
||
|
||
from common import ensure_dir, write_json
|
||
|
||
|
||
def command_version(command: list[str]) -> tuple[bool, str]:
|
||
if not shutil.which(command[0]):
|
||
return False, "not found"
|
||
try:
|
||
completed = subprocess.run(command, text=True, capture_output=True, timeout=20)
|
||
output = (completed.stdout or completed.stderr).strip().splitlines()
|
||
return completed.returncode == 0, output[0] if output else "available"
|
||
except Exception as exc:
|
||
return False, str(exc)
|
||
|
||
|
||
def run_docx_env(skill_dir: Path) -> tuple[bool, str]:
|
||
env_script = skill_dir / "vendor/docx-toolkit/scripts/env_check.sh"
|
||
if not env_script.exists():
|
||
return False, "vendor/docx-toolkit/scripts/env_check.sh not found"
|
||
try:
|
||
completed = subprocess.run(["bash", str(env_script)], text=True, capture_output=True, timeout=40)
|
||
return completed.returncode == 0, (completed.stdout + completed.stderr).strip()
|
||
except Exception as exc:
|
||
return False, str(exc)
|
||
|
||
|
||
def module_available(name: str) -> bool:
|
||
return importlib.util.find_spec(name) is not None
|
||
|
||
|
||
def check_environment(skill_dir: Path) -> dict[str, Any]:
|
||
python_docx = module_available("docx")
|
||
pandoc_ok, pandoc_version = command_version(["pandoc", "--version"])
|
||
dotnet_ok, dotnet_version = command_version(["dotnet", "--version"])
|
||
docx_ready, docx_output = run_docx_env(skill_dir)
|
||
|
||
final_docx_mode = "docx-openxml" if docx_ready else ("python-docx" if python_docx else "basic-ooxml")
|
||
requires_user_input = not docx_ready
|
||
next_action = (
|
||
"请选择:1) 安装完整 DOCX 环境;2) 使用基础 DOCX 兜底继续。回复选择后再进入项目分析。"
|
||
if requires_user_input
|
||
else "完整 DOCX 环境可用,可以进入项目分析。"
|
||
)
|
||
return {
|
||
"output_directory": "当前目录/软件著作权申请资料",
|
||
"capabilities": {
|
||
"markdown_drafts": True,
|
||
"application_txt": True,
|
||
"basic_docx": python_docx or True,
|
||
"python_docx": python_docx,
|
||
"pandoc_preview": pandoc_ok,
|
||
"docx_openxml_full": docx_ready,
|
||
"dotnet_sdk": dotnet_ok,
|
||
},
|
||
"versions": {
|
||
"pandoc": pandoc_version,
|
||
"dotnet": dotnet_version,
|
||
},
|
||
"final_docx_mode": final_docx_mode,
|
||
"recommendation": (
|
||
"完整 DOCX OpenXML 环境已就绪,建议使用完整 Word 生成和校验流程。"
|
||
if docx_ready
|
||
else "完整 DOCX OpenXML 环境未就绪。可以继续使用兜底 DOCX 生成;如需更规范的 Word 结构和校验,请先安装 .NET SDK 并运行 vendor/docx-toolkit/scripts/setup.sh。"
|
||
),
|
||
"install_prompt": (
|
||
"是否安装完整 DOCX 环境?安装后文档生成和校验更规范;不安装也可以继续生成 Markdown、TXT 和基础 DOCX。"
|
||
if not docx_ready
|
||
else "无需安装,完整环境可用。"
|
||
),
|
||
"requires_user_input": requires_user_input,
|
||
"confirmation_stage": "environment" if requires_user_input else None,
|
||
"next_action": next_action,
|
||
"docx_env_output": docx_output,
|
||
}
|
||
|
||
|
||
def write_markdown(path: Path, data: dict[str, Any]) -> None:
|
||
caps = data["capabilities"]
|
||
lines = [
|
||
"# 软著申请资料生成环境检查",
|
||
"",
|
||
f"- 输出目录:`{data['output_directory']}`",
|
||
f"- 最终 Word 模式:`{data['final_docx_mode']}`",
|
||
"",
|
||
"## 能力状态",
|
||
"",
|
||
f"- Markdown 草稿:{'可用' if caps['markdown_drafts'] else '不可用'}",
|
||
f"- 申请表 TXT:{'可用' if caps['application_txt'] else '不可用'}",
|
||
f"- 基础 DOCX 生成:{'可用' if caps['basic_docx'] else '不可用'}",
|
||
f"- python-docx:{'可用' if caps['python_docx'] else '不可用'}",
|
||
f"- pandoc 预览:{'可用' if caps['pandoc_preview'] else '不可用'}({data['versions']['pandoc']})",
|
||
f"- .NET SDK:{'可用' if caps['dotnet_sdk'] else '不可用'}({data['versions']['dotnet']})",
|
||
f"- DOCX OpenXML 完整环境:{'可用' if caps['docx_openxml_full'] else '不可用'}",
|
||
"",
|
||
"## 建议",
|
||
"",
|
||
data["recommendation"],
|
||
"",
|
||
"## 用户选择",
|
||
"",
|
||
data["install_prompt"],
|
||
"",
|
||
"如果完整 DOCX 环境不可用,必须先等待用户选择,并记录 `environment` 门禁后再继续。",
|
||
"",
|
||
"```text" if data.get("requires_user_input") else "",
|
||
"STOP_FOR_USER" if data.get("requires_user_input") else "",
|
||
f"NEXT_ACTION: {data['next_action']}" if data.get("requires_user_input") else "",
|
||
"```" if data.get("requires_user_input") else "",
|
||
"",
|
||
"## DOCX 环境输出摘要",
|
||
"",
|
||
"```text",
|
||
"\n".join(data["docx_env_output"].splitlines()[:40]),
|
||
"```",
|
||
"",
|
||
]
|
||
path.write_text("\n".join(lines), encoding="utf-8")
|
||
|
||
|
||
def main() -> None:
|
||
parser = argparse.ArgumentParser()
|
||
parser.add_argument("--out-dir", default="软件著作权申请资料")
|
||
args = parser.parse_args()
|
||
|
||
skill_dir = Path(__file__).resolve().parents[1]
|
||
out_dir = ensure_dir(Path(args.out_dir))
|
||
data = check_environment(skill_dir)
|
||
write_json(out_dir / "环境检查.json", data)
|
||
write_markdown(out_dir / "环境检查.md", data)
|
||
print(f"OK environment check: {out_dir}")
|
||
print(f"Final DOCX mode: {data['final_docx_mode']}")
|
||
print(data["recommendation"])
|
||
if data.get("requires_user_input"):
|
||
print("STOP_FOR_USER")
|
||
print(f"NEXT_ACTION: {data['next_action']}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|