From 8cfcc25f984172530350fdf6010252831d3b41c8 Mon Sep 17 00:00:00 2001 From: csh Date: Fri, 23 Jan 2026 14:24:07 +0800 Subject: [PATCH] :sparkles: feat(cli): parse toml config and dispatch actions --- README.md | 6 +++++ scripts/playbook.py | 40 ++++++++++++++++++++++++++++++++++ tests/cli/test_playbook_cli.py | 20 +++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/README.md b/README.md index 4b889f5..bdde171 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,12 @@ Playbook:TSL(`.tsl`/`.tsf`)+ C++ + Python + Markdown(代码格式化) ### 快速部署 +统一入口(配置驱动,示例见 `playbook.toml.example`): + +```bash +python scripts/playbook.py -config playbook.toml +``` + 使用 `sync_templates` 脚本一键部署项目架构: ```bash diff --git a/scripts/playbook.py b/scripts/playbook.py index 2820dc3..6984d61 100644 --- a/scripts/playbook.py +++ b/scripts/playbook.py @@ -1,11 +1,26 @@ #!/usr/bin/env python3 import sys +from pathlib import Path + +import tomllib + +ORDER = ["vendor", "sync_templates", "sync_standards", "install_skills", "format_md"] def usage() -> str: return "Usage:\n python scripts/playbook.py -config \n python scripts/playbook.py -h" +def load_config(path: Path) -> dict: + return tomllib.loads(path.read_text(encoding="utf-8")) + + +def run_action(name: str, config: dict, context: dict) -> int: + _ = config, context + print(f"[action] {name}") + return 0 + + def main(argv: list[str]) -> int: if "-h" in argv or "-help" in argv: print(usage()) @@ -13,6 +28,31 @@ def main(argv: list[str]) -> int: if "-config" not in argv: print("ERROR: -config is required.\n" + usage(), file=sys.stderr) return 2 + idx = argv.index("-config") + if idx + 1 >= len(argv) or not argv[idx + 1]: + print("ERROR: -config requires a path.\n" + usage(), file=sys.stderr) + return 2 + + config_path = Path(argv[idx + 1]).expanduser() + if not config_path.is_file(): + print(f"ERROR: config not found: {config_path}", file=sys.stderr) + return 2 + + config = load_config(config_path) + playbook_config = config.get("playbook", {}) + project_root = playbook_config.get("project_root") + if project_root: + root = Path(project_root).expanduser() + else: + root = config_path.parent + context = {"project_root": root.resolve(), "config_path": config_path.resolve()} + + for name in ORDER: + if name in config: + result = run_action(name, config[name], context) + if result != 0: + return result + return 0 diff --git a/tests/cli/test_playbook_cli.py b/tests/cli/test_playbook_cli.py index 970cf28..3ad0878 100644 --- a/tests/cli/test_playbook_cli.py +++ b/tests/cli/test_playbook_cli.py @@ -1,5 +1,6 @@ import subprocess import sys +import tempfile import unittest from pathlib import Path @@ -26,6 +27,25 @@ class PlaybookCliTests(unittest.TestCase): self.assertNotEqual(result.returncode, 0) self.assertIn("-config", result.stdout + result.stderr) + def test_action_order(self): + config_body = """ +[playbook] +project_root = "." + +[format_md] + +[sync_standards] +langs = ["tsl"] +""" + with tempfile.TemporaryDirectory() as tmp_dir: + config_path = Path(tmp_dir) / "playbook.toml" + config_path.write_text(config_body, encoding="utf-8") + result = run_cli("-config", str(config_path)) + + self.assertEqual(result.returncode, 0) + output = result.stdout + result.stderr + self.assertIn("sync_standards", output) + self.assertIn("format_md", output) if __name__ == "__main__": unittest.main()