-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile.j2
More file actions
149 lines (120 loc) · 5.52 KB
/
Dockerfile.j2
File metadata and controls
149 lines (120 loc) · 5.52 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
# 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
ARG PYTHON_VARIANT=slim
ARG WORKDIR_PATH=/workspace
FROM python:${PYTHON_VERSION} AS builder
ARG POETRY_VERSION
ARG POETRY_DYNAMIC_VERSIONING_PLUGIN_VERSION
ARG POETRY_PLUGIN_EXPORT_VERSION
ARG POETRY_DYNAMIC_VERSIONING_COMMANDS
ARG TOMLKIT_VERSION
{% if POETRY_DYNAMIC_VERSIONING_COMMANDS is defined %}
ENV POETRY_DYNAMIC_VERSIONING_COMMANDS=${POETRY_DYNAMIC_VERSIONING_COMMANDS}
{% endif %}
# Install Poetry and required Poetry plugins for build
RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache{{ CACHE_ID_SUFFIX }} pip install --root-user-action=ignore \
"poetry==$POETRY_VERSION" \
"poetry-dynamic-versioning[plugin]==$POETRY_DYNAMIC_VERSIONING_PLUGIN_VERSION" \
"poetry-plugin-export==$POETRY_PLUGIN_EXPORT_VERSION" \
"tomlkit==$TOMLKIT_VERSION"
# 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/pip,id=pip-cache{{ CACHE_ID_SUFFIX }} \
poetry export --only debug --without-hashes -f requirements.txt --output requirements-debug.txt
{% endif %}
# Build the wheel, caching Poetry's 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/pypoetry,id=pypoetry-cache{{ CACHE_ID_SUFFIX }} poetry build --format=wheel
FROM python:${PYTHON_VERSION}-${PYTHON_VARIANT} AS runtime
ARG WORKDIR_PATH=/workspace
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 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 {{ APT_PACKAGES }}
{% endif %}
{% if DEPS_IMAGE %}
# Pull external dependencies from the pre-built deps image
ARG DEPS_IMAGE
COPY --from={{ 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