common-python-tasks 0.11.1__tar.gz → 0.12.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/PKG-INFO +2 -2
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/README.md +1 -1
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/container-images.md +42 -6
- common_python_tasks-0.12.1/src/common_python_tasks/data/dockerfile_extensions/docker-in-docker/Dockerfile +27 -0
- common_python_tasks-0.12.1/src/common_python_tasks/data/dockerfile_extensions/docker-in-docker/install.sh +39 -0
- common_python_tasks-0.12.1/src/common_python_tasks/data/dockerfile_extensions/docker-in-docker/supervisor.sh +117 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/data/generic/Dockerfile.j2 +6 -5
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/env.py +34 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/tasks.py +14 -1
- common_python_tasks-0.11.1/src/common_python_tasks/data/dockerfile_extensions/.gitkeep +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/.gitignore +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/.poetry/plugins/poetry_dynamic_versioning-1.10.0.dist-info/licenses/LICENSE +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/.poetry/plugins/poetry_plugin_export-1.10.0.dist-info/licenses/LICENSE +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/LICENSE +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/configuration.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/documentation-workflows.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/getting-started.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/index.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/daily-development.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/development-stacks.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/documentation.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/index.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/packaging-and-releases.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/build-deps-image.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/build-image.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/build-package.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/build.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/bump-version.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/changelog.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/clean.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/container-shell.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/db-shell.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/docs-build.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/docs-serve.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/format.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/lint.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/publish-github-release.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/publish-package.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/push-image.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/release.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/reset-db.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/run-container.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/run-db-migrations.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/stack-down.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/stack-up.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/test.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/update-dependencies.md +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/pyproject.toml +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/scripts/__init__.py +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/scripts/add_common_python_tasks.py +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/scripts/docs_tasks_reference.py +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/scripts/release_script.py +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/scripts/update_docs_tasks_reference.py +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/scripts/utils.py +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/__init__.py +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/__main__.py +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/data/fastapi/alembic.ini.j2 +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/data/fastapi/compose-base.yml.j2 +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/data/fastapi/compose-db-debug.yml.j2 +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/data/fastapi/compose-db.yml.j2 +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/data/fastapi/compose-debug.yml.j2 +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/data/generic/.coveragerc +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/data/generic/.dockerignore +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/data/generic/Dockerfile.deps.j2 +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/data/generic/cliff.toml +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/data/generic/pytest.ini +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/data/generic/ruff.toml +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/docker.py +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/docker_compose.py +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/git.py +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/github.py +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/project.py +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/utils.py +0 -0
- {common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/zensical.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: common-python-tasks
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.1
|
|
4
4
|
Summary: Opinionated Poe the Poet tasks for Python package development.
|
|
5
5
|
Project-URL: Homepage, http://github.com/ci-sourcerer/common-python-tasks
|
|
6
6
|
Project-URL: Source, http://github.com/ci-sourcerer/common-python-tasks.git
|
|
@@ -42,7 +42,7 @@ Read the [complete documentation](https://ci-sourcerer.github.io/common-python-t
|
|
|
42
42
|
Add the package as a development dependency.
|
|
43
43
|
|
|
44
44
|
```shell
|
|
45
|
-
uv add --dev common-python-tasks==0.
|
|
45
|
+
uv add --dev common-python-tasks==0.12.1
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
Expose the default task set in `pyproject.toml`.
|
|
@@ -9,7 +9,7 @@ Read the [complete documentation](https://ci-sourcerer.github.io/common-python-t
|
|
|
9
9
|
Add the package as a development dependency.
|
|
10
10
|
|
|
11
11
|
```shell
|
|
12
|
-
uv add --dev common-python-tasks==0.
|
|
12
|
+
uv add --dev common-python-tasks==0.12.1
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Expose the default task set in `pyproject.toml`.
|
|
@@ -77,7 +77,7 @@ COPY . /tmp/build/
|
|
|
77
77
|
# Export debug requirements when the project defines a debug dependency group
|
|
78
78
|
RUN --mount=type=cache,target=/root/.cache/uv,id=uv-cache{{ CACHE_ID_SUFFIX }}{% for mount in UV_INDEX_SECRET_MOUNTS %} \
|
|
79
79
|
--mount={{ mount }}{% endfor %} \
|
|
80
|
-
uv export --group debug --no-hashes --format requirements-txt --output-file requirements-debug.txt
|
|
80
|
+
uv export --frozen --only-group debug --no-hashes --format requirements-txt --output-file requirements-debug.txt
|
|
81
81
|
{% endif %}
|
|
82
82
|
|
|
83
83
|
# Build the wheel, caching the uv cache directory to speed up subsequent builds.
|
|
@@ -172,10 +172,11 @@ LABEL git.commit=${GIT_COMMIT}
|
|
|
172
172
|
# current package does not provide a console script, the entrypoint will default to `python`
|
|
173
173
|
RUN echo "#!/bin/sh
|
|
174
174
|
|
|
175
|
-
{{ ENTRYPOINT_COMMAND|default('python') }} \"\$@\"" >/pkg/entrypoint.sh \
|
|
175
|
+
exec {{ ENTRYPOINT_COMMAND|default('python') }} \"\$@\"" >/pkg/entrypoint.sh \
|
|
176
176
|
&& chmod +x /pkg/entrypoint.sh
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
ARG RUNTIME_USER=py
|
|
179
|
+
USER ${RUNTIME_USER}
|
|
179
180
|
|
|
180
181
|
{% if EXTENSION_CONTENT %}
|
|
181
182
|
{{ EXTENSION_CONTENT }}
|
|
@@ -193,13 +194,13 @@ COPY --from=builder /tmp/build /tmp/build
|
|
|
193
194
|
|
|
194
195
|
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
|
|
195
196
|
|
|
196
|
-
USER
|
|
197
|
+
USER ${RUNTIME_USER}
|
|
197
198
|
{% endif %}
|
|
198
199
|
|
|
199
200
|
# Final (default) image: explicitly use runtime as the final target so debug is not used unless requested
|
|
200
201
|
FROM runtime AS final
|
|
201
202
|
|
|
202
|
-
USER
|
|
203
|
+
USER ${RUNTIME_USER}
|
|
203
204
|
```
|
|
204
205
|
|
|
205
206
|
### Dependency image template
|
|
@@ -310,12 +311,14 @@ RUN apt-get update \
|
|
|
310
311
|
USER py
|
|
311
312
|
```
|
|
312
313
|
|
|
313
|
-
Extension files are concatenated in their configured order and inserted near the end of `runtime`, after the entrypoint is created and after `USER py
|
|
314
|
+
Extension files are concatenated in their configured order and inserted near the end of `runtime`, after the entrypoint is created and after `USER ${RUNTIME_USER}` (which defaults to `py`). An extension that needs elevated permissions must switch to `USER root`; it should normally restore `USER ${RUNTIME_USER}` for the instructions that follow. Extensions that need to start as another user can redeclare `ARG RUNTIME_USER=root`; both the final and debug stages honor this argument. `COPY` paths remain relative to the project-root build context.
|
|
314
315
|
|
|
315
316
|
Extension content is treated as raw Dockerfile syntax, not as a Jinja template. This keeps project extensions independent of private template variables used by `common-python-tasks`.
|
|
316
317
|
|
|
317
318
|
`CONTAINER_EXTENSIONS` selects extension bundles shipped in the installed package's `data/dockerfile_extensions/` directory. Bundle names are colon-delimited and are applied after local extension files. A bundle may accept one value with `bundle=value`; that value is passed to the first `ARG` declared by the bundle that has not already been assigned to another extension. Arguments are ignored with a warning when the bundle declares no `ARG`.
|
|
318
319
|
|
|
320
|
+
A bundled extension can keep scripts and other supporting files beside its `Dockerfile`. The image builder exposes that directory as a BuildKit named context called `cpt-extension-<bundle-name>`, with unsupported characters normalized to hyphens. The bundle can copy an asset with `COPY --from=cpt-extension-example script.sh /usr/local/bin/script` without embedding it in a heredoc. These managed contexts are added only to the application image build.
|
|
321
|
+
|
|
319
322
|
Use an extension for additive runtime instructions. Use `CONTAINER_DOCKERFILE_HOOK_PATH` only when a change must rewrite another part of the selected Dockerfile. The hook must be an executable host-side script; it receives a temporary copy of the selected Dockerfile as its first argument and must edit that file in place. The hook also receives the following context variables.
|
|
320
323
|
|
|
321
324
|
| Variable | Meaning |
|
|
@@ -327,6 +330,39 @@ Use an extension for additive runtime instructions. Use `CONTAINER_DOCKERFILE_HO
|
|
|
327
330
|
| `COMMON_PYTHON_TASKS_DOCKER_PLAIN` | `1` when plain progress output is active, otherwise `0` |
|
|
328
331
|
| `COMMON_PYTHON_TASKS_DOCKER_SINGLE_ARCH` | `1` for a single-architecture build, otherwise `0` |
|
|
329
332
|
|
|
333
|
+
### Docker-in-Docker
|
|
334
|
+
|
|
335
|
+
The bundled `docker-in-docker` extension installs Docker CE, containerd, Buildx, and Compose from Docker's signed APT repository. Its installation and supervisor scripts are separate packaged files copied through the extension's named build context; image builds do not download scripts from the devcontainer feature repository.
|
|
336
|
+
|
|
337
|
+
Initial support is limited to Debian Bookworm variants (`slim-bookworm` and `bookworm`) on `amd64` and `arm64`. The extension checks the actual distribution and architecture during the build and rejects everything else, including Alpine and Trixie. Select extensions independently for each image build; other images do not need to enable Docker-in-Docker.
|
|
338
|
+
|
|
339
|
+
```sh
|
|
340
|
+
CONTAINER_PYTHON_VARIANT=slim-bookworm \
|
|
341
|
+
CONTAINER_EXTENSIONS=docker-in-docker poe build-image
|
|
342
|
+
poe run-container --privileged
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
The image starts as root through Tini and the DinD supervisor. The supervisor prepares nested cgroups, starts a dedicated Docker daemon, and waits for `docker info` to succeed before launching the existing `/pkg/entrypoint.sh` as `py`. Application arguments and exit status are preserved. Signals reach the application, and shutdown stops the application before stopping Docker. Startup failure or loss of the daemon terminates the container with a nonzero status. `DIND_STARTUP_TIMEOUT` controls the readiness deadline in seconds (default `60`, accepted range `1`–`9999`). Each process group gets up to five seconds to stop before being killed; allow more than ten seconds for the outer container's stop timeout.
|
|
346
|
+
|
|
347
|
+
Docker daemon output is written to `/var/log/docker.log` by default so it does not mix with application logs. Set `DIND_DOCKER_LOG_PATH` to choose another file path; inspect the file with `docker exec` or mount a log directory if the daemon logs need to be collected.
|
|
348
|
+
|
|
349
|
+
Docker listens only on `unix:///var/run/docker.sock`. The image sets `DOCKER_HOST` accordingly, and startup clears Docker context and TLS environment overrides so the application uses its own daemon. Do not mount the host Docker socket. The `py` user belongs to the Docker group and can control the nested daemon; this is a privileged container, not a sandbox for untrusted workloads.
|
|
350
|
+
|
|
351
|
+
The image declares volumes for `/var/lib/docker` and `/var/lib/containerd`. Docker creates anonymous volumes automatically. Use dedicated named volumes when state should survive container replacement, and never share them between concurrently running daemons. For example, when launching the built image directly, replace `your-image:tag` with its tag.
|
|
352
|
+
|
|
353
|
+
```sh
|
|
354
|
+
docker run --rm --privileged --stop-timeout 20 \
|
|
355
|
+
--mount source=my-app-docker,target=/var/lib/docker \
|
|
356
|
+
--mount source=my-app-containerd,target=/var/lib/containerd \
|
|
357
|
+
your-image:tag
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
For Compose, set `privileged: true`, `stop_grace_period: 20s`, and the equivalent volume mounts on the application service. Keep the image's entrypoint and startup user. `container-shell` and an explicit `--entrypoint` override bypass DinD startup; to inspect a running DinD container, use `docker exec -it --user py <container> sh`.
|
|
361
|
+
|
|
362
|
+
By default, the build installs the current stable packages available in Docker's repository. An optional bundle value pins the exact Docker Engine and CLI APT version. Include literal quotes around the version inside `CONTAINER_EXTENSIONS` so its epoch colon is not parsed as an extension separator. For example, `CONTAINER_EXTENSIONS='docker-in-docker="5:29.8.1-1~debian.12~bookworm"'` selects that version if available. This pins only Engine and CLI; containerd and CLI plugins still use the repository's current versions.
|
|
363
|
+
|
|
364
|
+
Maintainers can run the opt-in integration tests with `CPT_DIND_INTEGRATION=1 poe test tests/test_docker_in_docker.py`. These tests require a running Docker daemon, privileged Linux containers, and network access for image and package downloads. They build the actual generated application Dockerfile and remove their test images, containers, and anonymous volumes afterward.
|
|
365
|
+
|
|
330
366
|
## Supplying external dependencies
|
|
331
367
|
|
|
332
368
|
Dependency images support artifacts that should be built separately from the application wheel, such as compiled tools or browser binaries. The dependency image must place its exported content under `/tmp/deps`; the generated application Dockerfile copies that directory into its `runtime` stage.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Docker-in-Docker currently supports Debian Bookworm on amd64 and arm64.
|
|
2
|
+
USER root
|
|
3
|
+
|
|
4
|
+
# Optional exact APT version, including the epoch and distribution suffix.
|
|
5
|
+
ARG DIND_DOCKER_VERSION
|
|
6
|
+
|
|
7
|
+
COPY --from=cpt-extension-docker-in-docker --chmod=0755 install.sh \
|
|
8
|
+
/usr/local/share/cpt-dind-install.sh
|
|
9
|
+
RUN /usr/local/share/cpt-dind-install.sh \
|
|
10
|
+
&& rm /usr/local/share/cpt-dind-install.sh
|
|
11
|
+
|
|
12
|
+
COPY --from=cpt-extension-docker-in-docker --chmod=0755 supervisor.sh \
|
|
13
|
+
/usr/local/bin/cpt-dind-supervisor
|
|
14
|
+
|
|
15
|
+
RUN mv /pkg/entrypoint.sh /pkg/application-entrypoint.sh \
|
|
16
|
+
&& printf '%s\n' '#!/bin/sh' \
|
|
17
|
+
'exec /usr/bin/tini -- /usr/local/bin/cpt-dind-supervisor "$@"' \
|
|
18
|
+
>/pkg/entrypoint.sh \
|
|
19
|
+
&& chmod 0755 /pkg/entrypoint.sh
|
|
20
|
+
|
|
21
|
+
ENV DOCKER_HOST=unix:///var/run/docker.sock
|
|
22
|
+
VOLUME ["/var/lib/docker", "/var/lib/containerd"]
|
|
23
|
+
|
|
24
|
+
# The generic final and debug stages inherit this startup-user selection.
|
|
25
|
+
# The wrapper drops to py only for the application, after Docker is ready.
|
|
26
|
+
ARG RUNTIME_USER=root
|
|
27
|
+
USER ${RUNTIME_USER}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
# shellcheck source=/dev/null
|
|
5
|
+
. /etc/os-release
|
|
6
|
+
if [ "$ID" != debian ] || [ "${VERSION_CODENAME:-}" != bookworm ]; then
|
|
7
|
+
echo >&2 'docker-in-docker requires Debian Bookworm; use slim-bookworm.'
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
case "$(dpkg --print-architecture)" in
|
|
11
|
+
amd64 | arm64) ;;
|
|
12
|
+
*)
|
|
13
|
+
echo >&2 'docker-in-docker supports amd64 and arm64 only.'
|
|
14
|
+
exit 1
|
|
15
|
+
;;
|
|
16
|
+
esac
|
|
17
|
+
|
|
18
|
+
apt-get update
|
|
19
|
+
apt-get install -y --no-install-recommends \
|
|
20
|
+
bash ca-certificates curl iptables tini util-linux
|
|
21
|
+
install -m 0755 -d /etc/apt/keyrings
|
|
22
|
+
curl -fsSL https://download.docker.com/linux/debian/gpg \
|
|
23
|
+
-o /etc/apt/keyrings/docker.asc
|
|
24
|
+
chmod 0644 /etc/apt/keyrings/docker.asc
|
|
25
|
+
cat >/etc/apt/sources.list.d/docker.sources <<SOURCES
|
|
26
|
+
Types: deb
|
|
27
|
+
URIs: https://download.docker.com/linux/debian
|
|
28
|
+
Suites: bookworm
|
|
29
|
+
Components: stable
|
|
30
|
+
Architectures: $(dpkg --print-architecture)
|
|
31
|
+
Signed-By: /etc/apt/keyrings/docker.asc
|
|
32
|
+
SOURCES
|
|
33
|
+
apt-get update
|
|
34
|
+
apt-get install -y --no-install-recommends \
|
|
35
|
+
"docker-ce${DIND_DOCKER_VERSION:+=$DIND_DOCKER_VERSION}" \
|
|
36
|
+
"docker-ce-cli${DIND_DOCKER_VERSION:+=$DIND_DOCKER_VERSION}" \
|
|
37
|
+
containerd.io docker-buildx-plugin docker-compose-plugin
|
|
38
|
+
usermod -aG docker py
|
|
39
|
+
rm -rf /var/lib/apt/lists/*
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
_prepare_host() {
|
|
5
|
+
if [[ $(id -u) != 0 ]]; then
|
|
6
|
+
echo >&2 'docker-in-docker must start as root with --privileged.'
|
|
7
|
+
return 1
|
|
8
|
+
fi
|
|
9
|
+
if [[ ! -w /sys/fs/cgroup ]]; then
|
|
10
|
+
echo >&2 'docker-in-docker needs writable cgroups; use --privileged.'
|
|
11
|
+
return 1
|
|
12
|
+
fi
|
|
13
|
+
if [[ -d /sys/kernel/security ]] &&
|
|
14
|
+
! mountpoint -q /sys/kernel/security; then
|
|
15
|
+
mount -t securityfs none /sys/kernel/security
|
|
16
|
+
fi
|
|
17
|
+
_prepare_cgroups
|
|
18
|
+
if ! iptables -nL >/dev/null 2>&1 &&
|
|
19
|
+
iptables-legacy -nL >/dev/null 2>&1; then
|
|
20
|
+
update-alternatives --set iptables /usr/sbin/iptables-legacy
|
|
21
|
+
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
|
|
22
|
+
fi
|
|
23
|
+
# Only remove our daemon's PID file, never unrelated processes' files.
|
|
24
|
+
rm -f /var/run/docker.pid
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
_prepare_cgroups() {
|
|
28
|
+
[[ -f /sys/fs/cgroup/cgroup.controllers ]] || return 0
|
|
29
|
+
mkdir -p /sys/fs/cgroup/cpt-init
|
|
30
|
+
local attempt pid controllers
|
|
31
|
+
for ((attempt = 0; attempt < 5; attempt++)); do
|
|
32
|
+
while read -r pid; do
|
|
33
|
+
# Processes can disappear or become immovable while we move them.
|
|
34
|
+
printf '%s\n' "$pid" | tee \
|
|
35
|
+
/sys/fs/cgroup/cpt-init/cgroup.procs >/dev/null 2>&1 || true
|
|
36
|
+
done </sys/fs/cgroup/cgroup.procs
|
|
37
|
+
controllers=$(sed 's/[^ ]\+/+&/g' \
|
|
38
|
+
/sys/fs/cgroup/cgroup.controllers)
|
|
39
|
+
if printf '%s\n' "$controllers" | tee \
|
|
40
|
+
/sys/fs/cgroup/cgroup.subtree_control >/dev/null 2>&1; then
|
|
41
|
+
return 0
|
|
42
|
+
fi
|
|
43
|
+
sleep 0.1
|
|
44
|
+
done
|
|
45
|
+
echo >&2 'docker-in-docker could not enable nested cgroups.'
|
|
46
|
+
return 1
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
_stop_group() {
|
|
50
|
+
local pid=$1
|
|
51
|
+
[[ -n "$pid" ]] || return 0
|
|
52
|
+
kill -TERM -- "-$pid" 2>/dev/null || true
|
|
53
|
+
local attempt
|
|
54
|
+
for ((attempt = 0; attempt < 50; attempt++)); do
|
|
55
|
+
kill -0 -- "-$pid" 2>/dev/null || break
|
|
56
|
+
sleep 0.1
|
|
57
|
+
done
|
|
58
|
+
kill -KILL -- "-$pid" 2>/dev/null || true
|
|
59
|
+
wait "$pid" 2>/dev/null || true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
_shutdown() {
|
|
63
|
+
trap '' TERM INT
|
|
64
|
+
_stop_group "$app_pid"
|
|
65
|
+
_stop_group "$daemon_pid"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
_wait_for_docker() {
|
|
69
|
+
local deadline=$((SECONDS + DIND_STARTUP_TIMEOUT))
|
|
70
|
+
while ((SECONDS < deadline)); do
|
|
71
|
+
if ! kill -0 "$daemon_pid" 2>/dev/null; then
|
|
72
|
+
echo >&2 'docker-in-docker daemon exited during startup.'
|
|
73
|
+
return 1
|
|
74
|
+
fi
|
|
75
|
+
if timeout 1 docker --host "$DOCKER_HOST" info >/dev/null 2>&1; then
|
|
76
|
+
return 0
|
|
77
|
+
fi
|
|
78
|
+
sleep 0.2
|
|
79
|
+
done
|
|
80
|
+
echo >&2 'docker-in-docker timed out waiting for the daemon.'
|
|
81
|
+
return 1
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
_main() {
|
|
85
|
+
if [[ ! ${DIND_STARTUP_TIMEOUT:-60} =~ ^[1-9][0-9]{0,3}$ ]]; then
|
|
86
|
+
echo >&2 'DIND_STARTUP_TIMEOUT must be an integer from 1 to 9999.'
|
|
87
|
+
return 1
|
|
88
|
+
fi
|
|
89
|
+
export DIND_STARTUP_TIMEOUT=${DIND_STARTUP_TIMEOUT:-60}
|
|
90
|
+
export DIND_DOCKER_LOG_PATH=${DIND_DOCKER_LOG_PATH:-/var/log/docker.log}
|
|
91
|
+
export DOCKER_HOST=unix:///var/run/docker.sock
|
|
92
|
+
export container=docker
|
|
93
|
+
unset DOCKER_CONTEXT DOCKER_TLS_VERIFY DOCKER_CERT_PATH
|
|
94
|
+
_prepare_host
|
|
95
|
+
daemon_pid=''
|
|
96
|
+
app_pid=''
|
|
97
|
+
trap _shutdown EXIT
|
|
98
|
+
trap 'exit 143' TERM
|
|
99
|
+
trap 'exit 130' INT
|
|
100
|
+
setsid dockerd --host "$DOCKER_HOST" --group docker \
|
|
101
|
+
>>"$DIND_DOCKER_LOG_PATH" 2>&1 &
|
|
102
|
+
daemon_pid=$!
|
|
103
|
+
_wait_for_docker
|
|
104
|
+
setsid setpriv --reuid py --regid py --init-groups \
|
|
105
|
+
env HOME="$(getent passwd py | cut -d: -f6)" USER=py LOGNAME=py \
|
|
106
|
+
/pkg/application-entrypoint.sh "$@" &
|
|
107
|
+
app_pid=$!
|
|
108
|
+
local finished status=0
|
|
109
|
+
wait -n -p finished "$daemon_pid" "$app_pid" || status=$?
|
|
110
|
+
if [[ "$finished" == "$daemon_pid" ]]; then
|
|
111
|
+
echo >&2 'docker-in-docker daemon exited while the app was running.'
|
|
112
|
+
return 1
|
|
113
|
+
fi
|
|
114
|
+
return "$status"
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
_main "$@"
|
|
@@ -40,7 +40,7 @@ COPY . /tmp/build/
|
|
|
40
40
|
# Export debug requirements when the project defines a debug dependency group
|
|
41
41
|
RUN --mount=type=cache,target=/root/.cache/uv,id=uv-cache{{ CACHE_ID_SUFFIX }}{% for mount in UV_INDEX_SECRET_MOUNTS %} \
|
|
42
42
|
--mount={{ mount }}{% endfor %} \
|
|
43
|
-
uv export --group debug --no-hashes --format requirements-txt --output-file requirements-debug.txt
|
|
43
|
+
uv export --frozen --only-group debug --no-hashes --format requirements-txt --output-file requirements-debug.txt
|
|
44
44
|
{% endif %}
|
|
45
45
|
|
|
46
46
|
# Build the wheel, caching the uv cache directory to speed up subsequent builds.
|
|
@@ -133,10 +133,11 @@ LABEL git.commit=${GIT_COMMIT}
|
|
|
133
133
|
# This entrypoint is deliberately not configurable via environment variables in order to
|
|
134
134
|
# ensure that the container always uses the entrypoint selected at build time. If the
|
|
135
135
|
# current package does not provide a console script, the entrypoint will default to `python`
|
|
136
|
-
RUN echo "#!/bin/sh\n\
|
|
136
|
+
RUN echo "#!/bin/sh\n\nexec {{ ENTRYPOINT_COMMAND|default('python') }} \"\$@\"" >/pkg/entrypoint.sh \
|
|
137
137
|
&& chmod +x /pkg/entrypoint.sh
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
ARG RUNTIME_USER=py
|
|
140
|
+
USER ${RUNTIME_USER}
|
|
140
141
|
|
|
141
142
|
{% if EXTENSION_CONTENT %}
|
|
142
143
|
{{ EXTENSION_CONTENT }}
|
|
@@ -154,10 +155,10 @@ COPY --from=builder /tmp/build /tmp/build
|
|
|
154
155
|
|
|
155
156
|
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
|
|
156
157
|
|
|
157
|
-
USER
|
|
158
|
+
USER ${RUNTIME_USER}
|
|
158
159
|
{% endif %}
|
|
159
160
|
|
|
160
161
|
# Final (default) image: explicitly use runtime as the final target so debug is not used unless requested
|
|
161
162
|
FROM runtime AS final
|
|
162
163
|
|
|
163
|
-
USER
|
|
164
|
+
USER ${RUNTIME_USER}
|
|
@@ -227,6 +227,40 @@ def resolve_extension_content(descriptor: dict[str, str | None]) -> str:
|
|
|
227
227
|
utils.fatal(f"Unknown extension descriptor source: {descriptor['source']}")
|
|
228
228
|
|
|
229
229
|
|
|
230
|
+
def resolve_extension_build_context(
|
|
231
|
+
descriptor: dict[str, str | None],
|
|
232
|
+
) -> tuple[str, Path] | None:
|
|
233
|
+
"""Return the named build context for a bundled extension's asset directory.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
descriptor: Extension descriptor dictionary containing `source` and
|
|
237
|
+
`bundle_name` values.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
A Docker build-context name and its directory, or `None` when the
|
|
241
|
+
extension has no packaged assets.
|
|
242
|
+
"""
|
|
243
|
+
if descriptor["source"] != "bundle":
|
|
244
|
+
return None
|
|
245
|
+
bundle_name = descriptor["bundle_name"]
|
|
246
|
+
out = utils.load_data_file(
|
|
247
|
+
f"{bundle_name}/Dockerfile",
|
|
248
|
+
type_identifier="dockerfile_extensions",
|
|
249
|
+
fatal_on_missing=False,
|
|
250
|
+
)
|
|
251
|
+
if out is None:
|
|
252
|
+
utils.fatal(f"Extension bundle not found: {bundle_name}")
|
|
253
|
+
bundle_directory = out[0].parent
|
|
254
|
+
if not bundle_directory.is_dir() or not any(
|
|
255
|
+
path.name != "Dockerfile" for path in bundle_directory.iterdir()
|
|
256
|
+
):
|
|
257
|
+
return None
|
|
258
|
+
return (
|
|
259
|
+
f"cpt-extension-{re.sub(r'[^a-z0-9]+', '-', bundle_name.lower()).strip('-')}",
|
|
260
|
+
bundle_directory,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
|
|
230
264
|
def get_cache_id_suffix(no_cache: bool) -> str:
|
|
231
265
|
"""Return a cache-break suffix for Docker cache mount IDs.
|
|
232
266
|
Args:
|
|
@@ -541,6 +541,7 @@ def build_image(
|
|
|
541
541
|
resolve_container_docker_build_args,
|
|
542
542
|
resolve_container_dockerfile_hook_path,
|
|
543
543
|
resolve_container_dockerfile_path,
|
|
544
|
+
resolve_extension_build_context,
|
|
544
545
|
resolve_extension_content,
|
|
545
546
|
uv_index_secret_build_args,
|
|
546
547
|
uv_index_secret_mounts,
|
|
@@ -571,6 +572,11 @@ def build_image(
|
|
|
571
572
|
# Resolve all extension fragments up-front so we fail fast on missing
|
|
572
573
|
# bundles or files and avoid calling resolution logic multiple times.
|
|
573
574
|
resolved_fragments = [resolve_extension_content(desc) for desc in extensions]
|
|
575
|
+
extension_build_contexts = [
|
|
576
|
+
context
|
|
577
|
+
for desc in extensions
|
|
578
|
+
if (context := resolve_extension_build_context(desc)) is not None
|
|
579
|
+
]
|
|
574
580
|
|
|
575
581
|
resolved_docker_build_args = resolve_container_docker_build_args(
|
|
576
582
|
docker_build_args,
|
|
@@ -740,7 +746,14 @@ def build_image(
|
|
|
740
746
|
plain=plain,
|
|
741
747
|
single_arch=single_arch,
|
|
742
748
|
extra_build_args=merged_build_args or None,
|
|
743
|
-
docker_build_args=
|
|
749
|
+
docker_build_args=[
|
|
750
|
+
*resolved_docker_build_args,
|
|
751
|
+
*(
|
|
752
|
+
item
|
|
753
|
+
for name, path in extension_build_contexts
|
|
754
|
+
for item in ("--build-context", f"{name}={path}")
|
|
755
|
+
),
|
|
756
|
+
],
|
|
744
757
|
dockerfile_hook_path=resolved_dockerfile_hook_path,
|
|
745
758
|
)
|
|
746
759
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/packaging-and-releases.md
RENAMED
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/build-deps-image.md
RENAMED
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/build-image.md
RENAMED
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/build-package.md
RENAMED
|
File without changes
|
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/bump-version.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/container-shell.md
RENAMED
|
File without changes
|
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/docs-build.md
RENAMED
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/docs-serve.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/publish-package.md
RENAMED
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/push-image.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/run-container.md
RENAMED
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/run-db-migrations.md
RENAMED
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/docs/tasks/reference/stack-down.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/scripts/add_common_python_tasks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/scripts/update_docs_tasks_reference.py
RENAMED
|
File without changes
|
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/__init__.py
RENAMED
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/docker_compose.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{common_python_tasks-0.11.1 → common_python_tasks-0.12.1}/src/common_python_tasks/project.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|