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:
+125
-36
@@ -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
@@ -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/
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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/
|
||||
@@ -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"]
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user