mirror of
https://github.com/volcengine/OpenViking.git
synced 2026-09-28 19:53:23 +08:00
140 lines
6.6 KiB
Docker
140 lines
6.6 KiB
Docker
# syntax=docker/dockerfile:1.9
|
|
|
|
# Stage 1: provide Rust toolchain (required by setup.py -> build_ov_cli_artifact -> cargo build)
|
|
# ragfs-python's default S3-enabled dependency set currently requires rustc >= 1.91.1.
|
|
FROM rust:1.91.1-trixie AS rust-toolchain
|
|
|
|
# Stage 2: build Studio separately so npm failures stop the Docker build.
|
|
FROM node:24-trixie-slim AS web-studio-builder
|
|
ARG TARGETPLATFORM
|
|
WORKDIR /app/web-studio
|
|
|
|
# Keep npm install cached when only Studio sources change.
|
|
COPY web-studio/package.json web-studio/package-lock.json ./
|
|
RUN --mount=type=cache,target=/root/.npm,id=npm-${TARGETPLATFORM} npm ci
|
|
COPY web-studio/ ./
|
|
RUN npm run build -- --base=/studio/ \
|
|
&& test -f dist/index.html
|
|
|
|
# Stage 3: build Python environment with uv (builds Rust CLI + C++ extension)
|
|
FROM ghcr.io/astral-sh/uv:python3.13-trixie-slim AS py-builder
|
|
|
|
# Reuse Rust toolchain from stage 1 so setup.py can compile ov CLI in-place.
|
|
COPY --from=rust-toolchain /usr/local/cargo /usr/local/cargo
|
|
COPY --from=rust-toolchain /usr/local/rustup /usr/local/rustup
|
|
ENV CARGO_HOME=/usr/local/cargo
|
|
ENV RUSTUP_HOME=/usr/local/rustup
|
|
ENV PATH="/app/.venv/bin:/usr/local/cargo/bin:${PATH}"
|
|
ARG OPENVIKING_VERSION=
|
|
ARG TARGETPLATFORM
|
|
ARG UV_LOCK_STRATEGY=auto
|
|
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
build-essential \
|
|
ccache \
|
|
cmake \
|
|
git \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Route gcc/g++/cc through ccache so cmake (which asks shutil.which("gcc")) picks
|
|
# up /usr/lib/ccache/gcc and benefits from the BuildKit cache mount on /root/.ccache.
|
|
ENV PATH="/usr/lib/ccache:${PATH}"
|
|
ENV CCACHE_DIR=/root/.ccache
|
|
# Pin Cargo's target dir to a stable path so a BuildKit cache mount can persist
|
|
# build artifacts across layer reruns even when uv builds the wheel in an
|
|
# ephemeral isolated tempdir.
|
|
ENV CARGO_TARGET_DIR=/cargo-target
|
|
|
|
ENV UV_COMPILE_BYTECODE=1
|
|
ENV UV_LINK_MODE=copy
|
|
ENV UV_NO_DEV=1
|
|
WORKDIR /app
|
|
|
|
# Copy source required for setup.py artifact builds and native extension build.
|
|
COPY Cargo.toml Cargo.lock ./
|
|
COPY pyproject.toml uv.lock setup.py README.md ./
|
|
COPY build_support/ build_support/
|
|
COPY bot/ bot/
|
|
COPY crates/ crates/
|
|
COPY openviking/ openviking/
|
|
COPY --from=web-studio-builder /app/web-studio/dist/ openviking/web_studio/dist/
|
|
COPY openviking_cli/ openviking_cli/
|
|
COPY src/ src/
|
|
COPY third_party/ third_party/
|
|
|
|
# Install project and dependencies. setup.py packages the copied Studio bundle
|
|
# without running npm again, while build_ext still builds native extensions.
|
|
# Default to auto-refreshing uv.lock inside the ephemeral build context when it is
|
|
# stale, so Docker builds stay unblocked after dependency changes. Set
|
|
# UV_LOCK_STRATEGY=locked to keep fail-fast reproducibility checks.
|
|
RUN --mount=type=cache,target=/root/.cache/uv,id=uv-${TARGETPLATFORM} \
|
|
--mount=type=cache,target=/cargo-target,id=cargo-target-${TARGETPLATFORM} \
|
|
--mount=type=cache,target=/usr/local/cargo/registry,id=cargo-registry-${TARGETPLATFORM} \
|
|
--mount=type=cache,target=/usr/local/cargo/git,id=cargo-git-${TARGETPLATFORM} \
|
|
--mount=type=cache,target=/root/.ccache,id=ccache-${TARGETPLATFORM} \
|
|
if [ -n "${OPENVIKING_VERSION:-}" ]; then \
|
|
export SETUPTOOLS_SCM_PRETEND_VERSION_FOR_OPENVIKING="${OPENVIKING_VERSION}"; \
|
|
elif [ -f openviking/_version.py ]; then \
|
|
export SETUPTOOLS_SCM_PRETEND_VERSION_FOR_OPENVIKING="$(python -c "import runpy; print(runpy.run_path('openviking/_version.py')['version'])")"; \
|
|
else \
|
|
echo "OPENVIKING_VERSION build arg is required when building without openviking/_version.py" >&2; \
|
|
exit 2; \
|
|
fi; \
|
|
case "${UV_LOCK_STRATEGY}" in \
|
|
locked) \
|
|
uv sync --locked --no-editable --reinstall-package openviking --extra bot --extra gemini \
|
|
;; \
|
|
auto) \
|
|
if ! uv lock --check; then \
|
|
uv lock; \
|
|
fi; \
|
|
uv sync --locked --no-editable --reinstall-package openviking --extra bot --extra gemini \
|
|
;; \
|
|
*) \
|
|
echo "Unsupported UV_LOCK_STRATEGY: ${UV_LOCK_STRATEGY}" >&2; \
|
|
exit 2 \
|
|
;; \
|
|
esac
|
|
|
|
# Stage 4: runtime
|
|
FROM python:3.13-slim-trixie
|
|
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
ca-certificates \
|
|
curl \
|
|
git \
|
|
libstdc++6 \
|
|
ripgrep \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
WORKDIR /app
|
|
|
|
COPY --from=py-builder /app/.venv /app/.venv
|
|
# Fail the image build if VikingBot and the separately released SDK drift apart.
|
|
RUN /app/.venv/bin/python -I -c "import inspect; from importlib.metadata import version; from openviking_sdk.client import AsyncHTTPClient; signature = inspect.signature(AsyncHTTPClient.get_skill); raise SystemExit(0 if 'include_integrity' in signature.parameters else f\"incompatible openviking-sdk {version('openviking-sdk')}: AsyncHTTPClient.get_skill{signature} lacks include_integrity\")"
|
|
RUN /app/.venv/bin/python -I -c "from importlib.util import find_spec; from pathlib import Path; spec = find_spec('openviking.web_studio'); locations = list(spec.submodule_search_locations or ()) if spec else []; root = Path('/app/.venv').resolve(); p = (Path(locations[0]).resolve() / 'dist/index.html').resolve() if len(locations) == 1 else None; valid = p is not None and p.is_file() and p.is_relative_to(root); raise SystemExit(0 if valid else f'missing or misplaced Studio bundle: spec_found={spec is not None}, locations={locations!r}, resource={p}')"
|
|
COPY docker/openviking-entrypoint.sh /usr/local/bin/openviking-entrypoint
|
|
COPY docker/pending_health_server.py /usr/local/bin/openviking-pending-health
|
|
RUN mkdir -p /app/.openviking \
|
|
&& sed -i 's/\r$//' /usr/local/bin/openviking-entrypoint /usr/local/bin/openviking-pending-health \
|
|
&& chmod +x /usr/local/bin/openviking-entrypoint /usr/local/bin/openviking-pending-health
|
|
ENV HOME="/app" \
|
|
PATH="/app/.venv/bin:$PATH" \
|
|
OPENVIKING_CONFIG_FILE="/app/.openviking/ov.conf" \
|
|
OPENVIKING_CLI_CONFIG_FILE="/app/.openviking/ovcli.conf"
|
|
|
|
EXPOSE 1933
|
|
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
|
|
CMD ["openviking-entrypoint", "--healthcheck"]
|
|
|
|
# All persistent state (ov.conf, ovcli.conf, workspace data) lives under
|
|
# /app/.openviking, which mirrors the host's ~/.openviking layout. Mount one
|
|
# volume there to persist everything across container restarts:
|
|
# docker run -v ~/.openviking:/app/.openviking <image>
|
|
# If ov.conf is absent on first start, set OPENVIKING_CONF_CONTENT to the full
|
|
# JSON, or `docker exec` in and run `openviking-server init`.
|
|
# Override command to run CLI, e.g.:
|
|
# docker run --rm -v ~/.openviking:/app/.openviking <image> openviking --help
|
|
ENTRYPOINT ["openviking-entrypoint"]
|