common-python-tasks 0.8.0__tar.gz → 0.10.0__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.
Files changed (37) hide show
  1. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/PKG-INFO +35 -25
  2. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/README.md +34 -24
  3. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/pyproject.toml +0 -8
  4. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/scripts/add_common_python_tasks.py +8 -41
  5. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/scripts/update_readme_tasks_table.py +0 -2
  6. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/scripts/utils.py +2 -1
  7. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/__init__.py +7 -3
  8. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/data/fastapi/compose-base.yml.j2 +1 -2
  9. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/data/fastapi/compose-db.yml.j2 +1 -2
  10. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/data/fastapi/compose-debug.yml.j2 +1 -1
  11. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/data/generic/.dockerignore +1 -1
  12. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/data/generic/Dockerfile.deps.j2 +6 -1
  13. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/data/generic/Dockerfile.j2 +13 -39
  14. common_python_tasks-0.10.0/src/common_python_tasks/data/generic/ruff.toml +2 -0
  15. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/docker.py +7 -24
  16. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/docker_compose.py +17 -20
  17. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/env.py +89 -2
  18. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/git.py +2 -2
  19. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/github.py +20 -1
  20. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/project.py +127 -297
  21. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/tasks.py +28 -18
  22. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/utils.py +14 -6
  23. common_python_tasks-0.8.0/src/common_python_tasks/data/generic/ruff.toml +0 -2
  24. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/.gitignore +0 -0
  25. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/.poetry/plugins/poetry_dynamic_versioning-1.10.0.dist-info/licenses/LICENSE +0 -0
  26. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/.poetry/plugins/poetry_plugin_export-1.10.0.dist-info/licenses/LICENSE +0 -0
  27. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/LICENSE +0 -0
  28. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/scripts/__init__.py +0 -0
  29. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/scripts/readme_tasks_table.py +0 -0
  30. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/scripts/release_script.py +0 -0
  31. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/__main__.py +0 -0
  32. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/data/dockerfile_extensions/.gitkeep +0 -0
  33. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/data/fastapi/alembic.ini.j2 +0 -0
  34. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/data/fastapi/compose-db-debug.yml.j2 +0 -0
  35. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/data/generic/.coveragerc +0 -0
  36. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/data/generic/cliff.toml +0 -0
  37. {common_python_tasks-0.8.0 → common_python_tasks-0.10.0}/src/common_python_tasks/data/generic/pytest.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: common-python-tasks
3
- Version: 0.8.0
3
+ Version: 0.10.0
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
@@ -43,9 +43,7 @@ It supplies sensible defaults for formatting, linting, testing, packaging, relea
43
43
  Add `common-python-tasks` as a development dependency from your project root.
44
44
 
45
45
  ```shell
46
- uv add --dev common-python-tasks==0.8.0
47
- # or, for Poetry projects
48
- poetry add --group dev common-python-tasks==0.8.0
46
+ uv add --dev common-python-tasks==0.10.0
49
47
  ```
50
48
 
51
49
  Configure Poe the Poet to expose the default `common` task set.
@@ -70,7 +68,7 @@ The helper script performs the same development-dependency installation and Poe
70
68
  ```shell
71
69
  curl --fail --silent --show-error --location \
72
70
  --output /tmp/add_common_python_tasks.py \
73
- https://raw.githubusercontent.com/ci-sourcerer/common-python-tasks/v0.8.0/scripts/add_common_python_tasks.py
71
+ https://raw.githubusercontent.com/ci-sourcerer/common-python-tasks/v0.10.0/scripts/add_common_python_tasks.py
74
72
  ```
75
73
 
76
74
  Review the downloaded script before executing it.
@@ -82,10 +80,10 @@ less /tmp/add_common_python_tasks.py
82
80
  Run the reviewed script with the same pinned package version.
83
81
 
84
82
  ```shell
85
- COMMON_PYTHON_TASKS_VERSION=0.8.0 python3 /tmp/add_common_python_tasks.py
83
+ COMMON_PYTHON_TASKS_VERSION=0.10.0 python3 /tmp/add_common_python_tasks.py
86
84
  ```
87
85
 
88
- To install another release, replace both occurrences of `0.8.0` with that release's version.
86
+ To install another release, replace both occurrences of `0.10.0` with that release's version.
89
87
 
90
88
  ## Available tasks
91
89
 
@@ -108,7 +106,7 @@ The generated tables below list public tasks only. Tags identify which tasks are
108
106
  | --- | --- | --- |
109
107
  | `publish-package` | Publish the package to the PyPI server. | common, packaging |
110
108
  | `publish-github-release` | Publish or update a GitHub Release for the current repository. | common, packaging, release |
111
- | `update-dependencies` | Update project dependencies with Poetry or uv. | common, packaging |
109
+ | `update-dependencies` | Update project dependencies with uv. | common, packaging |
112
110
  | `build-package` | Build the package (wheel and sdist). | build, common, packaging |
113
111
  | `bump-version` | Bump the project version. | common, packaging |
114
112
  | `changelog` | Print the changelog for the current version based on git history and git-cliff. | common, packaging, release |
@@ -143,7 +141,7 @@ The generated tables below list public tasks only. Tags identify which tasks are
143
141
 
144
142
  Every project needs a `pyproject.toml` file at its root and Poe the Poet available in its development environment.
145
143
 
146
- Tasks that install, build, publish, or update dependencies also require either Poetry or uv. Package and release tasks need a resolvable project version from `project.version`, Poetry's version resolution, or Git tags. Dynamic-versioning plugins are supported but are not a baseline requirement.
144
+ Tasks that install, build, publish, or update dependencies require uv. Package and release tasks need a resolvable project version from `project.version` or Git tags. Dynamic versioning is supported but is not a baseline requirement.
147
145
 
148
146
  ### Task selection
149
147
 
@@ -163,18 +161,27 @@ include_script = "common_python_tasks:tasks(include_tags=['common', 'containers'
163
161
 
164
162
  The `containers` tag also includes container-based development-stack tasks. Use the tags in the task tables to tailor a smaller task set.
165
163
 
166
- ### Package manager backend selection
164
+ ### Package management
167
165
 
168
- Packaging-related tasks, including `build-package`, `publish-package`, release version resolution, and container build metadata, support Poetry and uv.
166
+ Packaging-related tasks, including `build-package`, `publish-package`, dependency updates, release version resolution, and container build metadata, use uv exclusively. The `uv` executable must be available on `PATH`.
169
167
 
170
- Backend selection uses this precedence.
168
+ ### Publish target selection
171
169
 
172
- 1. `COMMON_PYTHON_TASKS_PACKAGE_MANAGER`
173
- 2. `COMMON_PYTHON_TASKS_BUILD_TOOL`
174
- 3. `PACKAGE_MANAGER`
175
- 4. Auto-discovery (`auto`) based on installed tools and project metadata
170
+ The `publish-package` task resolves publish targets with this precedence.
176
171
 
177
- Supported values are `poetry`, `uv`, and `auto`.
172
+ 1. Explicit task arguments (`repository` or `repository_url`)
173
+ 2. Environment-variable fallback
174
+ 3. uv project configuration fallback from `[[tool.uv.index]]`
175
+ 4. The `uv publish` default
176
+
177
+ uv prefers named publish indexes.
178
+
179
+ - `COMMON_PYTHON_TASKS_PUBLISH_REPOSITORY`
180
+ - `UV_PUBLISH_INDEX`
181
+ - `COMMON_PYTHON_TASKS_PUBLISH_URL`
182
+ - `UV_PUBLISH_URL`
183
+
184
+ When using uv project configuration fallback, `[[tool.uv.index]]` must include both `name` and `publish-url` for publish selection. If multiple publishable indexes exist, set exactly one `default = true` or pass an explicit repository.
178
185
 
179
186
  ### Configuration precedence
180
187
 
@@ -196,7 +203,7 @@ After installing the package, a minimal project configuration looks like this.
196
203
  ```toml
197
204
  [project]
198
205
  name = "simple-cli-tool"
199
- version = "0.8.0"
206
+ version = "0.10.0"
200
207
 
201
208
  [tool.poe]
202
209
  include_script = "common_python_tasks:tasks()"
@@ -232,14 +239,16 @@ addopts = "-ra"
232
239
  #### Project and package settings
233
240
 
234
241
  - `PACKAGE_NAME`: Overrides the package name inferred from `pyproject.toml`
235
- - `COMMON_PYTHON_TASKS_PACKAGE_MANAGER`: Selects the package-manager backend as `poetry`, `uv`, or `auto`
236
- - `COMMON_PYTHON_TASKS_BUILD_TOOL`: Alias for `COMMON_PYTHON_TASKS_PACKAGE_MANAGER`
237
- - `PACKAGE_MANAGER`: Alias for `COMMON_PYTHON_TASKS_PACKAGE_MANAGER`
242
+ - `COMMON_PYTHON_TASKS_PUBLISH_REPOSITORY`: Preferred publish repository/index name for `publish-package`
243
+ - `COMMON_PYTHON_TASKS_PUBLISH_URL`: Preferred uv publish upload URL for `publish-package` when no repository/index is selected
244
+ - `UV_PUBLISH_INDEX`: uv publish-index fallback for `publish-package`
245
+ - `UV_PUBLISH_URL`: uv publish upload-url fallback for `publish-package`
238
246
 
239
247
  #### Container image settings
240
248
 
241
249
  - `CONTAINER_REGISTRY_USERNAME`: Container-registry username for image tagging; the default is the current local user
242
250
  - `CONTAINER_REGISTRY_URL`: Registry URL with a default of `docker.io/{username}`
251
+ - `CONTAINER_PYTHON_VARIANT`: Python base-image variant such as `slim`, `alpine`, etc. See <https://hub.docker.com/_/python> for available options. Defaults to `slim`; set to empty string for no variant (e.g., `FROM python:3.11`). The value is passed as the `PYTHON_VARIANT` Docker build argument and recorded in the `org.opencontainers.image.python.variant` image label.
243
252
  - `CONTAINER_DOCKER_BUILD_ARGS`: Additional arguments passed directly to `docker build`, parsed using shell quoting rules. Free arguments provided to the task after `--` take precedence.
244
253
  - `CONTAINER_DOCKERFILE_HOOK_PATH`: Optional host path to an executable hook script that receives the generated Dockerfile path and can modify the file before `docker build` runs.
245
254
  - `CONTAINER_APT_PACKAGES`: Space-delimited system packages installed in the generated image
@@ -255,6 +264,7 @@ addopts = "-ra"
255
264
  - `CONTAINER_DEPS_MAPPINGS`: Space-delimited `name:/target/path` entries for copying items from `/tmp/deps`. It is used only when no dependency move script is set.
256
265
  - `CONTAINER_DEPS_MOVE_SCRIPT`: Raw executable script to run after `/tmp/deps` is copied into the image
257
266
  - `CONTAINER_DEPS_MOVE_SCRIPT_PATH`: Host path to a dependency move script. This takes precedence over `CONTAINER_DEPS_MOVE_SCRIPT`.
267
+ - `UV_INDEX_{name}_USERNAME` and `UV_INDEX_{name}_PASSWORD`: Private Python index credentials consumed by uv during the Docker build. When any of these are set, the task automatically passes them as BuildKit secrets (`--secret id=uv_index_{name}_username,env=...`) and renders matching `--mount=type=secret` directives in the builder stage so uv can authenticate without baking credentials into the image. Multiple indices are supported; replace `{name}` with the uppercase index name (hyphens as underscores). These can be set in `tool.poe.env` for CI/CD or in the local environment for development.
258
268
 
259
269
  #### Release settings
260
270
 
@@ -386,7 +396,7 @@ GITHUB_RELEASE_ASSETS="dist/*.whl:dist/*.tar.gz" poe publish-github-release
386
396
 
387
397
  ### Container build fails with "unable to find package"
388
398
 
389
- Check that `pyproject.toml` has a correct package name and the package-discovery settings required by its build backend. For Poetry projects, this commonly includes `[tool.poetry] packages = [{ include = "your_package", from = "src" }]`.
399
+ Check that `pyproject.toml` has a correct package name and the package-discovery settings required by its build backend. For a `src` layout built with Hatch, configure `[tool.hatch.build.targets.wheel] packages = ["src/your_package"]`.
390
400
 
391
401
  ### Stack fails to start or services cannot connect
392
402
 
@@ -411,9 +421,9 @@ Ensure the project-root `.env` file is writable and inspect its permissions with
411
421
 
412
422
  See the [standard Dockerfile template](src/common_python_tasks/data/generic/Dockerfile.j2) for the implementation.
413
423
 
414
- - Multi-stage build: The build stage installs the selected package-manager backend and builds a wheel. The runtime stage installs only the wheel to keep the final image slim and reproducible.
415
- - Cache mounts: Pip and package-manager cache mounts speed up iterative builds without bloating the final image.
416
- - Explicit build metadata: `PYTHON_VERSION`, `PACKAGE_MANAGER`, `PACKAGE_MANAGER_VERSION`, `PACKAGE_NAME`, `AUTHORS`, and `GIT_COMMIT` make image metadata predictable and auditable.
424
+ - Multi-stage build: The build stage installs uv and builds a wheel. The runtime stage installs only the wheel to keep the final image slim and reproducible.
425
+ - Cache mounts: Pip and uv cache mounts speed up iterative builds without bloating the final image.
426
+ - Explicit build metadata: `PYTHON_VERSION`, `UV_VERSION`, `PACKAGE_VERSION`, `PACKAGE_NAME`, `AUTHORS`, and `GIT_COMMIT` make image metadata predictable and auditable.
417
427
  - Project-level build settings: `CONTAINER_APT_PACKAGES`, `CONTAINER_CUSTOM_ENTRYPOINT`, and `CONTAINER_DEPS_IMAGE` configure generated image behavior without being encoded as generic Docker arguments.
418
428
  - Optional debug stage: The image exports and installs the `debug` dependency group only when present and does not include it in the default final image.
419
429
  - Stable package path: Symlinks give entrypoints and consumers consistent `/pkg` and `/_$PACKAGE_NAME` paths regardless of wheel layout.
@@ -11,9 +11,7 @@ It supplies sensible defaults for formatting, linting, testing, packaging, relea
11
11
  Add `common-python-tasks` as a development dependency from your project root.
12
12
 
13
13
  ```shell
14
- uv add --dev common-python-tasks==0.8.0
15
- # or, for Poetry projects
16
- poetry add --group dev common-python-tasks==0.8.0
14
+ uv add --dev common-python-tasks==0.10.0
17
15
  ```
18
16
 
19
17
  Configure Poe the Poet to expose the default `common` task set.
@@ -38,7 +36,7 @@ The helper script performs the same development-dependency installation and Poe
38
36
  ```shell
39
37
  curl --fail --silent --show-error --location \
40
38
  --output /tmp/add_common_python_tasks.py \
41
- https://raw.githubusercontent.com/ci-sourcerer/common-python-tasks/v0.8.0/scripts/add_common_python_tasks.py
39
+ https://raw.githubusercontent.com/ci-sourcerer/common-python-tasks/v0.10.0/scripts/add_common_python_tasks.py
42
40
  ```
43
41
 
44
42
  Review the downloaded script before executing it.
@@ -50,10 +48,10 @@ less /tmp/add_common_python_tasks.py
50
48
  Run the reviewed script with the same pinned package version.
51
49
 
52
50
  ```shell
53
- COMMON_PYTHON_TASKS_VERSION=0.8.0 python3 /tmp/add_common_python_tasks.py
51
+ COMMON_PYTHON_TASKS_VERSION=0.10.0 python3 /tmp/add_common_python_tasks.py
54
52
  ```
55
53
 
56
- To install another release, replace both occurrences of `0.8.0` with that release's version.
54
+ To install another release, replace both occurrences of `0.10.0` with that release's version.
57
55
 
58
56
  ## Available tasks
59
57
 
@@ -76,7 +74,7 @@ The generated tables below list public tasks only. Tags identify which tasks are
76
74
  | --- | --- | --- |
77
75
  | `publish-package` | Publish the package to the PyPI server. | common, packaging |
78
76
  | `publish-github-release` | Publish or update a GitHub Release for the current repository. | common, packaging, release |
79
- | `update-dependencies` | Update project dependencies with Poetry or uv. | common, packaging |
77
+ | `update-dependencies` | Update project dependencies with uv. | common, packaging |
80
78
  | `build-package` | Build the package (wheel and sdist). | build, common, packaging |
81
79
  | `bump-version` | Bump the project version. | common, packaging |
82
80
  | `changelog` | Print the changelog for the current version based on git history and git-cliff. | common, packaging, release |
@@ -111,7 +109,7 @@ The generated tables below list public tasks only. Tags identify which tasks are
111
109
 
112
110
  Every project needs a `pyproject.toml` file at its root and Poe the Poet available in its development environment.
113
111
 
114
- Tasks that install, build, publish, or update dependencies also require either Poetry or uv. Package and release tasks need a resolvable project version from `project.version`, Poetry's version resolution, or Git tags. Dynamic-versioning plugins are supported but are not a baseline requirement.
112
+ Tasks that install, build, publish, or update dependencies require uv. Package and release tasks need a resolvable project version from `project.version` or Git tags. Dynamic versioning is supported but is not a baseline requirement.
115
113
 
116
114
  ### Task selection
117
115
 
@@ -131,18 +129,27 @@ include_script = "common_python_tasks:tasks(include_tags=['common', 'containers'
131
129
 
132
130
  The `containers` tag also includes container-based development-stack tasks. Use the tags in the task tables to tailor a smaller task set.
133
131
 
134
- ### Package manager backend selection
132
+ ### Package management
135
133
 
136
- Packaging-related tasks, including `build-package`, `publish-package`, release version resolution, and container build metadata, support Poetry and uv.
134
+ Packaging-related tasks, including `build-package`, `publish-package`, dependency updates, release version resolution, and container build metadata, use uv exclusively. The `uv` executable must be available on `PATH`.
137
135
 
138
- Backend selection uses this precedence.
136
+ ### Publish target selection
139
137
 
140
- 1. `COMMON_PYTHON_TASKS_PACKAGE_MANAGER`
141
- 2. `COMMON_PYTHON_TASKS_BUILD_TOOL`
142
- 3. `PACKAGE_MANAGER`
143
- 4. Auto-discovery (`auto`) based on installed tools and project metadata
138
+ The `publish-package` task resolves publish targets with this precedence.
144
139
 
145
- Supported values are `poetry`, `uv`, and `auto`.
140
+ 1. Explicit task arguments (`repository` or `repository_url`)
141
+ 2. Environment-variable fallback
142
+ 3. uv project configuration fallback from `[[tool.uv.index]]`
143
+ 4. The `uv publish` default
144
+
145
+ uv prefers named publish indexes.
146
+
147
+ - `COMMON_PYTHON_TASKS_PUBLISH_REPOSITORY`
148
+ - `UV_PUBLISH_INDEX`
149
+ - `COMMON_PYTHON_TASKS_PUBLISH_URL`
150
+ - `UV_PUBLISH_URL`
151
+
152
+ When using uv project configuration fallback, `[[tool.uv.index]]` must include both `name` and `publish-url` for publish selection. If multiple publishable indexes exist, set exactly one `default = true` or pass an explicit repository.
146
153
 
147
154
  ### Configuration precedence
148
155
 
@@ -164,7 +171,7 @@ After installing the package, a minimal project configuration looks like this.
164
171
  ```toml
165
172
  [project]
166
173
  name = "simple-cli-tool"
167
- version = "0.8.0"
174
+ version = "0.10.0"
168
175
 
169
176
  [tool.poe]
170
177
  include_script = "common_python_tasks:tasks()"
@@ -200,14 +207,16 @@ addopts = "-ra"
200
207
  #### Project and package settings
201
208
 
202
209
  - `PACKAGE_NAME`: Overrides the package name inferred from `pyproject.toml`
203
- - `COMMON_PYTHON_TASKS_PACKAGE_MANAGER`: Selects the package-manager backend as `poetry`, `uv`, or `auto`
204
- - `COMMON_PYTHON_TASKS_BUILD_TOOL`: Alias for `COMMON_PYTHON_TASKS_PACKAGE_MANAGER`
205
- - `PACKAGE_MANAGER`: Alias for `COMMON_PYTHON_TASKS_PACKAGE_MANAGER`
210
+ - `COMMON_PYTHON_TASKS_PUBLISH_REPOSITORY`: Preferred publish repository/index name for `publish-package`
211
+ - `COMMON_PYTHON_TASKS_PUBLISH_URL`: Preferred uv publish upload URL for `publish-package` when no repository/index is selected
212
+ - `UV_PUBLISH_INDEX`: uv publish-index fallback for `publish-package`
213
+ - `UV_PUBLISH_URL`: uv publish upload-url fallback for `publish-package`
206
214
 
207
215
  #### Container image settings
208
216
 
209
217
  - `CONTAINER_REGISTRY_USERNAME`: Container-registry username for image tagging; the default is the current local user
210
218
  - `CONTAINER_REGISTRY_URL`: Registry URL with a default of `docker.io/{username}`
219
+ - `CONTAINER_PYTHON_VARIANT`: Python base-image variant such as `slim`, `alpine`, etc. See <https://hub.docker.com/_/python> for available options. Defaults to `slim`; set to empty string for no variant (e.g., `FROM python:3.11`). The value is passed as the `PYTHON_VARIANT` Docker build argument and recorded in the `org.opencontainers.image.python.variant` image label.
211
220
  - `CONTAINER_DOCKER_BUILD_ARGS`: Additional arguments passed directly to `docker build`, parsed using shell quoting rules. Free arguments provided to the task after `--` take precedence.
212
221
  - `CONTAINER_DOCKERFILE_HOOK_PATH`: Optional host path to an executable hook script that receives the generated Dockerfile path and can modify the file before `docker build` runs.
213
222
  - `CONTAINER_APT_PACKAGES`: Space-delimited system packages installed in the generated image
@@ -223,6 +232,7 @@ addopts = "-ra"
223
232
  - `CONTAINER_DEPS_MAPPINGS`: Space-delimited `name:/target/path` entries for copying items from `/tmp/deps`. It is used only when no dependency move script is set.
224
233
  - `CONTAINER_DEPS_MOVE_SCRIPT`: Raw executable script to run after `/tmp/deps` is copied into the image
225
234
  - `CONTAINER_DEPS_MOVE_SCRIPT_PATH`: Host path to a dependency move script. This takes precedence over `CONTAINER_DEPS_MOVE_SCRIPT`.
235
+ - `UV_INDEX_{name}_USERNAME` and `UV_INDEX_{name}_PASSWORD`: Private Python index credentials consumed by uv during the Docker build. When any of these are set, the task automatically passes them as BuildKit secrets (`--secret id=uv_index_{name}_username,env=...`) and renders matching `--mount=type=secret` directives in the builder stage so uv can authenticate without baking credentials into the image. Multiple indices are supported; replace `{name}` with the uppercase index name (hyphens as underscores). These can be set in `tool.poe.env` for CI/CD or in the local environment for development.
226
236
 
227
237
  #### Release settings
228
238
 
@@ -354,7 +364,7 @@ GITHUB_RELEASE_ASSETS="dist/*.whl:dist/*.tar.gz" poe publish-github-release
354
364
 
355
365
  ### Container build fails with "unable to find package"
356
366
 
357
- Check that `pyproject.toml` has a correct package name and the package-discovery settings required by its build backend. For Poetry projects, this commonly includes `[tool.poetry] packages = [{ include = "your_package", from = "src" }]`.
367
+ Check that `pyproject.toml` has a correct package name and the package-discovery settings required by its build backend. For a `src` layout built with Hatch, configure `[tool.hatch.build.targets.wheel] packages = ["src/your_package"]`.
358
368
 
359
369
  ### Stack fails to start or services cannot connect
360
370
 
@@ -379,9 +389,9 @@ Ensure the project-root `.env` file is writable and inspect its permissions with
379
389
 
380
390
  See the [standard Dockerfile template](src/common_python_tasks/data/generic/Dockerfile.j2) for the implementation.
381
391
 
382
- - Multi-stage build: The build stage installs the selected package-manager backend and builds a wheel. The runtime stage installs only the wheel to keep the final image slim and reproducible.
383
- - Cache mounts: Pip and package-manager cache mounts speed up iterative builds without bloating the final image.
384
- - Explicit build metadata: `PYTHON_VERSION`, `PACKAGE_MANAGER`, `PACKAGE_MANAGER_VERSION`, `PACKAGE_NAME`, `AUTHORS`, and `GIT_COMMIT` make image metadata predictable and auditable.
392
+ - Multi-stage build: The build stage installs uv and builds a wheel. The runtime stage installs only the wheel to keep the final image slim and reproducible.
393
+ - Cache mounts: Pip and uv cache mounts speed up iterative builds without bloating the final image.
394
+ - Explicit build metadata: `PYTHON_VERSION`, `UV_VERSION`, `PACKAGE_VERSION`, `PACKAGE_NAME`, `AUTHORS`, and `GIT_COMMIT` make image metadata predictable and auditable.
385
395
  - Project-level build settings: `CONTAINER_APT_PACKAGES`, `CONTAINER_CUSTOM_ENTRYPOINT`, and `CONTAINER_DEPS_IMAGE` configure generated image behavior without being encoded as generic Docker arguments.
386
396
  - Optional debug stage: The image exports and installs the `debug` dependency group only when present and does not include it in the default final image.
387
397
  - Stable package path: Symlinks give entrypoints and consumers consistent `/pkg` and `/_$PACKAGE_NAME` paths regardless of wheel layout.
@@ -70,11 +70,3 @@ dirty = true
70
70
 
71
71
  [tool.uv]
72
72
  package = true
73
-
74
- [tool.ruff]
75
- line-length = 88
76
- extend-exclude = [".dist", ".poetry", "build"]
77
-
78
- [tool.ruff.lint]
79
- select = ["E4", "E7", "E9", "F", "I", "W"]
80
- ignore = ["W191"]
@@ -10,11 +10,6 @@ import tomllib
10
10
  from pathlib import Path
11
11
 
12
12
  PACKAGE_NAME = "common-python-tasks"
13
- PACKAGE_MANAGER_ENV_VARS = (
14
- "COMMON_PYTHON_TASKS_PACKAGE_MANAGER",
15
- "COMMON_PYTHON_TASKS_BUILD_TOOL",
16
- "PACKAGE_MANAGER",
17
- )
18
13
  PYPROJECT_PATH = Path("pyproject.toml")
19
14
  TOOL_POE_PATTERN = re.compile(r"^\s*\[tool\.poe\]\s*(?:#.*)?$")
20
15
  TABLE_PATTERN = re.compile(r"^\s*\[")
@@ -26,47 +21,18 @@ def _require_command(command: str) -> None:
26
21
  sys.exit(f"Required command not found: {command}")
27
22
 
28
23
 
29
- def _get_requested_package_manager() -> str:
30
- for env_var in PACKAGE_MANAGER_ENV_VARS:
31
- if os.getenv(env_var, "").strip():
32
- return os.environ[env_var].strip().lower()
33
- return "auto"
34
-
35
-
36
- def _select_package_manager() -> str:
37
- requested_manager = _get_requested_package_manager()
38
- if requested_manager in {"poetry", "uv"}:
39
- _require_command(requested_manager)
40
- return requested_manager
41
- if requested_manager != "auto":
42
- sys.exit(f"Unsupported package manager: {requested_manager}")
43
-
44
- for lock_file, package_manager in (
45
- (Path("uv.lock"), "uv"),
46
- (Path("poetry.lock"), "poetry"),
47
- ):
48
- if lock_file.is_file() and shutil.which(package_manager) is not None:
49
- return package_manager
50
-
51
- for package_manager in ("uv", "poetry"):
52
- if shutil.which(package_manager) is not None:
53
- return package_manager
54
-
55
- sys.exit("Neither uv nor Poetry is installed.")
56
-
57
-
58
24
  def _get_package_requirement() -> str:
59
25
  if os.getenv("COMMON_PYTHON_TASKS_VERSION", "").strip():
60
26
  return f"{PACKAGE_NAME}=={os.environ['COMMON_PYTHON_TASKS_VERSION'].strip()}"
61
27
  return PACKAGE_NAME
62
28
 
63
29
 
64
- def _install_package(package_manager: str) -> None:
65
- if package_manager == "uv":
66
- command = ["uv", "add", "--dev", _get_package_requirement()]
67
- else:
68
- command = ["poetry", "add", "--group", "dev", _get_package_requirement()]
69
- subprocess.run(command, check=True, shell=False)
30
+ def _install_package() -> None:
31
+ subprocess.run(
32
+ ["uv", "add", "--dev", _get_package_requirement()],
33
+ check=True,
34
+ shell=False,
35
+ )
70
36
 
71
37
 
72
38
  def _get_include_script() -> str:
@@ -132,7 +98,8 @@ def _print_available_tasks() -> None:
132
98
 
133
99
  def main() -> None:
134
100
  """Install common tasks and configure Poe in the current project."""
135
- _install_package(_select_package_manager())
101
+ _require_command("uv")
102
+ _install_package()
136
103
  _configure_poe()
137
104
  print("\n\033[1;32mCommon Python tasks added to project.\033[0m")
138
105
  print("\n\033[1mAvailable tasks:\033[0m")
@@ -1,5 +1,3 @@
1
- #!/usr/bin/env python3
2
-
3
1
  import os
4
2
  import sys
5
3
  from pathlib import Path
@@ -1,11 +1,12 @@
1
1
  import logging
2
2
  import subprocess
3
+ from typing import ClassVar
3
4
 
4
5
 
5
6
  class ColoredFormatter(logging.Formatter):
6
7
  """Format warning and error log levels with ANSI color codes."""
7
8
 
8
- COLORS = {
9
+ COLORS: ClassVar = {
9
10
  "WARNING": "\033[93m",
10
11
  "ERROR": "\033[91m",
11
12
  "CRITICAL": "\033[91m",
@@ -1,7 +1,7 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
3
  if TYPE_CHECKING:
4
- from typing import Sequence
4
+ from collections.abc import Sequence
5
5
 
6
6
  from poethepoet_tasks import TaskCollection
7
7
 
@@ -9,7 +9,8 @@ __all__ = ["TaskCollection"]
9
9
 
10
10
 
11
11
  def tasks(
12
- include_tags: "Sequence[str] | None" = None, exclude_tags: "Sequence[str]" = tuple()
12
+ include_tags: "Sequence[str] | None" = None,
13
+ exclude_tags: "Sequence[str] | None" = None,
13
14
  ) -> dict:
14
15
  """Return the task collection filtered by the requested tags.
15
16
 
@@ -25,6 +26,9 @@ def tasks(
25
26
  if include_tags is None and not exclude_tags:
26
27
  include_tags = ("common",)
27
28
  elif include_tags is None:
28
- include_tags = tuple()
29
+ include_tags = ()
30
+
31
+ if exclude_tags is None:
32
+ exclude_tags = ()
29
33
 
30
34
  return task_collection(include_tags=include_tags, exclude_tags=exclude_tags)
@@ -9,8 +9,7 @@ x-common-environment: &common-environment
9
9
  x-common-build-args: &common-build-args
10
10
  PACKAGE_NAME: ${PACKAGE_NAME}
11
11
  PYTHON_VERSION: ${PYTHON_VERSION}
12
- PACKAGE_MANAGER: ${PACKAGE_MANAGER}
13
- PACKAGE_MANAGER_VERSION: ${PACKAGE_MANAGER_VERSION}
12
+ UV_VERSION: ${UV_VERSION}
14
13
 
15
14
  services:
16
15
  api:
@@ -8,8 +8,7 @@ x-common-environment: &common-environment
8
8
  x-common-build-args: &common-build-args
9
9
  PACKAGE_NAME: ${PACKAGE_NAME}
10
10
  PYTHON_VERSION: ${PYTHON_VERSION}
11
- PACKAGE_MANAGER: ${PACKAGE_MANAGER}
12
- PACKAGE_MANAGER_VERSION: ${PACKAGE_MANAGER_VERSION}
11
+ UV_VERSION: ${UV_VERSION}
13
12
  WORKDIR_PATH: ${WORKDIR_PATH}
14
13
 
15
14
  services:
@@ -22,7 +22,7 @@ services:
22
22
  path: ./src/${PACKAGE_UNDERSCORE_NAME}
23
23
  target: /${PACKAGE_UNDERSCORE_NAME}
24
24
  - action: rebuild
25
- path: poetry.lock
25
+ path: uv.lock
26
26
  ports:
27
27
  - mode: ingress
28
28
  target: 5678
@@ -1,7 +1,7 @@
1
1
  *
2
2
  !dist/*.whl
3
3
  !pyproject.toml
4
- !poetry.lock
4
+ !uv.lock
5
5
  !README.md
6
6
  !LICENSE
7
7
 
@@ -5,7 +5,12 @@
5
5
  ARG PYTHON_VERSION=3
6
6
  ARG PYTHON_VARIANT=slim
7
7
 
8
- FROM python:${PYTHON_VERSION}-${PYTHON_VARIANT} AS deps
8
+ FROM python:${PYTHON_VERSION}${PYTHON_VARIANT:+-${PYTHON_VARIANT}} AS deps
9
+ ARG PYTHON_VERSION
10
+ ARG PYTHON_VARIANT
11
+
12
+ LABEL org.opencontainers.image.base.name="python:${PYTHON_VERSION}${PYTHON_VARIANT:+-${PYTHON_VARIANT}}" \
13
+ org.opencontainers.image.python.variant="${PYTHON_VARIANT}"
9
14
 
10
15
  ARG CACHE_ID_SUFFIX
11
16
  RUN printf '%s' "$CACHE_ID_SUFFIX" >/tmp/.cache_id_suffix
@@ -4,36 +4,12 @@
4
4
  # the latest. The `build-image` task passes the version properly here
5
5
  ARG PYTHON_VERSION=3
6
6
  # Variant for the runtime image, e.g. slim, alpine, etc.
7
- # See https://hub.docker.com/_/python for available variants
7
+ # See https://hub.docker.com/_/python for available variants. Leave empty for no variant.
8
8
  ARG PYTHON_VARIANT=slim
9
- ARG WORKDIR_PATH=/workspace
10
- {% set package_manager = PACKAGE_MANAGER|default('poetry') %}
11
9
 
12
10
  FROM python:${PYTHON_VERSION} AS builder
13
11
 
14
12
  ARG PACKAGE_VERSION
15
-
16
- {% if package_manager == 'poetry' %}
17
- ARG POETRY_VERSION
18
- ARG POETRY_DYNAMIC_VERSIONING_PLUGIN_VERSION
19
- ARG POETRY_PLUGIN_EXPORT_VERSION
20
-
21
- # Bypass VCS-based version detection: use the host-computed version directly.
22
- # This avoids requiring a .git directory in the build context and prevents
23
- # false dirty-version detection from a partial working tree.
24
- ENV POETRY_DYNAMIC_VERSIONING_BYPASS=${PACKAGE_VERSION}
25
-
26
- # Install Poetry and required Poetry plugins for package build
27
- RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache{{ CACHE_ID_SUFFIX }} \
28
- sh -c 'set -eu; \
29
- poetry_spec="poetry"; \
30
- if [ -n "${POETRY_VERSION:-}" ]; then poetry_spec="poetry==$POETRY_VERSION"; fi; \
31
- dynamic_spec="poetry-dynamic-versioning[plugin]"; \
32
- if [ -n "${POETRY_DYNAMIC_VERSIONING_PLUGIN_VERSION:-}" ]; then dynamic_spec="poetry-dynamic-versioning[plugin]==$POETRY_DYNAMIC_VERSIONING_PLUGIN_VERSION"; fi; \
33
- export_spec="poetry-plugin-export"; \
34
- if [ -n "${POETRY_PLUGIN_EXPORT_VERSION:-}" ]; then export_spec="poetry-plugin-export==$POETRY_PLUGIN_EXPORT_VERSION"; fi; \
35
- pip install --root-user-action=ignore "$poetry_spec" "$dynamic_spec" "$export_spec"'
36
- {% else %}
37
13
  ARG UV_VERSION
38
14
 
39
15
  # Bypass VCS-based version detection: use the host-computed version directly.
@@ -48,7 +24,6 @@ RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache{{ CACHE_ID_SUFFIX }}
48
24
  else \
49
25
  pip install --root-user-action=ignore uv; \
50
26
  fi'
51
- {% endif %}
52
27
 
53
28
  # Build package
54
29
  WORKDIR /tmp/build
@@ -56,28 +31,27 @@ COPY . /tmp/build/
56
31
 
57
32
  {% if HAS_DEBUG_DEPS %}
58
33
  # Export debug requirements when the project defines a debug dependency group
59
- {% if package_manager == 'poetry' %}
60
- RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache{{ CACHE_ID_SUFFIX }} \
61
- poetry export --only debug --without-hashes -f requirements.txt --output requirements-debug.txt
62
- {% else %}
63
- RUN --mount=type=cache,target=/root/.cache/uv,id=uv-cache{{ CACHE_ID_SUFFIX }} \
34
+ RUN --mount=type=cache,target=/root/.cache/uv,id=uv-cache{{ CACHE_ID_SUFFIX }}{% for mount in UV_INDEX_SECRET_MOUNTS %} \
35
+ --mount={{ mount }}{% endfor %} \
64
36
  uv export --group debug --no-hashes --format requirements-txt --output-file requirements-debug.txt
65
37
  {% endif %}
66
- {% endif %}
67
38
 
68
- # Build the wheel, caching the package-manager cache directory to speed up subsequent builds.
39
+ # Build the wheel, caching the uv cache directory to speed up subsequent builds.
69
40
  # If running this in a CI system, you must be using a persistent builder to take
70
41
  # advantage of the cache mount, and you should configure your CI to persist the
71
42
  # cache directory between builds
72
- {% if package_manager == 'poetry' %}
73
- RUN --mount=type=cache,target=/root/.cache/pypoetry,id=pypoetry-cache{{ CACHE_ID_SUFFIX }} poetry build --format=wheel
74
- {% else %}
75
- RUN --mount=type=cache,target=/root/.cache/uv,id=uv-cache{{ CACHE_ID_SUFFIX }} uv build --wheel
76
- {% endif %}
43
+ RUN --mount=type=cache,target=/root/.cache/uv,id=uv-cache{{ CACHE_ID_SUFFIX }}{% for mount in UV_INDEX_SECRET_MOUNTS %} \
44
+ --mount={{ mount }}{% endfor %} \
45
+ uv build --wheel
77
46
 
78
- FROM python:${PYTHON_VERSION}-${PYTHON_VARIANT} AS runtime
47
+ FROM python:${PYTHON_VERSION}${PYTHON_VARIANT:+-${PYTHON_VARIANT}} AS runtime
48
+ ARG PYTHON_VERSION
49
+ ARG PYTHON_VARIANT
79
50
  ARG WORKDIR_PATH=/workspace
80
51
 
52
+ LABEL org.opencontainers.image.base.name="python:${PYTHON_VERSION}${PYTHON_VARIANT:+-${PYTHON_VARIANT}}" \
53
+ org.opencontainers.image.python.variant="${PYTHON_VARIANT}"
54
+
81
55
  ENV PYTHONUNBUFFERED=1
82
56
  ENV PYTHONDONTWRITEBYTECODE=1
83
57
 
@@ -0,0 +1,2 @@
1
+ line-length = 88
2
+ extend-exclude = [".dist", "build"]