Files
OpenShell/deploy/docker/Dockerfile.python-wheels
T
Piotr Mlocek 18fb7af4e2 perf(docker): move version ARG below cached layers to fix cache invalidation (#385)
The OPENSHELL_CARGO_VERSION build arg contains a git commit hash that
changes on every build (e.g. 0.0.7-dev.11+g085b131ae). Declaring this
ARG near the top of each Dockerfile invalidated every layer below it --
including expensive dependency installs, toolchain setup, and the Rust
dependency pre-build step -- on every single commit.

Move the ARG declaration to just before the RUN that actually uses it so
upstream layers stay cached. This recovers ~5-6 minutes per build on the
gateway image (from ~9m back toward ~2-3m) and similarly improves cluster,
CLI, and Python wheel builds.

Also removes unused OPENSHELL_IMAGE_TAG ARG from cli-macos, python-wheels,
and python-wheels-macos Dockerfiles.
2026-03-16 18:56:30 -07:00

105 lines
4.6 KiB
Docker

# syntax=docker/dockerfile:1.6
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
ARG PYTHON_VERSION=3.12
FROM --platform=$BUILDPLATFORM python:${PYTHON_VERSION}-slim AS base
ENV PATH="/root/.cargo/bin:${PATH}"
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
curl \
gcc \
libc6-dev \
pkg-config \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
RUN pip install --no-cache-dir maturin
COPY deploy/docker/cross-build.sh /usr/local/bin/
FROM base AS builder
ARG TARGETARCH
ARG BUILDARCH
ARG CARGO_TARGET_CACHE_SCOPE=default
ARG SCCACHE_MEMCACHED_ENDPOINT
WORKDIR /build
RUN . cross-build.sh && install_cross_toolchain && install_sccache && add_rust_target
# Copy dependency manifests first for better caching.
COPY Cargo.toml Cargo.lock ./
COPY crates/openshell-cli/Cargo.toml crates/openshell-cli/Cargo.toml
COPY crates/openshell-core/Cargo.toml crates/openshell-core/Cargo.toml
COPY crates/openshell-providers/Cargo.toml crates/openshell-providers/Cargo.toml
COPY crates/openshell-router/Cargo.toml crates/openshell-router/Cargo.toml
COPY crates/openshell-sandbox/Cargo.toml crates/openshell-sandbox/Cargo.toml
COPY crates/openshell-server/Cargo.toml crates/openshell-server/Cargo.toml
COPY crates/openshell-bootstrap/Cargo.toml crates/openshell-bootstrap/Cargo.toml
COPY crates/openshell-core/build.rs crates/openshell-core/build.rs
COPY proto/ proto/
# Create dummy source files to build dependencies.
RUN mkdir -p crates/openshell-cli/src crates/openshell-core/src crates/openshell-providers/src crates/openshell-router/src crates/openshell-sandbox/src crates/openshell-server/src crates/openshell-bootstrap/src && \
echo "fn main() {}" > crates/openshell-cli/src/main.rs && \
echo "fn main() {}" > crates/openshell-sandbox/src/main.rs && \
echo "fn main() {}" > crates/openshell-server/src/main.rs && \
touch crates/openshell-core/src/lib.rs && \
touch crates/openshell-providers/src/lib.rs && \
touch crates/openshell-router/src/lib.rs && \
touch crates/openshell-bootstrap/src/lib.rs
# Build dependencies only (cached unless Cargo.toml/lock changes).
# sccache uses memcached in CI or the local disk cache mount for local dev.
# The cargo-target mount gives cargo a persistent target/ dir for incremental rebuilds.
RUN --mount=type=cache,id=cargo-registry-python-wheels-${TARGETARCH},sharing=locked,target=/root/.cargo/registry \
--mount=type=cache,id=cargo-git-python-wheels-${TARGETARCH},sharing=locked,target=/root/.cargo/git \
--mount=type=cache,id=cargo-target-python-wheels-${TARGETARCH}-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \
--mount=type=cache,id=sccache-python-wheels-${TARGETARCH},sharing=locked,target=/tmp/sccache \
. cross-build.sh && cargo_cross_build --release -p openshell-cli 2>/dev/null || true
# Copy actual source code and Python packaging files.
COPY crates/ crates/
COPY pyproject.toml README.md ./
COPY python/ python/
# Touch source files to ensure they're rebuilt (not the cached dummy).
RUN touch crates/openshell-cli/src/main.rs \
crates/openshell-cli/src/lib.rs \
crates/openshell-bootstrap/src/lib.rs \
crates/openshell-core/src/lib.rs \
crates/openshell-providers/src/lib.rs \
crates/openshell-router/src/lib.rs \
crates/openshell-sandbox/src/main.rs \
crates/openshell-server/src/main.rs \
crates/openshell-core/build.rs \
proto/*.proto
# Declare version ARG here (not earlier) so the git-hash-bearing value does not
# invalidate the expensive dependency-build layers above on every commit.
ARG OPENSHELL_CARGO_VERSION
RUN --mount=type=cache,id=cargo-registry-python-wheels-${TARGETARCH},sharing=locked,target=/root/.cargo/registry \
--mount=type=cache,id=cargo-git-python-wheels-${TARGETARCH},sharing=locked,target=/root/.cargo/git \
--mount=type=cache,id=cargo-target-python-wheels-${TARGETARCH}-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \
--mount=type=cache,id=sccache-python-wheels-${TARGETARCH},sharing=locked,target=/tmp/sccache \
. cross-build.sh && \
export_cross_env && \
export RUSTC_WRAPPER=sccache && \
export CARGO_BUILD_TARGET="$(rust_target)" && \
if [ -n "${OPENSHELL_CARGO_VERSION:-}" ]; then \
sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${OPENSHELL_CARGO_VERSION}"'"/}' Cargo.toml; \
fi && \
maturin build --release --target "${CARGO_BUILD_TARGET}" --out /wheels
FROM scratch AS wheels
COPY --from=builder /wheels/*.whl /