Files
overub/core/cli.py
2025-12-21 17:12:32 +01:00

168 lines
5.8 KiB
Python

import argparse
import subprocess
import shutil
from pathlib import Path
from core.logger import setup_logging
def create_plugin(args: argparse.Namespace) -> None:
root = Path(args.root).resolve()
plugin_path = root / "plugins" / "external" / args.name
plugin_path.mkdir(parents=True, exist_ok=True)
(plugin_path / "__init__.py").write_text(
"from .plugin import *\n",
encoding="utf-8",
)
(plugin_path / "plugin.py").write_text(
f"from core.plugin import Plugin\n\n\nclass {args.class_name}(Plugin):\n name = \"{args.name}\"\n version = \"0.1.0\"\n author = \"{args.author}\"\n description = \"{args.description}\"\n\n async def on_load(self):\n self.log.info(\"{args.name} loaded\")\n",
encoding="utf-8",
)
(plugin_path / "config.yml").write_text(
f"{args.name}:\n enabled: true\n settings: {{}}\n",
encoding="utf-8",
)
(plugin_path / "requirements.txt").write_text("", encoding="utf-8")
(plugin_path / "README.md").write_text(
f"# {args.name}\n\n{args.description}\n",
encoding="utf-8",
)
print(f"Created plugin at {plugin_path}")
def validate_plugin(args: argparse.Namespace) -> None:
path = Path(args.path).resolve()
required = ["__init__.py", "plugin.py"]
missing = [item for item in required if not (path / item).exists()]
if missing:
print(f"Missing files: {', '.join(missing)}")
raise SystemExit(1)
print("Plugin structure OK")
def build_plugin(args: argparse.Namespace) -> None:
path = Path(args.path).resolve()
output = Path(args.output).resolve()
shutil.make_archive(str(output), "zip", root_dir=path)
print(f"Built {output}.zip")
def publish_plugin(args: argparse.Namespace) -> None:
root = Path(args.root).resolve()
if not args.name or not args.version:
print("Publish requires --name and --version")
raise SystemExit(1)
plugin_path = Path(args.path).resolve() if args.path else root / "plugins" / "external" / args.name
if not plugin_path.exists():
print(f"Plugin path not found: {plugin_path}")
raise SystemExit(1)
tag = args.version if args.version.startswith("v") else f"v{args.version}"
try:
subprocess.run(["git", "status"], cwd=plugin_path, check=True, capture_output=True, text=True)
except subprocess.CalledProcessError as exc:
print(exc.stderr.strip() or "Git status failed")
raise SystemExit(1) from exc
try:
subprocess.run(["git", "tag", "-a", tag, "-m", f"Release {tag}"], cwd=plugin_path, check=True)
subprocess.run(["git", "push", "origin", tag], cwd=plugin_path, check=True)
except subprocess.CalledProcessError as exc:
print(exc.stderr.strip() or "Git tag/push failed")
raise SystemExit(1) from exc
try:
log = subprocess.run(
["git", "log", "-5", "--pretty=format:- %s"],
cwd=plugin_path,
check=True,
capture_output=True,
text=True,
)
note = log.stdout.strip() or f"Release {tag}"
subprocess.run(
[
"tea",
"releases",
"create",
"--tag",
tag,
"--title",
f"{args.name} {tag}",
"--note",
note,
],
cwd=plugin_path,
check=True,
)
except subprocess.CalledProcessError as exc:
print(exc.stderr.strip() or "Tea release creation failed")
raise SystemExit(1) from exc
print(f"Published {args.name} {tag}")
def test_plugin(args: argparse.Namespace) -> None:
import py_compile
path = Path(args.path).resolve()
errors = []
for file in path.rglob("*.py"):
try:
py_compile.compile(str(file), doraise=True)
except Exception as exc:
errors.append((file, exc))
if errors:
for file, exc in errors:
print(f"{file}: {exc}")
raise SystemExit(1)
print("Plugin tests OK")
def generate_docs(args: argparse.Namespace) -> None:
path = Path(args.path).resolve()
(path / "README.md").write_text("# Plugin\n", encoding="utf-8")
(path / "COMMANDS.md").write_text("# Commands\n", encoding="utf-8")
(path / "CONFIG.md").write_text("# Config\n", encoding="utf-8")
(path / "API.md").write_text("# API\n", encoding="utf-8")
print("Docs generated")
def run_cli() -> None:
parser = argparse.ArgumentParser(prog="overub")
parser.add_argument("--root", default=Path(__file__).resolve().parents[1])
sub = parser.add_subparsers(dest="command")
create = sub.add_parser("create-plugin")
create.add_argument("name")
create.add_argument("--class-name", default="MyPlugin")
create.add_argument("--author", default="overub")
create.add_argument("--description", default="OverUB plugin")
create.set_defaults(func=create_plugin)
validate = sub.add_parser("validate-plugin")
validate.add_argument("path")
validate.set_defaults(func=validate_plugin)
build = sub.add_parser("build-plugin")
build.add_argument("path")
build.add_argument("--output", default="overub-plugin")
build.set_defaults(func=build_plugin)
test = sub.add_parser("test-plugin")
test.add_argument("path")
test.set_defaults(func=test_plugin)
docs = sub.add_parser("docs-plugin")
docs.add_argument("path")
docs.set_defaults(func=generate_docs)
publish = sub.add_parser("publish-plugin")
publish.add_argument("--name", default="")
publish.add_argument("--version", default="")
publish.add_argument("--path", default="")
publish.set_defaults(func=publish_plugin)
args = parser.parse_args()
setup_logging("INFO")
if not hasattr(args, "func"):
parser.print_help()
return
args.func(args)