Files
AstrBot/scripts/hatch_build.py

101 lines
3.7 KiB
Python

"""
Custom Hatchling build hook.
Only runs when the environment variable ASTRBOT_BUILD_DASHBOARD=1 is set,
so that `uv sync` / editable installs are never affected.
Usage:
ASTRBOT_BUILD_DASHBOARD=1 uv build
When enabled, this hook:
1. Runs `npm run build` inside the `dashboard/` directory.
2. Copies the resulting `dashboard/dist/` tree into
`astrbot/dashboard/dist/` so the static assets are shipped
inside the Python wheel.
"""
import os
import shutil
import subprocess
from logging import getLogger
from pathlib import Path
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
logger = getLogger(__name__)
class CustomBuildHook(BuildHookInterface):
PLUGIN_NAME = "custom"
def initialize(self, version: str, build_data: dict) -> None:
root = Path(self.root)
dashboard_src = root / "dashboard"
dist_src = dashboard_src / "dist"
dist_target = root / "astrbot" / "dashboard" / "dist"
if os.environ.get("ASTRBOT_BUILD_DASHBOARD", "").strip() != "1":
# Ensure the target directory and a placeholder exist so the wheel
# build does not fail when artifacts = ["..."] is declared.
# Handle the case where dist_target might be a broken symlink (e.g. in AUR builds)
if dist_target.is_symlink() or dist_target.is_file():
dist_target.unlink()
dist_target.mkdir(parents=True, exist_ok=True)
if not any(dist_target.iterdir()):
placeholder = dist_target / ".placeholder"
placeholder.write_text("# dashboard placeholder\n")
return
if not dashboard_src.exists():
logger.warning(
"[hatch_build] 'dashboard/' directory not found: skipping dashboard build."
)
if dist_target.is_symlink() or dist_target.is_file():
dist_target.unlink()
dist_target.mkdir(parents=True, exist_ok=True)
placeholder = dist_target / ".placeholder"
placeholder.write_text("# dashboard placeholder\n")
return
# ── Install Node dependencies if node_modules is absent ─────────────
if not (dashboard_src / "node_modules").exists():
logger.info("[hatch_build] Installing dashboard Node dependencies...")
subprocess.run(
["npm", "install"],
cwd=dashboard_src,
check=True,
)
# ── Build the Vue/Vite dashboard ──────────────────────────────────────
logger.info("[hatch_build] Building Vue dashboard (npm run build)...")
subprocess.run(
["npm", "run", "build"],
cwd=dashboard_src,
check=True,
)
if not dist_src.exists():
logger.warning(
"[hatch_build] dashboard/dist not found after build: skipping copy."
)
if dist_target.is_symlink() or dist_target.is_file():
dist_target.unlink()
dist_target.mkdir(parents=True, exist_ok=True)
placeholder = dist_target / ".placeholder"
placeholder.write_text("# dashboard placeholder\n")
return
# ── Copy into the Python package tree ───────────────────────────────
if dist_target.is_symlink() or dist_target.is_file():
dist_target.unlink()
elif dist_target.exists():
shutil.rmtree(dist_target)
shutil.copytree(dist_src, dist_target)
logger.info(
f"[hatch_build] Dashboard dist copied → {dist_target.relative_to(root)}"
)