- hatch_build.py: custom hatchling build hook that compiles the React frontend (npm ci + build) when frontend/dist is missing or stale, so 'uv tool install git+https://gitea.zephyre.one/Pakobbix/nvcurve.git' works as a single command - install.sh: curl|bash installer (checks prerequisites, auto-installs uv, clones and installs) - Makefile: dev targets (frontend, install, dev, test, clean) - README/docs: document the one-liner, clone, and direct-git installs
69 lines
2.5 KiB
Python
69 lines
2.5 KiB
Python
"""Custom hatchling build hook: build the React frontend if it is missing or stale.
|
|
|
|
This makes NVCurve installable with a single command, e.g.::
|
|
|
|
uv tool install "git+https://gitea.zephyre.one/Pakobbix/nvcurve.git"
|
|
|
|
The hook runs inside the isolated build environment right before the wheel
|
|
(or sdist) is assembled. If ``frontend/dist`` does not exist yet — or is older
|
|
than the frontend sources — it compiles the frontend using the host's ``npm``
|
|
(PATH is inherited from the environment).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
|
|
|
# Frontend inputs that must be newer than dist/index.html to trigger a rebuild.
|
|
_FRONTEND_INPUTS = ("src", "index.html", "vite.config.ts", "package.json", "tsconfig.json")
|
|
|
|
|
|
class FrontendBuildHook(BuildHookInterface):
|
|
"""Build ``frontend/dist`` with npm when it is missing or stale."""
|
|
|
|
PLUGIN_NAME = "custom"
|
|
|
|
def initialize(self, version: str, build_data: dict) -> None:
|
|
frontend = os.path.join(self.root, "frontend")
|
|
dist_index = os.path.join(frontend, "dist", "index.html")
|
|
|
|
if not self._needs_build(frontend, dist_index):
|
|
return
|
|
|
|
npm = shutil.which("npm")
|
|
if npm is None:
|
|
raise RuntimeError(
|
|
"npm not found on PATH. Node.js 18+ and npm are required to build "
|
|
"the NVCurve frontend. Install them and retry, or use install.sh "
|
|
"which checks prerequisites for you."
|
|
)
|
|
|
|
print(
|
|
"[nvcurve] frontend/dist missing or stale — building frontend with npm ...",
|
|
file=sys.stderr,
|
|
)
|
|
subprocess.run([npm, "ci", "--no-audit", "--no-fund"], cwd=frontend, check=True)
|
|
subprocess.run([npm, "run", "build"], cwd=frontend, check=True)
|
|
|
|
@staticmethod
|
|
def _needs_build(frontend: str, dist_index: str) -> bool:
|
|
if not os.path.isfile(dist_index):
|
|
return True
|
|
dist_mtime = os.path.getmtime(dist_index)
|
|
for name in _FRONTEND_INPUTS:
|
|
path = os.path.join(frontend, name)
|
|
if os.path.isfile(path):
|
|
if os.path.getmtime(path) > dist_mtime:
|
|
return True
|
|
elif os.path.isdir(path):
|
|
for root, _dirs, files in os.walk(path):
|
|
for file in files:
|
|
if os.path.getmtime(os.path.join(root, file)) > dist_mtime:
|
|
return True
|
|
return False
|