Spaces:
Build error
Build error
tudragon154203 Claude Happy commited on
Commit ·
172e5d3
1
Parent(s): 79aa9eb
build(docker): fix Rust extension build in container
Browse filesAdds Rust toolchain, libssl-dev, and pkg-config to builder stage.
Updates Dockerfile to build extension via script and copy binary
to runtime image, ensuring 'headroom._core' availability.
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Dockerfile +19 -3
- scripts/build_rust_extension.sh +27 -24
Dockerfile
CHANGED
|
@@ -11,14 +11,22 @@ ARG PYTHON_SITE_PACKAGES=/usr/local/lib/python${PYTHON_VERSION}/site-packages
|
|
| 11 |
FROM python:${PYTHON_VERSION}-slim@${PYTHON_DIGEST} AS builder
|
| 12 |
|
| 13 |
ARG UV_VERSION
|
|
|
|
| 14 |
|
| 15 |
RUN apt-get update && \
|
| 16 |
apt-get install -y --no-install-recommends \
|
| 17 |
build-essential \
|
| 18 |
g++ \
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
&& rm -rf /var/lib/apt/lists/*
|
| 20 |
|
| 21 |
-
RUN
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
WORKDIR /build
|
| 24 |
|
|
@@ -30,10 +38,16 @@ ARG HEADROOM_EXTRAS=proxy,code,ml
|
|
| 30 |
RUN --mount=type=cache,target=/root/.cache/uv \
|
| 31 |
uv pip install --system ".[${HEADROOM_EXTRAS}]"
|
| 32 |
|
| 33 |
-
# Layer 2: copy real source, reinstall only headroom-ai (no deps)
|
|
|
|
| 34 |
COPY headroom/ headroom/
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
RUN --mount=type=cache,target=/root/.cache/uv \
|
| 36 |
-
uv pip install --system --no-deps --reinstall-package headroom-ai .
|
|
|
|
| 37 |
|
| 38 |
# ---- Runtime stage (python-slim): supports root/nonroot via build arg ----
|
| 39 |
FROM python:${PYTHON_VERSION}-slim@${PYTHON_DIGEST} AS runtime-slim-base
|
|
@@ -46,6 +60,8 @@ RUN apt-get update && \
|
|
| 46 |
rm -rf /var/lib/apt/lists/*
|
| 47 |
|
| 48 |
COPY --from=builder ${PYTHON_SITE_PACKAGES} ${PYTHON_SITE_PACKAGES}
|
|
|
|
|
|
|
| 49 |
COPY --from=builder /usr/local/bin/headroom /usr/local/bin/headroom
|
| 50 |
|
| 51 |
RUN mkdir -p /home/nonroot /data && \
|
|
|
|
| 11 |
FROM python:${PYTHON_VERSION}-slim@${PYTHON_DIGEST} AS builder
|
| 12 |
|
| 13 |
ARG UV_VERSION
|
| 14 |
+
ARG PYTHON_SITE_PACKAGES
|
| 15 |
|
| 16 |
RUN apt-get update && \
|
| 17 |
apt-get install -y --no-install-recommends \
|
| 18 |
build-essential \
|
| 19 |
g++ \
|
| 20 |
+
curl \
|
| 21 |
+
libssl-dev \
|
| 22 |
+
pkg-config \
|
| 23 |
+
unzip \
|
| 24 |
&& rm -rf /var/lib/apt/lists/*
|
| 25 |
|
| 26 |
+
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --profile minimal --default-toolchain stable
|
| 27 |
+
ENV PATH="/root/.cargo/bin:${PATH}"
|
| 28 |
+
|
| 29 |
+
RUN python -m pip install --no-cache-dir uv==${UV_VERSION} maturin
|
| 30 |
|
| 31 |
WORKDIR /build
|
| 32 |
|
|
|
|
| 38 |
RUN --mount=type=cache,target=/root/.cache/uv \
|
| 39 |
uv pip install --system ".[${HEADROOM_EXTRAS}]"
|
| 40 |
|
| 41 |
+
# Layer 2: copy real source, reinstall only headroom-ai (no deps), then build
|
| 42 |
+
# the Rust extension via maturin build (no venv needed — uses system Python).
|
| 43 |
COPY headroom/ headroom/
|
| 44 |
+
COPY Cargo.toml Cargo.lock ./
|
| 45 |
+
COPY crates/ crates/
|
| 46 |
+
COPY scripts/build_rust_extension.sh scripts/build_rust_extension.sh
|
| 47 |
+
COPY .cargo/ .cargo/
|
| 48 |
RUN --mount=type=cache,target=/root/.cache/uv \
|
| 49 |
+
uv pip install --system --no-deps --reinstall-package headroom-ai . && \
|
| 50 |
+
bash ./scripts/build_rust_extension.sh
|
| 51 |
|
| 52 |
# ---- Runtime stage (python-slim): supports root/nonroot via build arg ----
|
| 53 |
FROM python:${PYTHON_VERSION}-slim@${PYTHON_DIGEST} AS runtime-slim-base
|
|
|
|
| 60 |
rm -rf /var/lib/apt/lists/*
|
| 61 |
|
| 62 |
COPY --from=builder ${PYTHON_SITE_PACKAGES} ${PYTHON_SITE_PACKAGES}
|
| 63 |
+
COPY --from=builder /build/headroom/_core*.so /build/headroom/_core*.pyd /build/headroom/_core*.dylib /src/headroom/ 2>/dev/null || true
|
| 64 |
+
COPY --from=builder /build/headroom/_core*.so /build/headroom/_core*.pyd /build/headroom/_core*.dylib /usr/local/lib/python${PYTHON_VERSION}/site-packages/headroom/ 2>/dev/null || true
|
| 65 |
COPY --from=builder /usr/local/bin/headroom /usr/local/bin/headroom
|
| 66 |
|
| 67 |
RUN mkdir -p /home/nonroot /data && \
|
scripts/build_rust_extension.sh
CHANGED
|
@@ -1,48 +1,51 @@
|
|
| 1 |
#!/usr/bin/env bash
|
| 2 |
-
# Build
|
| 3 |
-
#
|
| 4 |
#
|
| 5 |
-
#
|
| 6 |
-
#
|
| 7 |
-
#
|
| 8 |
-
#
|
| 9 |
-
# fails. Copying the built binary into `headroom/` fixes the lookup with zero
|
| 10 |
-
# symlinks and works on Windows.
|
| 11 |
#
|
| 12 |
-
# Idempotent. Safe to run repeatedly. Requires `maturin` in PATH
|
| 13 |
-
# inside the project venv).
|
| 14 |
|
| 15 |
set -euo pipefail
|
| 16 |
|
| 17 |
cd "$(dirname "$0")/.."
|
| 18 |
|
| 19 |
if ! command -v maturin >/dev/null 2>&1; then
|
| 20 |
-
echo "error: maturin not found.
|
| 21 |
-
echo " source .venv/bin/activate (Unix)" >&2
|
| 22 |
-
echo " .venv\\Scripts\\activate (Windows)" >&2
|
| 23 |
exit 1
|
| 24 |
fi
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
-name "_core.cpython-*.so" -o -name "_core.cpython-*.dylib" -o -name "_core.pyd" \
|
| 33 |
2>/dev/null | head -1)
|
| 34 |
|
| 35 |
if [[ -z "$EXT_FILE" ]]; then
|
| 36 |
-
echo "error:
|
|
|
|
| 37 |
exit 1
|
| 38 |
fi
|
| 39 |
|
| 40 |
-
# Copy into the in-tree package dir. Uses cp instead of ln -sf because
|
| 41 |
-
# Windows (MSYS2/Git Bash) lacks reliable symlink support and requires
|
| 42 |
-
# elevated privileges for native symlinks.
|
| 43 |
DEST="headroom/$(basename "$EXT_FILE")"
|
| 44 |
cp -f "$EXT_FILE" "$DEST"
|
| 45 |
-
|
|
|
|
| 46 |
|
| 47 |
-
# Smoke-test the import to fail loudly if anything is misconfigured.
|
| 48 |
python -c "from headroom._core import DiffCompressor; print('headroom._core OK:', DiffCompressor)"
|
|
|
|
| 1 |
#!/usr/bin/env bash
|
| 2 |
+
# Build Rust → Python extension (headroom._core) and copy it into in-tree
|
| 3 |
+
# `headroom/` so `import headroom._core` resolves.
|
| 4 |
#
|
| 5 |
+
# `maturin build` works without venv activation, so this script runs inside
|
| 6 |
+
# Docker and on local shells. It builds a wheel, extracts the compiled
|
| 7 |
+
# extension, and copies it into `headroom/` to beat `pip install -e .` path
|
| 8 |
+
# shadowing.
|
|
|
|
|
|
|
| 9 |
#
|
| 10 |
+
# Idempotent. Safe to run repeatedly. Requires `maturin` and `python` in PATH.
|
|
|
|
| 11 |
|
| 12 |
set -euo pipefail
|
| 13 |
|
| 14 |
cd "$(dirname "$0")/.."
|
| 15 |
|
| 16 |
if ! command -v maturin >/dev/null 2>&1; then
|
| 17 |
+
echo "error: maturin not found. Install it (e.g. pip install maturin)." >&2
|
|
|
|
|
|
|
| 18 |
exit 1
|
| 19 |
fi
|
| 20 |
|
| 21 |
+
maturin build --release --interpreter python --out target/wheels \
|
| 22 |
+
--manifest-path crates/headroom-py/Cargo.toml
|
| 23 |
|
| 24 |
+
WHEEL=$(find target/wheels -name "*.whl" -type f 2>/dev/null | head -1)
|
| 25 |
+
|
| 26 |
+
if [[ -z "$WHEEL" ]]; then
|
| 27 |
+
echo "error: maturin build succeeded but produced no wheel." >&2
|
| 28 |
+
exit 1
|
| 29 |
+
fi
|
| 30 |
+
|
| 31 |
+
echo "Built wheel: $WHEEL"
|
| 32 |
+
|
| 33 |
+
TMPDIR=$(mktemp -d)
|
| 34 |
+
unzip -qo "$WHEEL" -d "$TMPDIR"
|
| 35 |
+
|
| 36 |
+
EXT_FILE=$(find "$TMPDIR/headroom" -maxdepth 1 \
|
| 37 |
-name "_core.cpython-*.so" -o -name "_core.cpython-*.dylib" -o -name "_core.pyd" \
|
| 38 |
2>/dev/null | head -1)
|
| 39 |
|
| 40 |
if [[ -z "$EXT_FILE" ]]; then
|
| 41 |
+
echo "error: wheel did not contain headroom/_core.* binary." >&2
|
| 42 |
+
rm -rf "$TMPDIR"
|
| 43 |
exit 1
|
| 44 |
fi
|
| 45 |
|
|
|
|
|
|
|
|
|
|
| 46 |
DEST="headroom/$(basename "$EXT_FILE")"
|
| 47 |
cp -f "$EXT_FILE" "$DEST"
|
| 48 |
+
rm -rf "$TMPDIR"
|
| 49 |
+
echo "installed: $DEST (from $WHEEL)"
|
| 50 |
|
|
|
|
| 51 |
python -c "from headroom._core import DiffCompressor; print('headroom._core OK:', DiffCompressor)"
|