diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93af96a9..89bd5349 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,8 +30,13 @@ jobs: with: python-version: "3.11" + # enable-cache persists ~/.cache/uv across runs, keyed on uv.lock — + # turns `uv sync` from ~45 s cold to ~5 s warm. - name: Install uv uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" # Node 22 is needed for --experimental-strip-types so node:test can # import .ts files directly from frontend/src/api/*. @@ -43,10 +48,12 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v1 + # apt install ffmpeg is ~30 s every run; cache the resolved .debs. - name: System deps (ffmpeg) - run: | - sudo apt-get update - sudo apt-get install -y ffmpeg + uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: ffmpeg + version: 1.0 - name: Install Python deps run: uv sync @@ -54,6 +61,16 @@ jobs: - name: Run pytest run: uv run pytest tests/ -q --tb=short + # Cache ~/.bun/install/cache keyed on bun.lock — `bun install` drops + # from ~15 s cold to near-instant on warm cache. + - name: Cache bun deps + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('frontend/bun.lock', 'bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun- + - name: Install frontend deps working-directory: frontend run: bun install diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8144d7b0..fe043f4f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,8 +50,12 @@ jobs: with: python-version: "3.11" + # enable-cache persists ~/.cache/uv keyed on uv.lock. - name: Install uv uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" # Node 22 is needed for --experimental-strip-types so node:test can # import .ts files directly from frontend/src/api/*. @@ -63,13 +67,13 @@ jobs: - name: Setup Bun uses: oven-sh/setup-bun@v1 - # Backend tests need ffmpeg (subprocess calls in fixtures) + the minimal - # apt deps pydub/imageio pull in. Model weights are mocked so no HF - # downloads happen. + # Backend tests need ffmpeg (subprocess calls in fixtures). Cache the + # resolved .debs so warm runs skip the apt-get update + install. - name: System deps (ffmpeg) - run: | - sudo apt-get update - sudo apt-get install -y ffmpeg + uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: ffmpeg + version: 1.0 - name: Install Python deps run: uv sync @@ -77,6 +81,14 @@ jobs: - name: Run pytest run: uv run pytest tests/ -q --tb=short + - name: Cache bun deps + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('frontend/bun.lock', 'bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun- + - name: Install frontend deps working-directory: frontend run: bun install @@ -170,6 +182,14 @@ jobs: libasound2-dev ffmpeg # ── Frontend build ───────────────────────────────────────────────── + - name: Cache bun deps + uses: actions/cache@v4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('frontend/bun.lock', 'bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun- + - name: Install frontend deps working-directory: frontend run: bun install diff --git a/backend/main.py b/backend/main.py index efbaa9ac..773c668a 100644 --- a/backend/main.py +++ b/backend/main.py @@ -157,7 +157,7 @@ async def global_exception_handler(request: Request, exc: Exception): _allowed = os.environ.get( "OMNIVOICE_ALLOWED_ORIGINS", - "http://localhost:5173,http://127.0.0.1:5173,tauri://localhost,http://tauri.localhost", + "http://localhost:3901,http://127.0.0.1:3901,tauri://localhost,http://tauri.localhost", ).split(",") app.add_middleware( @@ -191,8 +191,10 @@ if os.path.exists(frontend_path): else: @app.get("/") def _dev_fallback(): - return RedirectResponse(url="http://localhost:5173") + return RedirectResponse(url="http://localhost:3901") if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=8000) + # Port 3900 picked to dodge common 8000 conflicts (Django/Rails/Jupyter). + # Rust sidecar launcher in lib.rs::BACKEND_PORT must stay in sync. + uvicorn.run(app, host="0.0.0.0", port=3900) diff --git a/frontend/src-tauri/src/lib.rs b/frontend/src-tauri/src/lib.rs index ee3fabb7..6c12fdf3 100644 --- a/frontend/src-tauri/src/lib.rs +++ b/frontend/src-tauri/src/lib.rs @@ -7,7 +7,11 @@ use std::sync::Mutex; use std::time::Duration; use tauri::Manager; -const BACKEND_PORT: u16 = 8000; +// Unique port range (3900-3902) chosen to avoid common conflicts: +// 8000 collides with Django/Rails/Jupyter/Airflow on most dev machines. +// 3900 is the backend (FastAPI + uvicorn), 3901 is the Vite dev server, +// 3902 is reserved for future IPC / websocket listeners. +const BACKEND_PORT: u16 = 3900; // Version of the Astral `uv` binary we download at first run when no system // uv is on PATH. Pinned for reproducibility — bump alongside the uv.lock diff --git a/frontend/src-tauri/tauri.conf.json b/frontend/src-tauri/tauri.conf.json index 56242624..93a5bb0e 100644 --- a/frontend/src-tauri/tauri.conf.json +++ b/frontend/src-tauri/tauri.conf.json @@ -5,7 +5,7 @@ "identifier": "com.debpalash.omnivoice-studio", "build": { "frontendDist": "../dist", - "devUrl": "http://localhost:5173", + "devUrl": "http://localhost:3901", "beforeDevCommand": "bun run dev", "beforeBuildCommand": "bun run build" }, diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index ce119fd6..93c17c00 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -68,7 +68,7 @@ const doubleClickMaximize = () => { * We upload to the backend's /preview endpoint and serve via HTTP instead. * Falls back to createObjectURL for regular browsers. */ -const _PREVIEW_API = import.meta.env.VITE_OMNIVOICE_API || 'http://localhost:8000'; +const _PREVIEW_API = import.meta.env.VITE_OMNIVOICE_API || 'http://localhost:3900'; const fileToMediaUrl = async (file, prevUrls) => { // Revoke previous blob URLs if they exist if (prevUrls?.videoUrl?.startsWith('blob:')) URL.revokeObjectURL(prevUrls.videoUrl); diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 428b19fd..6c5dd74d 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -1,8 +1,9 @@ -// Backend always listens on localhost:8000 — both in dev (Vite @ 5173 talking +// Backend always listens on localhost:3900 — both in dev (Vite @ 3901 talking // to a separate uvicorn) and in the built .app (Tauri webview @ tauri://localhost -// talking to the bundled frozen backend sidecar). Relative fetches against +// talking to the venv-bootstrapped sidecar). Relative fetches against // tauri://localhost don't reach the sidecar, so we hardcode the absolute host. -export const API = 'http://localhost:8000'; +// Port 3900 chosen to avoid common 8000 conflicts (Django/Rails/Jupyter). +export const API = 'http://localhost:3900'; export class ApiError extends Error { status?: number; diff --git a/frontend/vite.config.js b/frontend/vite.config.js index f856cc27..582dd5b8 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -13,7 +13,7 @@ export default defineConfig({ }, }, server: { - port: 5173, + port: 3901, strictPort: true, host: false, watch: {