mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
Add prebuilt frontend fetch script. Closes #110.
This commit is contained in:
@@ -41,8 +41,6 @@ If you plan to contribute, read [CONTRIBUTING.md](CONTRIBUTING.md).
|
|||||||
- [UV](https://astral.sh/uv) package manager: `curl -LsSf https://astral.sh/uv/install.sh | sh`
|
- [UV](https://astral.sh/uv) package manager: `curl -LsSf https://astral.sh/uv/install.sh | sh`
|
||||||
- MeshCore radio connected via USB serial, TCP, or BLE
|
- MeshCore radio connected via USB serial, TCP, or BLE
|
||||||
|
|
||||||
If you are on a low-resource system and do not want to build the frontend locally, download the release zip named `remoteterm-prebuilt-frontend-vX.X.X-<short hash>.zip`. That bundle includes `frontend/prebuilt`, so you can run the app without doing a frontend build from source.
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Finding your serial port</summary>
|
<summary>Finding your serial port</summary>
|
||||||
|
|
||||||
@@ -111,6 +109,8 @@ uv run uvicorn app.main:app --host 0.0.0.0 --port 8000
|
|||||||
|
|
||||||
The release bundle includes `frontend/prebuilt`, so it does not require a local frontend build.
|
The release bundle includes `frontend/prebuilt`, so it does not require a local frontend build.
|
||||||
|
|
||||||
|
Alternatively, if you have already cloned the repo, you can fetch just the prebuilt frontend into your working tree without downloading the full release zip via `python3 scripts/fetch_prebuilt_frontend.py`.
|
||||||
|
|
||||||
## Path 2: Docker
|
## Path 2: Docker
|
||||||
|
|
||||||
> **Warning:** Docker has had reports intermittent issues with serial event subscriptions. The native method above is more reliable.
|
> **Warning:** Docker has had reports intermittent issues with serial event subscriptions. The native method above is more reliable.
|
||||||
|
|||||||
106
scripts/fetch_prebuilt_frontend.py
Executable file
106
scripts/fetch_prebuilt_frontend.py
Executable file
@@ -0,0 +1,106 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
fetch_prebuilt_frontend.py
|
||||||
|
|
||||||
|
Downloads the latest prebuilt frontend artifact from the GitHub releases page
|
||||||
|
and installs it into frontend/prebuilt/ so the backend can serve it directly.
|
||||||
|
|
||||||
|
No GitHub CLI or authentication required — uses only the public releases API
|
||||||
|
and browser_download_url. Requires only the Python standard library.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import urllib.request
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO = "jkingsman/Remote-Terminal-for-MeshCore"
|
||||||
|
API_URL = f"https://api.github.com/repos/{REPO}/releases/latest"
|
||||||
|
PREBUILT_PREFIX = "Remote-Terminal-for-MeshCore/frontend/prebuilt/"
|
||||||
|
|
||||||
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||||
|
PREBUILT_DIR = SCRIPT_DIR.parent / "frontend" / "prebuilt"
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_json(url: str) -> dict:
|
||||||
|
req = urllib.request.Request(url, headers={"Accept": "application/vnd.github+json"})
|
||||||
|
with urllib.request.urlopen(req) as resp:
|
||||||
|
return json.loads(resp.read())
|
||||||
|
|
||||||
|
|
||||||
|
def find_prebuilt_asset(release: dict) -> tuple[str, str, str]:
|
||||||
|
"""Return (tag_name, asset_name, download_url) for the prebuilt zip."""
|
||||||
|
tag = release.get("tag_name", "")
|
||||||
|
for asset in release.get("assets", []):
|
||||||
|
name = asset.get("name", "")
|
||||||
|
if name.startswith("remoteterm-prebuilt-frontend-") and name.endswith(".zip"):
|
||||||
|
return tag, name, asset["browser_download_url"]
|
||||||
|
raise SystemExit(
|
||||||
|
f"No prebuilt frontend artifact found in the latest release.\n"
|
||||||
|
f"Check https://github.com/{REPO}/releases for available assets."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def download(url: str, dest: Path) -> None:
|
||||||
|
with urllib.request.urlopen(url) as resp, open(dest, "wb") as f:
|
||||||
|
shutil.copyfileobj(resp, f)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_prebuilt(zip_path: Path, dest: Path) -> int:
|
||||||
|
with zipfile.ZipFile(zip_path) as zf:
|
||||||
|
members = [m for m in zf.namelist() if m.startswith(PREBUILT_PREFIX)]
|
||||||
|
if not members:
|
||||||
|
raise SystemExit(f"'{PREBUILT_PREFIX}' not found inside zip.")
|
||||||
|
|
||||||
|
if dest.exists():
|
||||||
|
shutil.rmtree(dest)
|
||||||
|
dest.mkdir(parents=True)
|
||||||
|
|
||||||
|
for member in members:
|
||||||
|
rel = member[len(PREBUILT_PREFIX):]
|
||||||
|
if not rel:
|
||||||
|
continue
|
||||||
|
target = dest / rel
|
||||||
|
if member.endswith("/"):
|
||||||
|
target.mkdir(parents=True, exist_ok=True)
|
||||||
|
else:
|
||||||
|
target.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with zf.open(member) as src, open(target, "wb") as dst:
|
||||||
|
shutil.copyfileobj(src, dst)
|
||||||
|
|
||||||
|
return len(members)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
print("Fetching latest release info...")
|
||||||
|
release = fetch_json(API_URL)
|
||||||
|
tag, asset_name, download_url = find_prebuilt_asset(release)
|
||||||
|
print(f" Release : {tag}")
|
||||||
|
print(f" Asset : {asset_name}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
zip_path = PREBUILT_DIR.parent / asset_name
|
||||||
|
try:
|
||||||
|
print(f"Downloading {asset_name}...")
|
||||||
|
download(download_url, zip_path)
|
||||||
|
|
||||||
|
print("Extracting prebuilt frontend...")
|
||||||
|
count = extract_prebuilt(zip_path, PREBUILT_DIR)
|
||||||
|
print(f"Extracted {count} entries.")
|
||||||
|
finally:
|
||||||
|
zip_path.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
print()
|
||||||
|
print(f"Done! Prebuilt frontend ({tag}) installed to frontend/prebuilt/")
|
||||||
|
print("Start the server with:")
|
||||||
|
print(" uv run uvicorn app.main:app --host 0.0.0.0 --port 8000")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nAborted.", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
Reference in New Issue
Block a user