Migrate to uv workspace with proper package dependencies and optimized Docker builds#12
Conversation
Co-authored-by: DiTo97 <40361744+DiTo97@users.noreply.github.com>
Co-authored-by: DiTo97 <40361744+DiTo97@users.noreply.github.com>
Co-authored-by: DiTo97 <40361744+DiTo97@users.noreply.github.com>
|
following the content of this blog post https://ledkvu.com/posts/adapt-dockerfile-with-uv/ and the related multi-stage Dockerfile builds with uv ARG PYTHON_IMAGE=python:3.12-slim
FROM ghcr.io/astral-sh/uv:latest AS uv-base
# 1. Generator stage: generate requirements files
FROM ${PYTHON_IMAGE} AS generator
# Configure uv settings
ENV UV_LOCKED=1
WORKDIR /packages
# Generate requirements files for each group
RUN --mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
--mount=from=uv-base,source=/uv,target=/bin/uv \
uv pip compile --emit-index-url --group heavy-rarely-updated -o requirements-heavy.txt \
&& uv pip compile --emit-index-url --group light-frequently-updated -o requirements-light.txt
# 2. Final image without uv
FROM ${PYTHON_IMAGE}
RUN apt-get update \
&& apt-get install -y --no-install-recommends gcc \
&& rm -rf /var/lib/apt/lists/*
ENV UV_SYSTEM_PYTHON=1 \
UV_LINK_MODE=copy
WORKDIR /app
# LAYER 1: heavy dependencies
RUN --mount=from=uv-base,source=/uv,target=/bin/uv \
--mount=type=cache,target=/root/.cache/uv \
--mount=from=generator,source=/packages/requirements-heavy.txt,target=/app/requirements-heavy.txt \
uv pip install -r requirements-heavy.txt
# LAYER 2: light dependencies
RUN --mount=from=uv-base,source=/uv,target=/bin/uv \
--mount=type=cache,target=/root/.cache/uv \
--mount=from=generator,source=/packages/requirements-light.txt,target=/app/requirements-light.txt \
uv pip install -r requirements-light.txt
# LAYER 3: source code
COPY ./src ./src
EXPOSE 8000
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]and # An example using multi-stage image builds to create a final image without uv.
# First, build the application in the `/app` directory.
# See `Dockerfile` for details.
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
# Omit development dependencies
ENV UV_NO_DEV=1
# Disable Python downloads, because we want to use the system interpreter
# across both images. If using a managed Python version, it needs to be
# copied from the build image into the final image; see `standalone.Dockerfile`
# for an example.
ENV UV_PYTHON_DOWNLOADS=0
WORKDIR /app
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --locked --no-install-project
COPY . /app
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked
# Then, use a final image without uv
FROM python:3.12-slim-bookworm
# It is important to use the image that matches the builder, as the path to the
# Python executable must be the same, e.g., using `python:3.11-slim-bookworm`
# will fail.
# Setup a non-root user
RUN groupadd --system --gid 999 nonroot \
&& useradd --system --gid 999 --uid 999 --create-home nonroot
# Copy the application from the builder
COPY --from=builder --chown=nonroot:nonroot /app /app
# Place executables in the environment at the front of the path
ENV PATH="/app/.venv/bin:$PATH"
# Use the non-root user to run our application
USER nonroot
# Use `/app` as the working directory
WORKDIR /app
# Run the FastAPI application by default
CMD ["fastapi", "run", "--host", "0.0.0.0", "src/uv_docker_example"]make sure our Dockerfile is as efficient as those best practices suggest in terms of layer caching, build time, push and pull. |
Co-authored-by: DiTo97 <40361744+DiTo97@users.noreply.github.com>
Updated the Dockerfile to follow uv best practices with multi-stage builds and optimized layer caching (commit bb4908e). Key improvements:
Performance gains:
The implementation matches the examples from the blog post, with dependency installation separated into its own cached layer. |
Co-authored-by: DiTo97 <40361744+DiTo97@users.noreply.github.com>
|
@copilot This is the error log I got trying to build the optimized Dockerfile """
Dockerfile:2221 | # First, install dependencies (this layer is cached unless lock file changes)
|
…bility Co-authored-by: DiTo97 <40361744+DiTo97@users.noreply.github.com>
Fixed the lockfile error in commit 5812941. Root cause: The Solution: Copy all workspace package # Copy workspace configuration and package definitions
COPY pyproject.toml uv.lock ./
COPY packages/gptzero/pyproject.toml ./packages/gptzero/
COPY packages/gptzero-sdk/pyproject.toml ./packages/gptzero-sdk/
COPY packages/gptzero-api/pyproject.toml ./packages/gptzero-api/
COPY packages/gptzero-service/pyproject.toml ./packages/gptzero-service/
# Install dependencies (cached unless configs change)
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --no-install-project --all-packagesThis maintains optimal layer caching (dependencies separate from source code) while providing uv with the workspace structure it needs. |
|
@copilot Dockerfile is working great now. I fixed it myself. The problem seems to be shifted to the jobs in the GitHub Actions workflow, as they are failing saying: """ and """ |
…t and ruff Co-authored-by: DiTo97 <40361744+DiTo97@users.noreply.github.com>
Fixed the GitHub Actions workflow failures in commit 7608548. Root cause: The Solution: Added - name: Install dependencies
run: uv sync --all-packages --group devNow all dev tools will be available when running tests and linting in CI. |
Refactor to uv Package Manager ✅
Summary
Successfully migrated GPTZero-V to use uv package manager with proper workspace configuration and optimized Docker builds.
Completed Tasks
Latest Fix (CI Failures)
Issue: GitHub Actions jobs were failing with:
error: Failed to spawn: ruff(No such file or directory)error: Failed to spawn: pytest(No such file or directory)Root Cause: The
devdependency group containingpytest,ruff, etc. was not being installed during CI runs. The workflow useduv sync --all-packagesbut didn't include the--group devflag.Solution: Added
--group devflag to all workflow jobs:This ensures dev tools like
pytestandruffare available when running tests and linting.Performance Improvements
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.