118 lines
6.1 KiB
Docker
118 lines
6.1 KiB
Docker
# syntax=docker/dockerfile:1.7
|
|
# thothii-core: Fastify (Node 22) + harness Python 3.12 (tht CLI) + runtime Pi.
|
|
# Singolo container, entrypoint logico "server" (default).
|
|
ARG PI_VERSION=0.80.3
|
|
ARG IMAGE_VERSION=local
|
|
|
|
# ---- Pinned Node source for the runtime binary and npm ----
|
|
FROM node:22-bookworm@sha256:7725a5c2c83eed1d36258c66efae14b1ceccd021db9ed1d9559d3335ed3d68ed AS node-runtime
|
|
|
|
# ---- Stage 0: locked Pi runtime ----
|
|
FROM node:22-bookworm@sha256:7725a5c2c83eed1d36258c66efae14b1ceccd021db9ed1d9559d3335ed3d68ed AS pi-runtime-build
|
|
ARG PI_VERSION
|
|
ARG PI_RUNTIME_PACKAGE_VERSION
|
|
ARG PI_PACKAGE_NAME=@earendil-works/pi-coding-agent
|
|
WORKDIR /opt/pi-runtime
|
|
COPY docker/pi-runtime/package.json docker/pi-runtime/package-lock.json ./
|
|
RUN if [ -n "$PI_RUNTIME_PACKAGE_VERSION" ]; then \
|
|
node -e 'const fs=require("node:fs"); const [name,version]=process.argv.slice(1); const manifest=JSON.parse(fs.readFileSync("package.json","utf8")); manifest.dependencies[name]=version; fs.writeFileSync("package.json",JSON.stringify(manifest,null,2)+"\\n");' "$PI_PACKAGE_NAME" "$PI_RUNTIME_PACKAGE_VERSION"; \
|
|
npm install --package-lock-only --ignore-scripts --omit=dev "$PI_PACKAGE_NAME@$PI_RUNTIME_PACKAGE_VERSION"; \
|
|
fi \
|
|
&& npm ci --omit=dev \
|
|
&& test "$(./node_modules/.bin/pi --version)" = "$PI_VERSION"
|
|
|
|
# ---- Stage 1: backend TypeScript -> dist ----
|
|
FROM node:22-bookworm@sha256:7725a5c2c83eed1d36258c66efae14b1ceccd021db9ed1d9559d3335ed3d68ed AS backend-build
|
|
WORKDIR /src/backend
|
|
COPY backend/package*.json ./
|
|
RUN npm ci
|
|
COPY backend/ ./
|
|
RUN npm run build
|
|
|
|
# ---- Stage 2: runtime (Python 3.12 nativo + Node 22 copiato, stesso glibc bookworm) ----
|
|
FROM python:3.12-slim-bookworm@sha256:d50fb7611f86d04a3b0471b46d7557818d88983fc3136726336b2a4c657aa30b AS runtime
|
|
ARG PI_VERSION
|
|
ARG IMAGE_VERSION
|
|
LABEL org.opencontainers.image.title="thothii-core" \
|
|
org.opencontainers.image.version="${IMAGE_VERSION}" \
|
|
org.opencontainers.image.description="ThothII core with its embedded Pi runtime" \
|
|
io.thothii.pi.version="${PI_VERSION}"
|
|
|
|
# Runtime tools
|
|
RUN set -eux; \
|
|
runtime_packages="curl ca-certificates ripgrep fd-find tini git openssh-client"; \
|
|
if ! command -v flock >/dev/null 2>&1; then \
|
|
runtime_packages="$runtime_packages util-linux"; \
|
|
fi; \
|
|
apt-get update; \
|
|
apt-get install -y --no-install-recommends $runtime_packages; \
|
|
rm -rf /var/lib/apt/lists/*; \
|
|
command -v flock >/dev/null 2>&1; \
|
|
ln -s /usr/bin/fdfind /usr/local/bin/fd
|
|
|
|
# Node 22 + npm copiati dall'immagine ufficiale (stesso Debian bookworm → binario compatibile)
|
|
COPY --from=node-runtime /usr/local/bin/node /usr/local/bin/node
|
|
COPY --from=node-runtime /usr/local/lib/node_modules /usr/local/lib/node_modules
|
|
RUN ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
|
|
&& ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx
|
|
|
|
# Utente non-root
|
|
RUN useradd --create-home --uid 10001 --shell /bin/bash thoth
|
|
# Docker copies these owned directories into newly-created named volumes, allowing the non-root
|
|
# runtime user to create application settings, sessions, registry snapshots, state, and locks.
|
|
RUN mkdir -p /home/thoth/.pi/agent /data/settings /data/sessions /data/workspace-registry /data/workspace-secrets \
|
|
&& chown -R thoth:thoth /home/thoth/.pi /data
|
|
|
|
COPY harness/ /app/harness/
|
|
# Pi scrive lock/settings in .pi: ownership thoth per sopravvivere al rebuild
|
|
RUN chown -R thoth:thoth /app/harness/.pi
|
|
|
|
# Harness: venv nativo (python 3.12) + tht installato (non-editable).
|
|
# tht carica workflow.yaml module-relative (Path(__file__).parent.parent); con il package
|
|
# in site-packages quel path non contiene workflow.yaml -> lo copiamo dopo l'install.
|
|
# (pip install -e non funziona: pip non riconosce /app/harness come editable req.)
|
|
# psycopg2-binary è wheel → niente gcc/libpq-dev.
|
|
RUN python -m venv /opt/venv \
|
|
&& /opt/venv/bin/pip install --no-cache-dir --upgrade pip \
|
|
&& (cd /app/harness && /opt/venv/bin/pip install --no-cache-dir .) \
|
|
&& cp /app/harness/workflow.yaml /opt/venv/lib/python3.12/site-packages/workflow.yaml
|
|
# Il workspace locale predefinito converge sul file canonico. Il CLI onora anche THT_CONFIG,
|
|
# quindi cambiare CWD non cambia l'identita' dello workspace.
|
|
RUN mkdir -p /app/harness/config \
|
|
&& cp --remove-destination /app/harness/workspaces/local.yaml /app/harness/config/tht.yaml
|
|
# PiProcessManager (backend) prepende harnessDir/.venv/bin al PATH del child Pi → symlink al venv reale
|
|
RUN ln -s /opt/venv /app/harness/.venv
|
|
|
|
# Backend: dist + node_modules (stesso Node major 22 + glibc bookworm → compatibili)
|
|
COPY --from=backend-build /src/backend/dist /app/backend/dist
|
|
COPY --from=backend-build /src/backend/node_modules /app/backend/node_modules
|
|
COPY backend/package*.json /app/backend/
|
|
|
|
# Runtime Pi is installed only from the committed lockfile. The image exposes its immutable
|
|
# executable directly, so no host Pi installation or writable global npm directory is needed.
|
|
COPY --from=pi-runtime-build /opt/pi-runtime/node_modules /opt/pi-runtime/node_modules
|
|
RUN ln -s /opt/pi-runtime/node_modules/.bin/pi /usr/local/bin/pi \
|
|
&& test "$(pi --version)" = "$PI_VERSION"
|
|
|
|
ENV PATH="/opt/venv/bin:/usr/local/bin:$PATH" \
|
|
PI_VERSION="${PI_VERSION}" \
|
|
HOST=0.0.0.0 PORT=8787 \
|
|
THT_HARNESS_DIR=/app/harness \
|
|
THT_BIN=/opt/venv/bin/tht \
|
|
PI_BIN=pi \
|
|
HOME=/home/thoth
|
|
|
|
COPY scripts/verify-line-endings.sh /usr/local/bin/verify-line-endings
|
|
COPY docker/core-entrypoint.sh docker/workspace-maintenance-entrypoint.sh docker/session-migrate.sh docker/ensure-pi-trust.mjs docker/embedding-model-init.sh /app/docker/
|
|
COPY docker/smoke/core-smoke.sh /app/docker/smoke/core-smoke.sh
|
|
RUN /usr/local/bin/verify-line-endings /app/docker \
|
|
&& chmod +x /app/docker/core-entrypoint.sh /app/docker/workspace-maintenance-entrypoint.sh /app/docker/session-migrate.sh /app/docker/embedding-model-init.sh /app/docker/smoke/core-smoke.sh
|
|
|
|
WORKDIR /app/backend
|
|
USER thoth
|
|
EXPOSE 8787
|
|
HEALTHCHECK --interval=15s --timeout=3s --retries=5 --start-period=30s \
|
|
CMD curl -fsS http://127.0.0.1:8787/health || exit 1
|
|
ENTRYPOINT ["/usr/bin/tini","--","/app/docker/core-entrypoint.sh"]
|
|
CMD ["server"]
|