Files

96 lines
3.6 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""chongming 项目模板初始化。
用法:
python init.py <目标目录> [--name 项目名] [--app-name 应用名] [--no-sync]
行为:
1. 拒绝覆盖非空目录
2. 复制模板内容(不含 init.py 与模板自身 README;README.project.md 重命名为 README.md)
3. 替换占位符 {{project_name}}(发行名)/ {{app_name}}(应用显示名)
4. 默认在目标目录执行 uv sync(--no-sync 跳过)
示例:
python init.py ~/proj/my-gateway
python init.py ../demo --name demo --app-name "演示网关"
"""
from __future__ import annotations
import argparse
import shutil
import subprocess
import sys
from pathlib import Path
TEMPLATE_DIR = Path(__file__).resolve().parent
# 模板自身文件 / 生成物:不复制
SKIP_NAMES = {"init.py", "README.md", ".git", "__pycache__", ".venv", "dist", "build"}
# 做占位符替换的文本文件
TEXT_SUFFIXES = {".py", ".toml", ".md", ".json5", ".json", ".yaml", ".yml", ".sh", ".txt"}
TEXT_NAMES = {".gitignore", ".env.example"}
# 生成项目里该文件重命名为 README.md
PROJECT_README = "README.project.md"
def main() -> int:
parser = argparse.ArgumentParser(description="chongming 项目模板初始化")
parser.add_argument("target", help="目标项目目录(不存在则创建;必须为空)")
parser.add_argument("--name", help="项目名(pyproject 的 name,默认取目标目录名)")
parser.add_argument("--app-name", help="应用显示名(config.toml 的 app.name,默认同项目名)")
parser.add_argument("--no-sync", action="store_true", help="跳过 uv sync")
args = parser.parse_args()
target = Path(args.target).expanduser().resolve()
project_name = args.name or target.name
app_name = args.app_name or project_name
if target.exists() and any(target.iterdir()):
print(f"❌ 目标目录非空,拒绝覆盖: {target}")
return 1
replacements = {"{{project_name}}": project_name, "{{app_name}}": app_name}
copied = 0
for src in sorted(TEMPLATE_DIR.rglob("*")):
rel = src.relative_to(TEMPLATE_DIR)
if any(part in SKIP_NAMES for part in rel.parts):
continue
dst = target / ("README.md" if rel.as_posix() == PROJECT_README else rel)
if src.is_dir():
dst.mkdir(parents=True, exist_ok=True)
continue
dst.parent.mkdir(parents=True, exist_ok=True)
if src.suffix in TEXT_SUFFIXES or src.name in TEXT_NAMES:
text = src.read_text(encoding="utf-8")
for key, value in replacements.items():
text = text.replace(key, value)
dst.write_text(text, encoding="utf-8")
else:
shutil.copy2(src, dst)
copied += 1
print(f"✅ 已初始化: {target}")
print(f" 项目名={project_name} 应用名={app_name} 文件数={copied}")
if args.no_sync:
print("(--no-sync:跳过 uv sync)")
elif shutil.which("uv") is None:
print("⚠️ 未找到 uv,跳过依赖安装;请在目标目录自行执行 uv sync")
else:
print("→ uv sync(首次安装依赖,可能需要几分钟)")
if subprocess.call(["uv", "sync"], cwd=target) != 0:
print("⚠️ uv sync 未成功,请在目标目录手动重试")
print("\n下一步:")
print(f" cd {target}")
print(" uv run serve # 启动(http://localhost:8000/docs)")
print(" curl 'http://localhost:8000/api/v1/example/hello?name=world'")
print(" uv run gen_plugin <name> # 生成新插件骨架")
return 0
if __name__ == "__main__":
sys.exit(main())