# Base images are pinned to an immutable digest (in addition to a human-readable # tag) so every build resolves the exact same layers — reproducible builds and # safe rollbacks (#563). # Stage 1: Build frontend SPA FROM node:22.23.0-slim@sha256:d9f850096136edbc402debdd8729579a288aac64574ada0ff4db26b6ae58b0b2 AS frontend # Public Supabase anon key for the savings leaderboard; empty by default so # the image's leaderboard stays disabled (#589). Pass --build-arg to enable. ARG OPENJARVIS_LEADERBOARD_PUBLIC_ANON= WORKDIR /frontend COPY frontend/package.json frontend/package-lock.json* ./ RUN npm ci --ignore-scripts 2>/dev/null || npm install COPY frontend/ . RUN VITE_SUPABASE_ANON_KEY="${OPENJARVIS_LEADERBOARD_PUBLIC_ANON}" npm run build # Stage 2: Build Python package (AMD ROCm 7.2) FROM rocm/dev-ubuntu-22.04:7.2@sha256:05af5f04a06b04676d4c7438997d0deadaeb7478961ad621376e199bf3aeb644 AS builder RUN apt-get update && \ apt-get install -y --no-install-recommends \ build-essential \ ca-certificates \ curl \ python3 \ python3-dev \ python3-pip \ python3-venv && \ rm -rf /var/lib/apt/lists/* ENV PATH="/root/.cargo/bin:${PATH}" RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ sh -s -- -y --profile minimal --default-toolchain none && \ rustup toolchain install 1.88 --profile minimal && \ rustup default 1.88 WORKDIR /app # Install dependencies from the committed lockfile (#567). See deploy/docker/Dockerfile # for the rationale behind the frozen export + --no-deps install. COPY pyproject.toml uv.lock README.md ./ RUN pip install --no-cache-dir uv && \ uv export --frozen --no-dev --extra server --no-emit-project > requirements.txt && \ uv pip install --system --no-deps -r requirements.txt && \ uv pip install --system --no-deps "maturin>=1.12.6,<2" COPY src/ src/ COPY rust/ rust/ COPY scripts/install scripts/install COPY deploy/windows deploy/windows COPY --from=frontend /src/openjarvis/server/static src/openjarvis/server/static/ RUN uv pip install --system --no-deps . && \ maturin build --release \ --manifest-path rust/crates/openjarvis-python/Cargo.toml \ --interpreter python3 \ --out /tmp/openjarvis-rust-wheel && \ uv pip install --system --no-deps /tmp/openjarvis-rust-wheel/*.whl && \ python3 -c "import openjarvis_rust; print('openjarvis_rust ok')" && \ python3 -m pip uninstall -y maturin && \ rm -rf /tmp/openjarvis-rust-wheel rust # Stage 3: Runtime FROM rocm/dev-ubuntu-22.04:7.2@sha256:05af5f04a06b04676d4c7438997d0deadaeb7478961ad621376e199bf3aeb644 RUN apt-get update && \ apt-get install -y --no-install-recommends python3 python3-pip && \ rm -rf /var/lib/apt/lists/* COPY --from=builder /usr/local /usr/local COPY --from=builder /app /app WORKDIR /app # Run as an unprivileged user (#565). ROCm GPU access is gated by the `video` and # `render` groups (see group_add in docker-compose.gpu.rocm.yml), so the user is # added to both; root is not required. RUN groupadd --system --gid 10001 openjarvis && \ useradd --system --uid 10001 --gid openjarvis \ --create-home --home-dir /home/openjarvis openjarvis && \ (getent group video >/dev/null || groupadd --system video) && \ (getent group render >/dev/null || groupadd --system render) && \ usermod -aG video,render openjarvis ENV HOME=/home/openjarvis USER openjarvis EXPOSE 8000 ENTRYPOINT ["jarvis"] CMD ["serve", "--host", "0.0.0.0", "--port", "8000"]