@@ -43,6 +43,9 @@ RUN NODE_ENV=production npm run build
4343# We'll build a light-weight layer along the way with just docs stuff
4444FROM python:${PYTHON_IMAGE_VERSION} AS docs
4545
46+ # Pull the uv binary in so we can use it as a faster pip / pip-compile.
47+ COPY --from=ghcr.io/astral-sh/uv:0.11.7 /uv /uvx /usr/local/bin/
48+
4649# By default, Docker has special steps to avoid keeping APT caches in the layers, which
4750# is good, but in our case, we're going to mount a special cache volume (kept between
4851# builds), so we WANT the cache to persist.
@@ -68,36 +71,33 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
6871 && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
6972
7073# We create an /opt directory with a virtual environment in it to store our
71- # application in.
74+ # application in. uv picks this up via VIRTUAL_ENV below.
7275RUN set -x \
7376 && python3 -m venv /opt/warehouse
7477
75- # Now that we've created our virtual environment, we'll go ahead and update
76- # our $PATH to refer to it first.
78+ # Point uv (and shells) at the venv we just created.
79+ # Hynek-recommended uv tuning (see https://hynek.me/articles/docker-uv/):
80+ # UV_LINK_MODE=copy — silence hardlink-across-FS warnings (cache mount)
81+ # UV_COMPILE_BYTECODE=1 — pay the compile cost once, at build time
82+ # UV_PYTHON_DOWNLOADS=never — never auto-fetch a Python; use the system one
83+ # UV_PYTHON — pin uv to the venv's interpreter
84+ ENV VIRTUAL_ENV="/opt/warehouse" \
85+ UV_LINK_MODE=copy \
86+ UV_COMPILE_BYTECODE=1 \
87+ UV_PYTHON_DOWNLOADS=never \
88+ UV_PYTHON="/opt/warehouse/bin/python"
7789ENV PATH="/opt/warehouse/bin:${PATH}"
7890
79- # Next, we want to update pip inside of this virtual
80- # environment to ensure that we have the latest version.
81- RUN pip --no-cache-dir --disable-pip-version-check install --upgrade pip
82-
83- # We copy this into the docker container prior to copying in the rest of our
84- # application so that we can skip installing requirements if the only thing
85- # that has changed is the Warehouse code itself.
86- COPY requirements /tmp/requirements
87-
88- # Install the Python level Warehouse requirements, this is done after copying
89- # the requirements but prior to copying Warehouse itself into the container so
90- # that code changes don't require triggering an entire install of all of
91- # Warehouse's dependencies.
92- RUN --mount=type=cache,target=/root/.cache/pip \
91+ # Install the Python level Warehouse requirements. requirements/ is bind-mounted
92+ # rather than COPY'd to keep it out of the layer.
93+ RUN --mount=type=cache,target=/root/.cache/uv \
94+ --mount=type=bind,source=requirements,target=/tmp/requirements \
9395 set -x \
94- && pip --disable-pip-version-check \
95- install --no-deps --only-binary :all: \
96+ && uv pip install --no-deps --only-binary :all: \
9697 -r /tmp/requirements/docs-dev.txt \
9798 -r /tmp/requirements/docs-user.txt \
9899 -r /tmp/requirements/docs-blog.txt \
99- && pip check \
100- && find /opt/warehouse -name '*.pyc' -delete
100+ && uv pip check
101101
102102WORKDIR /opt/warehouse/src/
103103
@@ -117,6 +117,9 @@ USER docs
117117# image that it gets deployed into.
118118FROM python:${PYTHON_IMAGE_VERSION} AS build
119119
120+ # Pull the uv binary in so we can use it as a faster pip / pip-compile.
121+ COPY --from=ghcr.io/astral-sh/uv:0.11.7 /uv /uvx /usr/local/bin/
122+
120123# Define whether we're building a production or a development image. This will
121124# generally be used to control whether or not we install our development and
122125# test dependencies.
@@ -132,47 +135,42 @@ ARG CI=no
132135ARG IPYTHON=no
133136
134137# We create an /opt directory with a virtual environment in it to store our
135- # application in.
138+ # application in. uv picks this up via VIRTUAL_ENV below.
136139RUN set -x \
137140 && python3 -m venv /opt/warehouse
138141
139- # Now that we've created our virtual environment, we'll go ahead and update
140- # our $PATH to refer to it first.
142+ # Point uv (and shells) at the venv we just created.
143+ # See the docs stage above for the rationale behind each UV_* variable.
144+ ENV VIRTUAL_ENV="/opt/warehouse" \
145+ UV_LINK_MODE=copy \
146+ UV_COMPILE_BYTECODE=1 \
147+ UV_PYTHON_DOWNLOADS=never \
148+ UV_PYTHON="/opt/warehouse/bin/python"
141149ENV PATH="/opt/warehouse/bin:${PATH}"
142150
143- # Next, we want to update pip inside of this virtual
144- # environment to ensure that we have the latest version.
145- RUN pip --no-cache-dir --disable-pip-version-check install --upgrade pip
146-
147- # We copy this into the docker container prior to copying in the rest of our
148- # application so that we can skip installing requirements if the only thing
149- # that has changed is the Warehouse code itself.
150- COPY requirements /tmp/requirements
151-
152151# Install our development dependencies if we're building a development install
153- # otherwise this will do nothing.
154- RUN --mount=type=cache,target=/root/.cache/pip \
152+ # otherwise this will do nothing. requirements/ is bind-mounted to stay out of
153+ # the layer.
154+ RUN --mount=type=cache,target=/root/.cache/uv \
155+ --mount=type=bind,source=requirements,target=/tmp/requirements \
155156 set -x \
156- && if [ "$DEVEL" = "yes" ]; then pip --disable- pip-version-check install -r /tmp/requirements/dev.txt; fi
157+ && if [ "$DEVEL" = "yes" ]; then uv pip install -r /tmp/requirements/dev.txt; fi
157158
158- RUN --mount=type=cache,target=/root/.cache/pip \
159+ RUN --mount=type=cache,target=/root/.cache/uv \
160+ --mount=type=bind,source=requirements,target=/tmp/requirements \
159161 set -x \
160- && if [ "$DEVEL" = "yes" ] && [ "$IPYTHON" = "yes" ]; then pip --disable- pip-version-check install -r /tmp/requirements/ipython.txt; fi
162+ && if [ "$DEVEL" = "yes" ] && [ "$IPYTHON" = "yes" ]; then uv pip install -r /tmp/requirements/ipython.txt; fi
161163
162- # Install the Python level Warehouse requirements, this is done after copying
163- # the requirements but prior to copying Warehouse itself into the container so
164- # that code changes don't require triggering an entire install of all of
165- # Warehouse's dependencies.
166- RUN --mount=type=cache,target=/root/.cache/pip \
164+ # Install the Python level Warehouse requirements.
165+ RUN --mount=type=cache,target=/root/.cache/uv \
166+ --mount=type=bind,source=requirements,target=/tmp/requirements \
167167 set -x \
168- && pip --disable-pip-version-check \
169- install --no-deps --only-binary :all: \
168+ && uv pip install --no-deps --only-binary :all: \
170169 -r /tmp/requirements/deploy.txt \
171170 -r /tmp/requirements/main.txt \
172171 $(if [ "$DEVEL" = "yes" ]; then echo '-r /tmp/requirements/tests.txt -r /tmp/requirements/lint.txt' ; fi) \
173172 $(if [ "$CI" = "yes" ]; then echo '-r /tmp/requirements/docs-dev.txt -r /tmp/requirements/docs-user.txt -r /tmp/requirements/docs-blog.txt' ; fi ) \
174- && pip check \
175- && find /opt/warehouse -name '*.pyc' -delete
173+ && uv pip check
176174
177175
178176
0 commit comments