diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 778429700..82d569a42 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -41,10 +41,9 @@ jobs: - name: Checkout code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - lfs: true persist-credentials: false - # dist is shipped in Python installations, so it must match the source in every PR. + # Installing LeLab builds the frontend, so every PR must build. - name: Set up Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: @@ -52,17 +51,12 @@ jobs: cache: npm cache-dependency-path: frontend/package-lock.json - - name: Verify frontend tests and committed bundle + - name: Verify frontend tests and build working-directory: frontend run: | npm ci npm test npm run build - if [[ -n "$(git status --porcelain -- dist)" ]]; then - git diff --stat -- dist - echo "::error::frontend/dist is stale. Run 'cd frontend && npm ci && npm run build' and commit frontend/dist in this PR." - exit 1 - fi - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 diff --git a/.github/workflows/sync_space.yml b/.github/workflows/sync_space.yml index fd595f3c3..d1ae4e772 100644 --- a/.github/workflows/sync_space.yml +++ b/.github/workflows/sync_space.yml @@ -4,7 +4,6 @@ on: branches: [main] paths: - 'frontend/**' - - '!frontend/dist/**' - '.github/workflows/sync_space.yml' workflow_dispatch: @@ -16,16 +15,14 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - lfs: true persist-credentials: false - - name: Push frontend/ (without dist/) to Hugging Face Space + - name: Push frontend/ to Hugging Face Space env: HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | cd frontend - # The Space builds its own bundle via Docker; dist/ would be - # rejected by HF's pre-receive hook for containing raw binaries. - rm -rf dist + # HF's pre-receive hook rejects raw binaries, so the meshes go back to LFS. + echo 'public/so-101-urdf/meshes/*.stl filter=lfs diff=lfs merge=lfs -text' >> .gitattributes git init -b main git config user.email "ci@huggingface.co" git config user.name "GitHub CI" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 212ac7922..47ffb1708 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -54,7 +54,6 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - lfs: true # lerobot is a transitive dep that needs these native libs at install / # import time. List taken from LeRobot's fast_tests.yml. diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 728efd54c..403656088 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,8 +15,6 @@ default_language_version: python: python3.12 -exclude: '^frontend/dist/' - repos: ##### Meta ##### - repo: meta diff --git a/CLAUDE.md b/CLAUDE.md index 8d7b0c204..bd756681b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co LeLab is a FastAPI + React web interface wrapping the [LeRobot](https://github.com/huggingface/lerobot) framework for the SO-101 leader/follower arm. It exposes teleoperation, dataset recording, calibration, replay, and training as HTTP/WebSocket endpoints, replacing LeRobot's CLI + keyboard-driven flows. -The frontend (React + Vite) lives in [`frontend/`](frontend/). The built bundle in [`frontend/dist/`](frontend/dist/) is committed and shipped inside the Python wheel as package data (`frontend.__init__.py` makes setuptools treat it as a package); [`lelab/server.py`](lelab/server.py) mounts it as `StaticFiles` at `/` so a single `lelab` process serves both API and UI on `:8000`. The `frontend/` directory is also force-pushed to the [LeLab HF Space](https://huggingface.co/spaces/lerobot/LeLab) by [`.github/workflows/sync_space.yml`](.github/workflows/sync_space.yml) — that's a separate runtime; the Space builds the [Dockerfile](frontend/Dockerfile) (which runs `npm run build` again, so committed `dist/` doesn't matter to it). +The frontend (React + Vite) lives in [`frontend/`](frontend/). The bundle in `frontend/dist/` is not committed: [setup.py](setup.py) builds it while building the wheel, with Node from the `nodejs-wheel-binaries` build requirement, and ships it as package data (`frontend.__init__.py` makes setuptools treat it as a package); [`lelab/server.py`](lelab/server.py) mounts it as `StaticFiles` at `/` so a single `lelab` process serves both API and UI on `:8000`. The `frontend/` directory is also force-pushed to the [LeLab HF Space](https://huggingface.co/spaces/lerobot/LeLab) by [`.github/workflows/sync_space.yml`](.github/workflows/sync_space.yml) — that's a separate runtime; the Space builds the [Dockerfile](frontend/Dockerfile) (which runs `npm run build` itself). ## Common commands @@ -25,7 +25,7 @@ lelab # uvicorn on :8000, serves built frontend at /, opens browser lelab --dev # spawns Vite dev (:8080) + uvicorn --reload (:8000), opens browser to :8080 ``` -Frontend PRs must include the rebuilt `frontend/dist/` bundle: use Node.js 22 and run `cd frontend && npm ci && npm run build`, then commit `dist/` alongside the source changes. The required [Quality workflow](.github/workflows/quality.yml) rebuilds and rejects stale bundles before merge. `lelab --dev` serves directly from Vite, so development itself needs no rebuild. +Editable installs skip the frontend build: `lelab --dev` serves directly from Vite, and `lelab --rebuild` builds `frontend/dist/` with your local Node.js 22 when you want to try the production bundle. The SO-101 meshes in `frontend/public/` are kept out of LFS because installing from git does not fetch LFS files. Run the Python tests with `pytest` (config in [pyproject.toml](pyproject.toml); install dev deps via `pip install -e ".[test]"`). Tests live in [tests/](tests/) and cover request schemas, pure helpers, and idle/mutex branches of the feature handlers — subprocess/thread happy paths and HF Jobs integration are deliberately not unit-tested. Lint with `ruff check` / `ruff format` (config in [pyproject.toml](pyproject.toml)). There is no Python build step; for end-to-end validation, run `lelab` and exercise endpoints (curl or via the frontend). diff --git a/README.md b/README.md index cd2068857..a50e968f3 100644 --- a/README.md +++ b/README.md @@ -78,18 +78,7 @@ PRs welcome. Hot-reload mode for working on the code: lelab --dev ``` -Vite on `:8080`, uvicorn `--reload` on `:8000`. - -For frontend changes, use Node.js 22 and rebuild the bundle before submitting your PR: - -```bash -cd frontend -npm ci -npm run build -git add dist -``` - -Commit `frontend/dist` alongside the source changes. Python installations serve this bundle; the required Quality check verifies that it is up to date. +Vite on `:8080`, uvicorn `--reload` on `:8000`. Installing LeLab builds the frontend bundle, so PRs never include `frontend/dist`.