-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile.j2
More file actions
156 lines (125 loc) · 5.88 KB
/
Copy pathDockerfile.j2
File metadata and controls
156 lines (125 loc) · 5.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# syntax=docker/dockerfile:1
# It is wise to use the Python version you are developing with and not blindly choose
# the latest. The `build-image` task passes the version properly here
ARG PYTHON_VERSION=3
# Variant for the runtime image, e.g. slim, alpine, etc.
# See https://hub.docker.com/_/python for available variants. Leave empty for no variant.
ARG PYTHON_VARIANT=slim
FROM python:${PYTHON_VERSION} AS builder
ARG PACKAGE_VERSION
ARG UV_VERSION
# Bypass VCS-based version detection: use the host-computed version directly.
# This avoids requiring a .git directory in the build context and prevents
# false dirty-version detection from a partial working tree.
ENV UV_DYNAMIC_VERSIONING_BYPASS=${PACKAGE_VERSION}
# Install uv for package build and dependency export steps
RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache{{ CACHE_ID_SUFFIX }} \
sh -c 'if [ -n "${UV_VERSION:-}" ]; then \
pip install --root-user-action=ignore "uv==$UV_VERSION"; \
else \
pip install --root-user-action=ignore uv; \
fi'
# Build package
WORKDIR /tmp/build
COPY . /tmp/build/
{% if HAS_DEBUG_DEPS %}
# Export debug requirements when the project defines a debug dependency group
RUN --mount=type=cache,target=/root/.cache/uv,id=uv-cache{{ CACHE_ID_SUFFIX }}{% for mount in UV_INDEX_SECRET_MOUNTS %} \
--mount={{ mount }}{% endfor %} \
uv export --group debug --no-hashes --format requirements-txt --output-file requirements-debug.txt
{% endif %}
# Build the wheel, caching the uv cache directory to speed up subsequent builds.
# If running this in a CI system, you must be using a persistent builder to take
# advantage of the cache mount, and you should configure your CI to persist the
# cache directory between builds
RUN --mount=type=cache,target=/root/.cache/uv,id=uv-cache{{ CACHE_ID_SUFFIX }}{% for mount in UV_INDEX_SECRET_MOUNTS %} \
--mount={{ mount }}{% endfor %} \
uv build --wheel
FROM python:${PYTHON_VERSION}${PYTHON_VARIANT:+-${PYTHON_VARIANT}} AS runtime
ARG PYTHON_VERSION
ARG PYTHON_VARIANT
ARG WORKDIR_PATH=/workspace
LABEL org.opencontainers.image.base.name="python:${PYTHON_VERSION}${PYTHON_VARIANT:+-${PYTHON_VARIANT}}" \
org.opencontainers.image.python.variant="${PYTHON_VARIANT}"
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
ARG NONROOT_USERNAME=py
ARG NONROOT_UID=1000
ARG NONROOT_GID=1000
{% if CONTAINER_ENV_VARS %}
# Inject custom builder stage environment variables
{% for env_var in CONTAINER_ENV_VARS -%}
ENV {{ env_var }}
{% endfor %}
{%- endif %}
ENV DEBIAN_FRONTEND=noninteractive
{% if CONTAINER_APT_PACKAGES %}
# Install optional runtime apt packages selected by Python-side template rendering
RUN --mount=type=cache,target=/var/cache/apt,id=apt-cache{{ CACHE_ID_SUFFIX }} \
--mount=type=cache,target=/var/lib/apt/lists,sharing=locked,id=apt-lists{{ CACHE_ID_SUFFIX }} \
apt-get update && apt-get install -y --no-install-recommends {{ CONTAINER_APT_PACKAGES }}
{% endif %}
{% if CONTAINER_DEPS_IMAGE %}
# Pull external dependencies from the pre-built deps image
ARG CONTAINER_DEPS_IMAGE
COPY --from={{ CONTAINER_DEPS_IMAGE }} /tmp/deps /tmp/deps
{% endif %}
{% if CONTAINER_DEPS_MOVE_SCRIPT %}
RUN set -eux; \
cat >/tmp/container-deps-move-script <<'SCRIPT' && \
chmod +x /tmp/container-deps-move-script && \
/tmp/container-deps-move-script
{{ CONTAINER_DEPS_MOVE_SCRIPT }}
SCRIPT
{% endif %}
# Create a named non-root user with a writable home directory
# Written to cover all Python image variants
RUN set -eux; \
if adduser --help 2>&1 | grep -q -- '--disabled-password'; then \
addgroup --gid "${NONROOT_GID}" "${NONROOT_USERNAME}"; \
adduser --uid "${NONROOT_UID}" --gid "${NONROOT_GID}" --home "${WORKDIR_PATH}" --shell /bin/sh --disabled-password --gecos '' "${NONROOT_USERNAME}"; \
else \
addgroup -g "${NONROOT_GID}" "${NONROOT_USERNAME}" && \
adduser -D -u "${NONROOT_UID}" -G "${NONROOT_USERNAME}" -h "${WORKDIR_PATH}" -s /bin/sh "${NONROOT_USERNAME}"; \
fi; \
mkdir -p "${WORKDIR_PATH}"; \
chown -R "${NONROOT_USERNAME}":"${NONROOT_USERNAME}" "${WORKDIR_PATH}"
WORKDIR ${WORKDIR_PATH}
# Grab package from builder image
COPY --from=builder /tmp/build/dist/*.whl /tmp/
# Install package
RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache{{ CACHE_ID_SUFFIX }} pip install --root-user-action=ignore /tmp/*.whl
# Create symlinks for the package
ARG PACKAGE_NAME
ENV PACKAGE_NAME=${PACKAGE_NAME}
RUN ln -s "$(python -c "import os; from importlib import resources; print(resources.files(os.environ['PACKAGE_NAME']))")" "/_$PACKAGE_NAME" \
&& ln -s "/_$PACKAGE_NAME" "/pkg" \
&& rm -rf "/_$PACKAGE_NAME/__pycache__"
ENTRYPOINT ["/pkg/entrypoint.sh"]
ARG AUTHORS
ARG GIT_COMMIT
LABEL org.opencontainers.image.authors=${AUTHORS}
LABEL git.commit=${GIT_COMMIT}
# Set custom entrypoint if provided
# This entrypoint is deliberately not configurable via environment variables in order to
# ensure that the container always uses the entrypoint selected at build time. If the
# current package does not provide a console script, the entrypoint will default to `python`
RUN echo "#!/bin/sh\n\n{{ ENTRYPOINT_COMMAND|default('python') }} \"\$@\"" >/pkg/entrypoint.sh \
&& chmod +x /pkg/entrypoint.sh
USER py
{% if EXTENSION_CONTENT %}
{{ EXTENSION_CONTENT }}
{% endif %}
{% if HAS_DEBUG_DEPS %}
# Optional debug stage: only installs debug deps if they were exported. This stage will not
# be built by default (the final stage below is the runtime image), and it will safely do
# nothing if there are no debug requirements
FROM runtime AS debug
USER root
COPY --from=builder /tmp/build /tmp/build
RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache{{ CACHE_ID_SUFFIX }} pip install --root-user-action=ignore -r /tmp/build/requirements-debug.txt
USER py
{% endif %}
# Final (default) image: explicitly use runtime as the final target so debug is not used unless requested
FROM runtime AS final
USER py