refactor(docker): consolidated build into pi-base + pi-runtime stages

- Replaced the slim artifacts-only image with a full pi-base image (python + bun + rustup + natives + omp_rpc + omp shim).
- Moved robomp Dockerfile to repo root as Dockerfile.robomp, extending pi-base instead of copying from a scratch artifacts image.
- Renamed PI_ARTIFACTS_IMAGE to PI_BASE and updated all npm scripts and compose config accordingly.
- Split .dockerignore into per-Dockerfile shadows (Dockerfile.dockerignore, Dockerfile.robomp.dockerignore).
This commit is contained in:
can1357
2026-05-17 05:43:14 +02:00
parent 7b20915e20
commit b976d4d300
8 changed files with 242 additions and 221 deletions
+125 -36
View File
@@ -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-<arch>.node — N-API addon compiled from `crates/pi-natives`
# - omp_rpc-<version>-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-<arch>.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-<arch>.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/<x>/src/…` or `crates/<x>/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"]
+8 -19
View File
@@ -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/
+75
View File
@@ -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"]
@@ -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
+5 -4
View File
@@ -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",
-15
View File
@@ -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/
-132
View File
@@ -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-<arch>.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"]
+5 -4
View File
@@ -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