diff --git a/Dockerfile b/Dockerfile index 5193334c1..da8583a13 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,32 +1,35 @@ # syntax=docker/dockerfile:1.7-labs ############################################################################### -# oh-my-pi — build-artifacts image +# oh-my-pi — pi image # -# Produces, in `/out/`, the cross-host build outputs that downstream consumers -# bake into their runtime images: -# -# - pi_natives.linux-.node — N-API addon compiled from `crates/pi-natives` -# - omp_rpc--py3-none-any.whl — Python RPC wheel from `python/omp-rpc` -# -# This image deliberately has no entrypoint and no apt-installed extras: it is -# meant to be referenced as a `COPY --from=` stage by other Dockerfiles. +# Stages: +# natives-builder — Rust + Bun → pi_natives.linux-.node +# wheel-builder — omp_rpc Python wheel +# pi-base — python + bun + rustup launcher + natives + omp_rpc +# + /usr/local/bin/omp shim +# pi-runtime — pi-base + pi source + bun install (DEFAULT, runnable) # # Build: -# docker build -t oh-my-pi/artifacts:dev . +# docker build -t oh-my-pi/pi:dev . # default = pi-runtime +# docker build --target pi-base -t oh-my-pi/pi-base:dev . # base for derived images # -# Consume from another Dockerfile: -# ARG PI_ARTIFACTS_IMAGE=oh-my-pi/artifacts:dev -# FROM ${PI_ARTIFACTS_IMAGE} AS pi-artifacts -# COPY --from=pi-artifacts /out/pi_natives.linux-*.node /opt/bun/bin/ -# COPY --from=pi-artifacts /out/*.whl /tmp/wheels/ +# Run: +# docker run --rm oh-my-pi/pi:dev --help +# docker run --rm -it -v "$PWD":/work oh-my-pi/pi:dev cli # interactive omp +# +# Consume as a base in another Dockerfile (see Dockerfile.robomp): +# ARG PI_BASE=oh-my-pi/pi:dev +# FROM ${PI_BASE} AS pi-base ############################################################################### +ARG BUN_VERSION=1.3.14 + ############################ # 1) natives-builder — Rust + Bun → pi_natives.linux-.node ############################ FROM rust:1.86-slim-bookworm AS natives-builder -ARG BUN_VERSION=1.3.14 +ARG BUN_VERSION ENV BUN_INSTALL=/opt/bun \ PATH=/opt/bun/bin:/usr/local/cargo/bin:/usr/local/bin:/usr/bin:/bin \ CARGO_TERM_COLOR=never @@ -41,11 +44,9 @@ RUN curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}" \ WORKDIR /pi -# ─── Layer 1: workspace manifests + lockfiles only ─────────────────────────── -# Editing source files (under `packages//src/…` or `crates//src/…`) won't -# bust the `bun install` layer below, because none of those globs match. Only -# touching a `package.json`, `Cargo.toml`, or a root lockfile invalidates this -# layer. `--parents` preserves the matched path under /pi/ (dockerfile 1.7-labs). +# Layer 1 — manifests + lockfiles only. Source edits under packages/*/src and +# crates/*/src won't bust `bun install` below. `--parents` preserves the +# matched path under /pi/ (requires syntax 1.7-labs). COPY --parents \ package.json bun.lock bunfig.toml \ tsconfig.base.json tsconfig.json \ @@ -56,19 +57,17 @@ COPY --parents \ crates/*/Cargo.toml \ /pi/ -# ─── Layer 2: hydrate node_modules from the manifests above ────────────────── +# Layer 2 — hydrate node_modules from the manifests above. RUN bun install --frozen-lockfile --ignore-scripts -# ─── Layer 3: full source ──────────────────────────────────────────────────── -# `.dockerignore` keeps `target/`, `node_modules/`, `dist/`, `runs/`, editor / -# OS noise (`.DS_Store`, `CPU.*`, `*.cpuprofile`, …), and pre-built host-only -# natives output out of the build context. node_modules from Layer 2 is -# preserved across this COPY because it's never in the context to begin with. +# Layer 3 — full source. `Dockerfile.dockerignore` keeps target/, node_modules/, +# dist/, runs/, editor noise, etc. out of the context. node_modules from Layer 2 +# is preserved across this COPY because it's never in the build context. COPY . /pi/ -# ─── Layer 4: compile pi-natives to a Linux N-API addon ────────────────────── -# Persistent caches make repeat builds incremental even when the source layer -# invalidates: cargo's package index + git-deps + the workspace's target dir. +# Layer 4 — compile pi-natives to a Linux N-API addon. Persistent caches keep +# repeat builds incremental: cargo's package index + git-deps + the workspace +# target dir. RUN --mount=type=cache,target=/root/.cargo/registry \ --mount=type=cache,target=/root/.cargo/git \ --mount=type=cache,target=/pi/target \ @@ -79,9 +78,9 @@ RUN --mount=type=cache,target=/root/.cargo/registry \ cp packages/natives/native/pi_natives.linux-*.node /out/ ############################ -# 2) python-builder — omp-rpc wheel +# 2) wheel-builder — omp-rpc wheel ############################ -FROM python:3.12-slim-bookworm AS python-builder +FROM python:3.12-slim-bookworm AS wheel-builder RUN apt-get update \ && apt-get install -y --no-install-recommends git \ @@ -94,8 +93,98 @@ COPY python/omp-rpc /src RUN python -m build --wheel --outdir /out ############################ -# 3) artifacts — final image, nothing but the two outputs. +# 3) pi-base — python + bun + rustup + natives + omp_rpc + omp shim +# +# Sharable runtime base. Derived images (pi-runtime below, Dockerfile.robomp) +# extend this and overlay their own source tree. Default PI_ROOT=/work/pi is +# friendly to derived images that mount a host pi checkout there; pi-runtime +# overrides it to /pi because its source is baked in. ############################ -FROM scratch AS artifacts -COPY --from=natives-builder /out/pi_natives.linux-*.node /out/ -COPY --from=python-builder /out/*.whl /out/ +FROM python:3.12-slim-bookworm AS pi-base + +ARG BUN_VERSION +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + BUN_INSTALL=/opt/bun \ + PI_ROOT=/work/pi \ + CARGO_HOME=/data/cache/cargo \ + CARGO_TARGET_DIR=/data/cache/cargo-target \ + RUSTUP_HOME=/data/cache/rustup \ + PATH=/opt/bun/bin:/usr/local/cargo/bin:/usr/local/bin:/usr/bin:/bin + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + git curl ca-certificates unzip openssh-client tini sqlite3 \ + build-essential pkg-config libssl-dev \ + && rm -rf /var/lib/apt/lists/* + +RUN curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}" \ + && /opt/bun/bin/bun --version + +# Rustup launcher only — the real toolchain is fetched lazily into RUSTUP_HOME +# on first cargo invocation, driven by pi's `rust-toolchain.toml`. Keeps the +# image small while sharing the toolchain across reboots when /data is mounted. +RUN curl -fsSL https://sh.rustup.rs -o /tmp/rustup-init.sh \ + && CARGO_HOME=/usr/local/cargo RUSTUP_HOME=/usr/local/rustup-bootstrap \ + sh /tmp/rustup-init.sh -y --no-modify-path --default-toolchain none --profile minimal \ + && rm -f /tmp/rustup-init.sh \ + && rm -rf /usr/local/rustup-bootstrap \ + && /usr/local/cargo/bin/rustup --version + +# pi-natives addon: pi's loader probes /opt/bun/bin as a fallback path. +COPY --from=natives-builder /out/pi_natives.linux-*.node /opt/bun/bin/ + +# omp-rpc Python wheel. +COPY --from=wheel-builder /out/*.whl /tmp/wheels/ +RUN pip install /tmp/wheels/omp_rpc-*.whl && rm -rf /tmp/wheels + +# `omp` shim — runs the coding-agent CLI against $PI_ROOT via Bun. Derived +# images override PI_ROOT to point at wherever their pi source lives. +RUN printf '%s\n' \ + '#!/usr/bin/env bash' \ + 'set -euo pipefail' \ + ': "${PI_ROOT:=/work/pi}"' \ + 'if [ ! -d "$PI_ROOT/packages/coding-agent" ]; then' \ + ' echo "pi: PI_ROOT=$PI_ROOT does not look like a pi checkout" >&2' \ + ' exit 127' \ + 'fi' \ + 'exec bun "$PI_ROOT/packages/coding-agent/src/cli.ts" "$@"' \ + > /usr/local/bin/omp \ + && chmod +x /usr/local/bin/omp + +############################ +# 4) pi-runtime — pi-base + pi source + bun install (DEFAULT) +# +# A self-contained, runnable omp image. `docker run oh-my-pi/pi:dev --help` +# Just Works without a host checkout. +############################ +FROM pi-base AS pi-runtime + +ENV PI_ROOT=/pi +WORKDIR /pi + +# Same manifests-only layered install pattern as natives-builder — `bun install` +# only re-runs when a package.json / lockfile changes. +COPY --parents \ + package.json bun.lock bunfig.toml \ + tsconfig.base.json tsconfig.json \ + packages/*/package.json \ + packages/tsconfig.workspace.json \ + python/robomp/web/package.json \ + /pi/ + +RUN bun install --frozen-lockfile --ignore-scripts + +# Pi source. `Dockerfile.dockerignore` keeps **/node_modules out of the context +# so stale isolated-linker symlinks from a host install can't shadow the +# hoisted node_modules that `bun install` just produced. +COPY . /pi/ + +# Regenerate the docs index that `--ignore-scripts` skipped above. The root +# package.json's `prepare` script normally handles this on a vanilla install. +RUN bun --cwd=packages/coding-agent run generate-docs-index + +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/omp"] +CMD ["--help"] diff --git a/Dockerfile.dockerignore b/Dockerfile.dockerignore index a60e23cb8..1a2b0a11b 100644 --- a/Dockerfile.dockerignore +++ b/Dockerfile.dockerignore @@ -1,17 +1,11 @@ -# Pi-artifacts build context (this file shadows `.dockerignore` only for the -# pi-root `Dockerfile`). Robomp builds with `dockerfile: python/robomp/Dockerfile` -# still fall back to the shared `.dockerignore` next door because they don't -# have their own ignore file. -# -# Keep this file in sync with `.dockerignore` for the shared rules; everything -# below the divider is the artifacts-only addendum. - -# ─── Shared with .dockerignore ──────────────────────────────────────────────── +# Build context for the pi-root `Dockerfile` (oh-my-pi/pi:dev). Shadows +# .dockerignore for this file only. Robomp uses Dockerfile.robomp + +# Dockerfile.robomp.dockerignore alongside. # Heavy build outputs — must never reach the build context. `target/` alone is # >100 GB on a dev machine. target/ -node_modules/ +**/node_modules dist/ runs/ @@ -59,6 +53,10 @@ packages/natives/native/pi_natives.darwin-*.node packages/natives/native/pi_natives.dev.node packages/ai/test/.temp-images/ python/omp-rpc/src/omp_rpc.egg-info/ +python/robomp/data/ +python/robomp/.cache/ +python/robomp/src/robomp/static/ +python/robomp/web/dist/ # Scratch files the repo creates ad-hoc. syntax.jsonl @@ -68,12 +66,3 @@ pi-*.html # Secrets. Should never be in the image regardless. .env - -# ─── Pi-artifacts only ──────────────────────────────────────────────────────── -# Robomp's source tree is unused by the artifacts image — pi-natives + omp-rpc -# are the only outputs, and `python/omp-rpc/` is reached explicitly by the -# python-builder stage (`COPY python/omp-rpc /src`). Everything under -# `python/robomp/` (orchestrator source, web bundle, tests, container scripts) -# would otherwise be transferred as part of the `COPY . /pi/` layer and bake -# uselessly into the natives-builder cache. -python/robomp/ diff --git a/Dockerfile.robomp b/Dockerfile.robomp new file mode 100644 index 000000000..f79fca4b9 --- /dev/null +++ b/Dockerfile.robomp @@ -0,0 +1,75 @@ +# syntax=docker/dockerfile:1.7-labs +############################################################################### +# robomp — GitHub triage+fix bot orchestrator. +# +# Extends `pi-base` (from /Dockerfile, default target oh-my-pi/pi:dev) and adds +# the robomp Python package + a Vite-built SolidJS dashboard bundle. The pi +# toolchain (python + bun + rustup launcher + pi-natives + omp_rpc wheel + +# /usr/local/bin/omp shim) all comes from PI_BASE; this file only layers what's +# robomp-specific. +# +# Build (from pi root): +# bun run pi:image # build oh-my-pi/pi:dev first +# docker build -f Dockerfile.robomp -t robomp:dev . +# +# Compose (recommended): +# docker compose --project-directory python/robomp build +############################################################################### + +ARG PI_BASE=oh-my-pi/pi:dev +ARG BUN_VERSION=1.3.14 + +############################ +# 1) web-builder — Bun + Vite, builds the SolidJS dashboard bundle. +############################ +FROM oven/bun:${BUN_VERSION}-slim AS web-builder +WORKDIR /work +# Root manifests + the web workspace manifest are enough for `bun install +# --filter robomp-web` to hydrate just the dashboard's node_modules. +COPY package.json bun.lock ./ +COPY python/robomp/web/package.json ./python/robomp/web/package.json +RUN bun install --filter robomp-web +COPY --exclude=node_modules --exclude=dist python/robomp/web/ ./python/robomp/web/ +RUN bun --cwd=python/robomp/web run build + +############################ +# 2) runtime — pi-base + robomp src + web bundle + pip install +############################ +FROM ${PI_BASE} AS runtime + +# robomp runs against the host pi checkout mounted at /work/pi read-only. +ENV PI_ROOT=/work/pi + +WORKDIR /app + +# robomp itself. Drop the Vite-built dashboard into the package tree before +# `pip install` so it lands in the installed wheel (`static/**/*` is declared +# as package-data in pyproject.toml). +COPY python/robomp/pyproject.toml ./ +COPY python/robomp/src/ ./src/ +COPY --from=web-builder /work/python/robomp/web/dist/ ./src/static/ + +RUN pip install --no-cache-dir \ + "fastapi>=0.112" "uvicorn[standard]>=0.30" "httpx>=0.27" \ + "pydantic>=2.6" "pydantic-settings>=2.2" "python-dotenv>=1.0" \ + "click>=8.1" \ + && pip install --no-cache-dir --no-deps . + +# Host agent config is mounted read-only under /srv/agent-home-stage with +# host-controlled permissions. The entrypoint copies it into root-owned +# world-readable files under /srv/agent-home; the agent subprocess runs with +# HOME=/srv/agent-home, so ~/.omp and ~/.agent resolve there without exposing +# mutable host mounts. +RUN mkdir -p /srv/agent-home/.agent /srv/agent-home/.omp/agent \ + && mkdir -p /srv/agent-home-stage/.agent /srv/agent-home-stage/.omp/agent \ + && printf '[install]\nbackend = "copyfile"\n' > /srv/agent-home/.bunfig.toml + +COPY python/robomp/entrypoint.sh /usr/local/bin/robomp-entrypoint +RUN chmod +x /usr/local/bin/robomp-entrypoint + +VOLUME ["/data"] +EXPOSE 8080 +EXPOSE 8081 + +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/robomp-entrypoint"] +CMD ["python", "-m", "robomp", "serve"] diff --git a/.dockerignore b/Dockerfile.robomp.dockerignore similarity index 53% rename from .dockerignore rename to Dockerfile.robomp.dockerignore index 55bbc1f73..bd5bc3e72 100644 --- a/.dockerignore +++ b/Dockerfile.robomp.dockerignore @@ -1,7 +1,13 @@ -# Heavy build outputs — must never reach the build context. `target/` alone is -# >100 GB on a dev machine. +# Build context for `Dockerfile.robomp` (robomp:dev). Shadows .dockerignore +# for this file only — the pi-root build uses Dockerfile.dockerignore instead. +# +# Note: this duplicates most of the entries in Dockerfile.dockerignore. That's +# the cost of per-Dockerfile shadows (no shared file to factor common rules +# into). Keep them roughly in sync. + +# Heavy build outputs — must never reach the build context. target/ -node_modules/ +**/node_modules dist/ runs/ @@ -13,17 +19,14 @@ runs/ .pi_config/ .omp/plugins/ -# VCS, editors, IDEs — irrelevant to the build, churn on every IDE keystroke. +# VCS, editors, IDEs. .git/ .npm/ .vscode/ .zed/ .idea/ -# OS + transient noise. Finder rewrites .DS_Store whenever you peek at a -# folder; profilers drop `CPU.*` blobs at random times. Letting any of these -# into the build context busts BuildKit's content hash and forces a full -# native rebuild for no good reason. +# OS + transient noise. .DS_Store *.swp *.swo @@ -52,9 +55,6 @@ packages/natives/native/pi_natives.darwin-*.node packages/natives/native/pi_natives.dev.node packages/ai/test/.temp-images/ python/omp-rpc/src/omp_rpc.egg-info/ - -# robomp runtime state — robomp has its own Dockerfile/build context; -# keep these out of the monorepo image too. python/robomp/data/ python/robomp/.cache/ python/robomp/src/robomp/static/ @@ -68,3 +68,16 @@ pi-*.html # Secrets. Should never be in the image regardless. .env + +# Robomp-only excludes. Natives + wheel + python + bun + rustup all come +# from PI_BASE; the web-builder stage only needs root manifests + the +# python/robomp/web tree; the runtime stage only COPYs python/robomp/ +# pyproject + src + entrypoint. Everything below is dead weight in the +# robomp build context. +crates/ +docs/ +assets/ +scripts/ +LICENSE +AGENTS.md +README.md diff --git a/package.json b/package.json index 36a99b45c..05e3e2545 100644 --- a/package.json +++ b/package.json @@ -121,15 +121,16 @@ "robomp:install": "pip install -e 'python/robomp[dev]'", "robomp:serve": "python3 -m robomp serve", "robomp:test:integration": "ROBOMP_INTEGRATION=1 python3 -m pytest -x python/robomp/tests/test_worker_smoke.py", - "robomp:pi-artifacts": "docker build -t \"${PI_ARTIFACTS_IMAGE:-oh-my-pi/artifacts:dev}\" .", - "robomp:build": "bun run robomp:pi-artifacts && docker compose --project-directory python/robomp build", - "robomp:rebuild": "bun run robomp:pi-artifacts && docker compose --project-directory python/robomp build --no-cache", + "pi:image": "docker build -t \"${PI_IMAGE:-oh-my-pi/pi:dev}\" .", + "pi:run": "docker run --rm -it \"${PI_IMAGE:-oh-my-pi/pi:dev}\"", + "robomp:build": "bun run pi:image && docker compose --project-directory python/robomp build", + "robomp:rebuild": "bun run pi:image && docker compose --project-directory python/robomp build --no-cache", "robomp:up": "docker compose --project-directory python/robomp up -d", "robomp:down": "docker compose --project-directory python/robomp down", "robomp:restart": "docker compose --project-directory python/robomp restart robomp", "robomp:logs": "docker compose --project-directory python/robomp logs -f robomp", "robomp:dev": "bun run robomp:build && bun run robomp:up && bun run robomp:logs", - "robomp:reset": "docker compose --project-directory python/robomp down -v && (docker image rm \"${PI_ARTIFACTS_IMAGE:-oh-my-pi/artifacts:dev}\" || true)", + "robomp:reset": "docker compose --project-directory python/robomp down -v && (docker image rm \"${PI_IMAGE:-oh-my-pi/pi:dev}\" || true)", "robomp:web:dev": "bun --cwd=python/robomp/web run dev", "robomp:web:build": "bun --cwd=python/robomp/web run build", "lint:py": "ruff check python && ruff format --check python", diff --git a/python/robomp/.dockerignore b/python/robomp/.dockerignore deleted file mode 100644 index 227f20ad6..000000000 --- a/python/robomp/.dockerignore +++ /dev/null @@ -1,15 +0,0 @@ -.venv/ -.pi-context/ -data/ -*.pyc -__pycache__/ -.pytest_cache/ -.git/ -.env -*.sqlite -*.sqlite-wal -*.sqlite-shm -web/node_modules/ -web/dist/ -src/static/ -node_modules/ diff --git a/python/robomp/Dockerfile b/python/robomp/Dockerfile deleted file mode 100644 index 6ba771cfd..000000000 --- a/python/robomp/Dockerfile +++ /dev/null @@ -1,132 +0,0 @@ -# syntax=docker/dockerfile:1.7-labs -############################################################################### -# roboomp — orchestrator image -# -# Build is split across three stages: -# -# 1) pi-artifacts — pull a pre-built `oh-my-pi/artifacts:dev` image (built -# separately from /work/pi/Dockerfile, see `bun run robomp:pi-artifacts`): -# - pi_natives.linux-.node → /opt/bun/bin/ (the pi loader probes here) -# - omp_rpc-*.whl → pip install -# 2) web-builder — Bun + Vite compile the SolidJS dashboard bundle from -# the `web/` workspace into `web/dist/`. -# 3) runtime — slim Python 3.12 image that copies in (1) the natives -# + wheel, (2) the dashboard bundle, and (3) the roboomp source. -# -# At runtime the full pi checkout is mounted read-only at /work/pi so `omp` -# (the Bun shim below) executes the coding-agent source directly. The image -# itself stays slim: no rust compile, no pi source tree, no node_modules. -############################################################################### - -ARG PI_ARTIFACTS_IMAGE=oh-my-pi/artifacts:dev - -############################ -# 1) pi-artifacts — pull the pre-built natives + omp-rpc wheel. -############################ -FROM ${PI_ARTIFACTS_IMAGE} AS pi-artifacts - -############################ -# 2) web-builder — Bun + Vite, builds the SolidJS dashboard bundle. -############################ -FROM oven/bun:1.3.14-slim AS web-builder -WORKDIR /work -# Build context is the pi monorepo root, so the web-builder stage installs -# from pi's bun.lock — that's how `web/package.json` resolves its `catalog:` -# references against the workspace-wide catalog declared at pi root. -COPY package.json bun.lock ./ -COPY python/robomp/web/package.json ./python/robomp/web/package.json -RUN bun install --filter robomp-web -COPY --exclude=node_modules --exclude=dist python/robomp/web/ ./python/robomp/web/ -RUN bun --cwd=python/robomp/web run build - -############################ -# 3) runtime — slim image with everything roboomp needs at boot. -############################ -FROM python:3.12-slim-bookworm AS runtime - -ENV PYTHONDONTWRITEBYTECODE=1 \ - PYTHONUNBUFFERED=1 \ - PIP_NO_CACHE_DIR=1 \ - PIP_DISABLE_PIP_VERSION_CHECK=1 \ - BUN_INSTALL=/opt/bun \ - PI_ROOT=/work/pi \ - # Persistent build caches under the /data volume so cargo target and - # rustup toolchains are shared across every per-issue worktree and - # survive container restarts. Bun's install cache is deliberately - # workspace-private at runtime; bun chmod/chown behavior makes a shared - # cross-slot cache unreliable. - CARGO_HOME=/data/cache/cargo \ - CARGO_TARGET_DIR=/data/cache/cargo-target \ - RUSTUP_HOME=/data/cache/rustup \ - PATH=/opt/bun/bin:/usr/local/cargo/bin:/usr/local/bin:/usr/bin:/bin - -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - git curl ca-certificates unzip openssh-client tini sqlite3 \ - build-essential pkg-config libssl-dev \ - && rm -rf /var/lib/apt/lists/* - -ARG BUN_VERSION=1.3.14 -RUN curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}" \ - && /opt/bun/bin/bun --version - -# Rustup launcher. Install the cargo/rustc/rustup proxies into a fixed -# image path; the real toolchain is *not* baked in — it's installed -# lazily into RUSTUP_HOME (=/data/cache/rustup) on the first `cargo` -# invocation inside a worktree, driven by pi's rust-toolchain.toml. -# That keeps the image small while sharing the toolchain across reboots. -RUN curl -fsSL https://sh.rustup.rs -o /tmp/rustup-init.sh \ - && CARGO_HOME=/usr/local/cargo RUSTUP_HOME=/usr/local/rustup-bootstrap \ - sh /tmp/rustup-init.sh -y --no-modify-path --default-toolchain none --profile minimal \ - && rm -f /tmp/rustup-init.sh \ - && rm -rf /usr/local/rustup-bootstrap \ - && /usr/local/cargo/bin/rustup --version - -# pi-natives addon: pi's loader probes /opt/bun/bin as a fallback path. -COPY --from=pi-artifacts /out/pi_natives.linux-*.node /opt/bun/bin/ - -# omp-rpc Python wheel. -COPY --from=pi-artifacts /out/*.whl /tmp/wheels/ -RUN pip install /tmp/wheels/omp_rpc-*.whl && rm -rf /tmp/wheels - -WORKDIR /app - -# `omp` shim — calls into the mounted pi checkout via Bun. -RUN printf '%s\n' \ - '#!/usr/bin/env bash' \ - 'set -euo pipefail' \ - ': "${PI_ROOT:=/work/pi}"' \ - 'if [ ! -d "$PI_ROOT/packages/coding-agent" ]; then' \ - ' echo "roboomp: PI_ROOT=$PI_ROOT does not look like a pi checkout" >&2' \ - ' exit 127' \ - 'fi' \ - 'exec bun "$PI_ROOT/packages/coding-agent/src/cli.ts" "$@"' \ - > /usr/local/bin/omp \ - && chmod +x /usr/local/bin/omp - -# roboomp itself. Drop the Vite-built dashboard into the package tree before -# `pip install` so it lands in the installed wheel (`static/**/*` is declared -# as package-data in pyproject.toml). -COPY python/robomp/pyproject.toml ./ -COPY python/robomp/src/ ./src/ -COPY --from=web-builder /work/python/robomp/web/dist/ ./src/static/ -RUN pip install --upgrade pip \ - && pip install \ - "fastapi>=0.112" "uvicorn[standard]>=0.30" "httpx>=0.27" \ - "pydantic>=2.6" "pydantic-settings>=2.2" "python-dotenv>=1.0" \ - "click>=8.1" \ - && pip install --no-deps . - -RUN mkdir -p /srv/agent-home/.agent /srv/agent-home/.omp/agent \ - && mkdir -p /srv/agent-home-stage/.agent /srv/agent-home-stage/.omp/agent \ - && printf '[install]\nbackend = "copyfile"\n' > /srv/agent-home/.bunfig.toml - -COPY python/robomp/entrypoint.sh /usr/local/bin/robomp-entrypoint -RUN chmod +x /usr/local/bin/robomp-entrypoint - -VOLUME ["/data"] -EXPOSE 8080 -EXPOSE 8081 - -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/robomp-entrypoint"] -CMD ["python", "-m", "robomp", "serve"] diff --git a/python/robomp/docker-compose.yml b/python/robomp/docker-compose.yml index 302a93281..f4732a86c 100644 --- a/python/robomp/docker-compose.yml +++ b/python/robomp/docker-compose.yml @@ -13,13 +13,14 @@ services: build: # pi root: gives the web-builder stage access to the workspace # bun.lock + catalog (web/package.json refs `catalog:` versions). - # python/robomp/data is excluded via pi's .dockerignore. + # python/robomp/data and pi-root excludes are filtered via + # Dockerfile.robomp.dockerignore at the context root. context: ../.. - dockerfile: python/robomp/Dockerfile + dockerfile: Dockerfile.robomp args: - # Tag of the pre-built artifacts image produced by `bun run robomp:pi-artifacts` + # Tag of the pre-built pi-base image produced by `bun run pi:image` # (sources: pi root /Dockerfile). Override per-environment as needed. - PI_ARTIFACTS_IMAGE: oh-my-pi/artifacts:dev + PI_BASE: oh-my-pi/pi:dev image: robomp:dev container_name: robomp # Phase B (graceful shutdown): gives the orchestrator at least