From a13d8a909d5a43503204fa61aa25bbce2268224d Mon Sep 17 00:00:00 2001 From: Sourish Chakraborty <80025780+sourrris@users.noreply.github.com> Date: Mon, 1 Jun 2026 21:53:22 +0530 Subject: [PATCH] fix: copy package includes in Docker builds, incl. GPU (#450) * fix: copy package includes in Docker build * fix: copy package includes in GPU Docker builds too PR #450 fixed the CPU Dockerfile but Dockerfile.gpu and Dockerfile.gpu.rocm have the identical bug: they COPY src/ then `uv pip install ".[server]"` without copying the non-src force-include paths (scripts/install, deploy/windows), so hatchling's wheel build fails the same way on GPU images (#447). Adds the two COPY lines to both GPU Dockerfiles and generalizes the regression test to guard every wheel-building Dockerfile (CPU + both GPU variants) instead of only the CPU one. Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: Jon Saad-Falcon <41205309+jonsaadfalcon@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) --- deploy/docker/Dockerfile | 2 ++ deploy/docker/Dockerfile.gpu | 2 ++ deploy/docker/Dockerfile.gpu.rocm | 2 ++ tests/deployment/test_docker.py | 39 +++++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+) diff --git a/deploy/docker/Dockerfile b/deploy/docker/Dockerfile index 6e1f1e50..06cf6950 100644 --- a/deploy/docker/Dockerfile +++ b/deploy/docker/Dockerfile @@ -13,6 +13,8 @@ FROM python:3.12-slim-bookworm AS builder WORKDIR /app COPY pyproject.toml README.md ./ COPY src/ src/ +COPY scripts/install scripts/install +COPY deploy/windows deploy/windows # Copy built frontend into the server static directory COPY --from=frontend /src/openjarvis/server/static src/openjarvis/server/static/ diff --git a/deploy/docker/Dockerfile.gpu b/deploy/docker/Dockerfile.gpu index 56cf2047..ab1a2cba 100644 --- a/deploy/docker/Dockerfile.gpu +++ b/deploy/docker/Dockerfile.gpu @@ -17,6 +17,8 @@ RUN apt-get update && \ WORKDIR /app COPY pyproject.toml README.md ./ COPY src/ src/ +COPY scripts/install scripts/install +COPY deploy/windows deploy/windows COPY --from=frontend /src/openjarvis/server/static src/openjarvis/server/static/ diff --git a/deploy/docker/Dockerfile.gpu.rocm b/deploy/docker/Dockerfile.gpu.rocm index 55473ba4..52c67247 100644 --- a/deploy/docker/Dockerfile.gpu.rocm +++ b/deploy/docker/Dockerfile.gpu.rocm @@ -17,6 +17,8 @@ RUN apt-get update && \ WORKDIR /app COPY pyproject.toml README.md ./ COPY src/ src/ +COPY scripts/install scripts/install +COPY deploy/windows deploy/windows COPY --from=frontend /src/openjarvis/server/static src/openjarvis/server/static/ diff --git a/tests/deployment/test_docker.py b/tests/deployment/test_docker.py index ccc3c96f..72396a85 100644 --- a/tests/deployment/test_docker.py +++ b/tests/deployment/test_docker.py @@ -4,6 +4,11 @@ from __future__ import annotations from pathlib import Path +try: + import tomllib +except ModuleNotFoundError: # pragma: no cover - Python < 3.11 + import tomli as tomllib + ROOT = Path(__file__).resolve().parent.parent.parent DOCKER_DIR = ROOT / "deploy" / "docker" @@ -20,6 +25,40 @@ class TestDockerFiles: assert "ENTRYPOINT" in content assert "jarvis" in content + def test_dockerfile_copies_forced_package_includes(self): + # Every Dockerfile that builds the wheel from an explicit `COPY src/` + # context (rather than `COPY . .`) must also copy the non-src + # force-include paths before installing, or hatchling's wheel build + # fails (see #447). Guard ALL such Dockerfiles, not just the CPU one, + # so the GPU variants can't silently regress. + project = tomllib.loads((ROOT / "pyproject.toml").read_text()) + force_include = project["tool"]["hatch"]["build"]["targets"]["wheel"][ + "force-include" + ] + non_src_includes = [s for s in force_include if not s.startswith("src/")] + + install_marker = 'uv pip install --system ".[server]"' + wheel_dockerfiles = [ + p + for p in sorted(DOCKER_DIR.glob("Dockerfile*")) + if install_marker in p.read_text() and "COPY src/ src/" in p.read_text() + ] + # Sanity: we actually found the wheel-building Dockerfiles to guard. + assert wheel_dockerfiles, "no wheel-building Dockerfiles found to check" + + for dockerfile in wheel_dockerfiles: + content = dockerfile.read_text() + install_step = content.index(install_marker) + for source in non_src_includes: + copy_marker = f"COPY {source} " + assert copy_marker in content, ( + f"{dockerfile.name} is missing '{copy_marker.strip()}' " + f"(a non-src force-include path)" + ) + assert content.index(copy_marker) < install_step, ( + f"{dockerfile.name} copies '{source}' after the install step" + ) + def test_docker_compose_valid_yaml(self): import importlib