replace flake8 with ruff #33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| jobs: | |
| # --------------------------------------------------------------------------- | |
| # Job 1 — Lint + unit tests | |
| # Runs on every pull request. No Gazebo, no GPU required. | |
| # --------------------------------------------------------------------------- | |
| lint-and-test: | |
| name: Lint and unit tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python 3.12 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| cache: "pip" | |
| - name: Install PyTorch (CPU build for CI) | |
| # requirements-train.txt pins +cu121 wheels which are only on the PyTorch | |
| # index. Install the CPU equivalents first so the rest of the deps resolve. | |
| run: | | |
| pip install torch==2.5.1 torchvision==0.20.1 \ | |
| --index-url https://download.pytorch.org/whl/cpu | |
| - name: Install remaining dependencies | |
| run: | | |
| # Strip torch/torchvision lines (already installed above as CPU builds) | |
| grep -v '^torch' requirements-train.txt > /tmp/req-ci.txt | |
| pip install -r /tmp/req-ci.txt | |
| - name: Lint | |
| run: | | |
| python -m ruff check train/ ocelot/ sim/ tests/ | |
| - name: Unit tests (no sim, no GPU) | |
| # tests/sim/ require the sim container — excluded here | |
| run: pytest tests/train/ -v --tb=short | |
| # --------------------------------------------------------------------------- | |
| # Job 2 — Offline model eval gate | |
| # Runs on every push to main that changes train/, ocelot/vla_node.py, or models/. | |
| # Blocks deployment if the model fails the pass/fail gate. | |
| # | |
| # Prerequisites: | |
| # • DVC remote configured in .dvc/config (public S3, anonymous access) | |
| # • models/vla.onnx tracked by DVC (dvc add models/vla.onnx) | |
| # • models/vla_tokens.json committed to git (generated by export_onnx.py) | |
| # • dataset/ tracked by DVC (dvc add dataset/) | |
| # --------------------------------------------------------------------------- | |
| eval: | |
| name: Model eval gate | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'push' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python 3.12 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| cache: "pip" | |
| - name: Install eval dependencies | |
| # Minimal install — only what eval_onnx.py needs. No torch/GPU required. | |
| run: | | |
| pip install dvc h5py onnxruntime numpy tqdm transformers | |
| - name: Pull dataset and model (DVC) | |
| id: dvc-pull | |
| continue-on-error: true | |
| run: dvc pull dataset/ models/vla.onnx | |
| - name: Run offline eval (50-episode stratified subset) | |
| id: eval-run | |
| if: steps.dvc-pull.outcome == 'success' | |
| continue-on-error: true | |
| # 50 episodes × ~100 frames ≈ 5 k frames at ~100–200 ms/frame on CPU | |
| # ≈ 8–17 min. Stratified ensures ≥ 1 episode per label type. | |
| run: | | |
| python3 train/eval_onnx.py \ | |
| --model_path models/vla.onnx \ | |
| --dataset_dir dataset/ \ | |
| --split test \ | |
| --max_episodes 50 \ | |
| --stratified \ | |
| --seed 42 \ | |
| --token_cache models/vla_tokens.json \ | |
| --output eval_results.json | |
| - name: Upload eval report | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: eval-results | |
| path: eval_results.json | |
| if-no-files-found: ignore | |
| - name: Log eval result | |
| if: always() | |
| run: | | |
| if [ "${{ steps.dvc-pull.outcome }}" != "success" ]; then | |
| echo "::warning::DVC pull failed — skipping eval. Set up DVC remote + secrets to enable." | |
| exit 0 | |
| fi | |
| if [ "${{ steps.eval-run.outcome }}" != "success" ]; then | |
| echo "::warning::Eval script failed — check logs above." | |
| exit 0 | |
| fi | |
| python3 - <<'EOF' | |
| import json | |
| r = json.load(open("eval_results.json")) | |
| print(f"Episodes evaluated : {r['n_episodes']}") | |
| print(f"Overall MSE : {r['overall_mse']:.5f} (threshold: {r['mse_threshold']})") | |
| if r.get("per_label_mse"): | |
| print("Per-label MSE:") | |
| for lk, mse in sorted(r["per_label_mse"].items()): | |
| flag = " !" if mse >= r["per_label_limit"] else " " | |
| print(f" {flag} {lk:30s}: {mse:.5f}") | |
| verdict = "PASS" if r["pass"] else "FAIL" | |
| print(f"Verdict: {verdict}") | |
| if not r["pass"]: | |
| print("::warning::Model eval gate FAILED — see results above.") | |
| EOF |