common-python-tasks 0.1.0__tar.gz → 0.3.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 (33) hide show
  1. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/PKG-INFO +11 -11
  2. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/README.md +9 -9
  3. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/pyproject.toml +2 -3
  4. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/__init__.py +2 -8
  5. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/__main__.py +23 -19
  6. common_python_tasks-0.3.0/src/common_python_tasks/data/generic/.pytest_cache/v/cache/lastfailed +11 -0
  7. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/generic/.pytest_cache/v/cache/nodeids +85 -0
  8. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/docker.py +296 -233
  9. common_python_tasks-0.1.0/src/common_python_tasks/compose.py → common_python_tasks-0.3.0/src/common_python_tasks/docker_compose.py +15 -9
  10. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/env.py +41 -16
  11. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/git.py +106 -49
  12. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/github.py +92 -42
  13. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/project.py +67 -27
  14. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/tasks.py +180 -85
  15. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/utils.py +44 -6
  16. common_python_tasks-0.1.0/src/common_python_tasks/data/generic/.pytest_cache/v/cache/lastfailed +0 -4
  17. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/LICENSE +0 -0
  18. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/dockerfile_extensions/.gitkeep +0 -0
  19. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/fastapi/alembic.ini.j2 +0 -0
  20. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/fastapi/compose-base.yml.j2 +0 -0
  21. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/fastapi/compose-db-debug.yml.j2 +0 -0
  22. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/fastapi/compose-db.yml.j2 +0 -0
  23. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/fastapi/compose-debug.yml.j2 +0 -0
  24. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/generic/.coveragerc +0 -0
  25. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/generic/.dockerignore +0 -0
  26. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/generic/.flake8 +0 -0
  27. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/generic/.isort.cfg +0 -0
  28. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/generic/.pytest_cache/.gitignore +0 -0
  29. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/generic/.pytest_cache/CACHEDIR.TAG +0 -0
  30. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/generic/.pytest_cache/README.md +0 -0
  31. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/generic/Dockerfile.deps.j2 +0 -0
  32. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/generic/Dockerfile.j2 +0 -0
  33. {common_python_tasks-0.1.0 → common_python_tasks-0.3.0}/src/common_python_tasks/data/generic/pytest.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: common-python-tasks
3
- Version: 0.1.0
3
+ Version: 0.3.0
4
4
  Summary: Opinionated Poe the Poet tasks for Python package development.
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -23,7 +23,7 @@ Requires-Dist: git-cliff (>=2.12.0,<3.0.0)
23
23
  Requires-Dist: isort (>=8.0.1,<9.0.0)
24
24
  Requires-Dist: jinja2 (>=3.1.6,<4.0.0)
25
25
  Requires-Dist: poethepoet-tasks (>=0.3.0,<0.4.0)
26
- Requires-Dist: pytest (>=9.0.1,<10.0.0)
26
+ Requires-Dist: pytest (>=9.0.3,<10.0.0)
27
27
  Requires-Dist: pytest-cov (>=7.0.0,<8.0.0)
28
28
  Requires-Dist: tomlkit (>=0.13.3,<0.14.0)
29
29
  Project-URL: Homepage, http://github.com/ci-sourcerer/common-python-tasks
@@ -50,7 +50,7 @@ curl -sSL https://api.github.com/repos/ci-sourcerer/common-python-tasks/contents
50
50
  To install a specific release, set the environment variable `COMMON_PYTHON_TASKS_VERSION`.
51
51
 
52
52
  ```shell
53
- COMMON_PYTHON_TASKS_VERSION=0.1.0 \
53
+ COMMON_PYTHON_TASKS_VERSION=0.3.0 \
54
54
  sh -c "$(curl -sSL https://api.github.com/repos/ci-sourcerer/common-python-tasks/contents/scripts/add-common-python-tasks.sh | jq -r '.content' | base64 -d)"
55
55
  ```
56
56
 
@@ -71,9 +71,9 @@ There's no real reason to run the automated script; I just like automating every
71
71
  ```toml
72
72
  [project]
73
73
  name = "my-awesome-project"
74
- version = "0.1.0"
74
+ version = "0.3.0"
75
75
  dependencies = [
76
- "common-python-tasks==0.1.0", # Always pin to a specific version
76
+ "common-python-tasks==0.3.0", # Always pin to a specific version
77
77
  ]
78
78
 
79
79
  [tool.poe]
@@ -248,7 +248,7 @@ The following environment variables configure package and container behavior.
248
248
  - `GITHUB_RELEASE_TAG` optional tag name to publish for the GitHub Release.
249
249
  - `GITHUB_RELEASE_NAME` optional release title to use for the GitHub Release.
250
250
  - `GITHUB_RELEASE_BODY` optional release body text to use for the GitHub Release.
251
- - `RELEASE_UPDATE_CHANGELOG` truthy value to update `CHANGELOG.md` with `git-cliff --tag "$RELEASE_TAG" --prepend CHANGELOG.md` before the release tag is created. This is enabled by default; set it to a falsy value to disable changelog commits during release.
251
+ - `RELEASE_UPDATE_CHANGELOG` truthy value to update `CHANGELOG.md` by prepending the `git-cliff --unreleased --tag "$RELEASE_TAG"` output before the release tag is created. This is enabled by default; set it to a falsy value to disable changelog commits during release.
252
252
  - `RELEASE_PRE_SCRIPT` optional shell command to run before the release steps.
253
253
  - `RELEASE_POST_SCRIPT` optional shell command to run after the release completes.
254
254
  - Hook commands receive the following env vars: `RELEASE_SCRIPT_PHASE`, `RELEASE_TAG`, `RELEASE_VERSION`, `RELEASE_STAGE`, `RELEASE_COMPONENT`, and `RELEASE_DRY_RUN`.
@@ -287,8 +287,8 @@ By default, `tasks()` exposes the common task set. You can still include or excl
287
287
  ```toml
288
288
  [project]
289
289
  name = "simple-cli-tool"
290
- version = "0.1.0"
291
- dependencies = ["common-python-tasks==0.1.0"]
290
+ version = "0.3.0"
291
+ dependencies = ["common-python-tasks==0.3.0"]
292
292
 
293
293
  [tool.poe]
294
294
  include_script = "common_python_tasks:tasks()"
@@ -301,8 +301,8 @@ Available tasks: common defaults such as `format`, `lint`, `test`, and `build`.
301
301
  ```toml
302
302
  [project]
303
303
  name = "containerized-app"
304
- version = "0.1.0"
305
- dependencies = ["common-python-tasks==0.1.0"]
304
+ version = "0.3.0"
305
+ dependencies = ["common-python-tasks==0.3.0"]
306
306
 
307
307
  [tool.poe]
308
308
  include_script = "common_python_tasks:tasks(include_tags=['common', 'containers'])"
@@ -319,7 +319,7 @@ Available tasks: All tasks including `build-image` and `push-image`.
319
319
  ```toml
320
320
  [project]
321
321
  name = "custom-test-setup"
322
- dependencies = ["common-python-tasks==0.1.0"]
322
+ dependencies = ["common-python-tasks==0.3.0"]
323
323
  dynamic = ["version"]
324
324
 
325
325
  [tool.poe]
@@ -17,7 +17,7 @@ curl -sSL https://api.github.com/repos/ci-sourcerer/common-python-tasks/contents
17
17
  To install a specific release, set the environment variable `COMMON_PYTHON_TASKS_VERSION`.
18
18
 
19
19
  ```shell
20
- COMMON_PYTHON_TASKS_VERSION=0.1.0 \
20
+ COMMON_PYTHON_TASKS_VERSION=0.3.0 \
21
21
  sh -c "$(curl -sSL https://api.github.com/repos/ci-sourcerer/common-python-tasks/contents/scripts/add-common-python-tasks.sh | jq -r '.content' | base64 -d)"
22
22
  ```
23
23
 
@@ -38,9 +38,9 @@ There's no real reason to run the automated script; I just like automating every
38
38
  ```toml
39
39
  [project]
40
40
  name = "my-awesome-project"
41
- version = "0.1.0"
41
+ version = "0.3.0"
42
42
  dependencies = [
43
- "common-python-tasks==0.1.0", # Always pin to a specific version
43
+ "common-python-tasks==0.3.0", # Always pin to a specific version
44
44
  ]
45
45
 
46
46
  [tool.poe]
@@ -215,7 +215,7 @@ The following environment variables configure package and container behavior.
215
215
  - `GITHUB_RELEASE_TAG` optional tag name to publish for the GitHub Release.
216
216
  - `GITHUB_RELEASE_NAME` optional release title to use for the GitHub Release.
217
217
  - `GITHUB_RELEASE_BODY` optional release body text to use for the GitHub Release.
218
- - `RELEASE_UPDATE_CHANGELOG` truthy value to update `CHANGELOG.md` with `git-cliff --tag "$RELEASE_TAG" --prepend CHANGELOG.md` before the release tag is created. This is enabled by default; set it to a falsy value to disable changelog commits during release.
218
+ - `RELEASE_UPDATE_CHANGELOG` truthy value to update `CHANGELOG.md` by prepending the `git-cliff --unreleased --tag "$RELEASE_TAG"` output before the release tag is created. This is enabled by default; set it to a falsy value to disable changelog commits during release.
219
219
  - `RELEASE_PRE_SCRIPT` optional shell command to run before the release steps.
220
220
  - `RELEASE_POST_SCRIPT` optional shell command to run after the release completes.
221
221
  - Hook commands receive the following env vars: `RELEASE_SCRIPT_PHASE`, `RELEASE_TAG`, `RELEASE_VERSION`, `RELEASE_STAGE`, `RELEASE_COMPONENT`, and `RELEASE_DRY_RUN`.
@@ -254,8 +254,8 @@ By default, `tasks()` exposes the common task set. You can still include or excl
254
254
  ```toml
255
255
  [project]
256
256
  name = "simple-cli-tool"
257
- version = "0.1.0"
258
- dependencies = ["common-python-tasks==0.1.0"]
257
+ version = "0.3.0"
258
+ dependencies = ["common-python-tasks==0.3.0"]
259
259
 
260
260
  [tool.poe]
261
261
  include_script = "common_python_tasks:tasks()"
@@ -268,8 +268,8 @@ Available tasks: common defaults such as `format`, `lint`, `test`, and `build`.
268
268
  ```toml
269
269
  [project]
270
270
  name = "containerized-app"
271
- version = "0.1.0"
272
- dependencies = ["common-python-tasks==0.1.0"]
271
+ version = "0.3.0"
272
+ dependencies = ["common-python-tasks==0.3.0"]
273
273
 
274
274
  [tool.poe]
275
275
  include_script = "common_python_tasks:tasks(include_tags=['common', 'containers'])"
@@ -286,7 +286,7 @@ Available tasks: All tasks including `build-image` and `push-image`.
286
286
  ```toml
287
287
  [project]
288
288
  name = "custom-test-setup"
289
- dependencies = ["common-python-tasks==0.1.0"]
289
+ dependencies = ["common-python-tasks==0.3.0"]
290
290
  dynamic = ["version"]
291
291
 
292
292
  [tool.poe]
@@ -24,13 +24,13 @@ dependencies = [
24
24
  "isort (>=8.0.1,<9.0.0)",
25
25
  "poethepoet-tasks (>=0.3.0,<0.4.0)",
26
26
  "pytest-cov (>=7.0.0,<8.0.0)",
27
- "pytest (>=9.0.1,<10.0.0)",
27
+ "pytest (>=9.0.3,<10.0.0)",
28
28
  "tomlkit (>=0.13.3,<0.14.0)",
29
29
  "jinja2 (>=3.1.6,<4.0.0)",
30
30
  "git-cliff (>=2.12.0,<3.0.0)",
31
31
  ]
32
32
  dynamic = []
33
- version = "0.1.0"
33
+ version = "0.3.0"
34
34
 
35
35
  [project.urls]
36
36
  Homepage = "http://github.com/ci-sourcerer/common-python-tasks"
@@ -44,7 +44,6 @@ include_script = "common_python_tasks:tasks(exclude_tags=['containers'])"
44
44
  PYTHONPATH = "src"
45
45
  RELEASE_UPDATE_CHANGELOG = "1"
46
46
  RELEASE_PRE_SCRIPT = "RELEASE_SCRIPT_PHASE=pre python scripts/release_script.py"
47
- RELEASE_POST_SCRIPT = "RELEASE_SCRIPT_PHASE=post python scripts/release_script.py"
48
47
 
49
48
  [tool.poetry.requires-plugins]
50
49
  poetry-dynamic-versioning = { version = ">=1.0.0,<2.0.0", extras = ["plugin"] }
@@ -9,8 +9,7 @@ __all__ = ["TaskCollection"]
9
9
 
10
10
 
11
11
  def tasks(
12
- include_tags: "Sequence[str] | None" = None,
13
- exclude_tags: "Sequence[str]" = tuple(),
12
+ include_tags: "Sequence[str] | None" = None, exclude_tags: "Sequence[str]" = tuple()
14
13
  ) -> dict:
15
14
  from .tasks import tasks as task_collection
16
15
 
@@ -19,9 +18,4 @@ def tasks(
19
18
  elif include_tags is None:
20
19
  include_tags = tuple()
21
20
 
22
- result = task_collection(include_tags=include_tags, exclude_tags=exclude_tags)
23
- globals()["tasks"] = _TASKS_ENTRYPOINT
24
- return result
25
-
26
-
27
- _TASKS_ENTRYPOINT = tasks
21
+ return task_collection(include_tags=include_tags, exclude_tags=exclude_tags)
@@ -13,7 +13,7 @@ def get_available_tasks(internal: bool = False) -> list[str]:
13
13
  """Return available task names for this package.
14
14
 
15
15
  Args:
16
- internal: When True, include internal task names starting with '_'.
16
+ internal: If `True`, include internal task names starting with '_'.
17
17
 
18
18
  Returns:
19
19
  A list of task names.
@@ -39,7 +39,6 @@ def _extract_docstring_excerpt(doc: str) -> str:
39
39
 
40
40
 
41
41
  def _get_task_docstring(task_name: str) -> str | None:
42
-
43
42
  task_config = tasks()["tasks"].get(task_name)
44
43
  if not isinstance(task_config, dict):
45
44
  return None
@@ -50,11 +49,8 @@ def _get_task_docstring(task_name: str) -> str | None:
50
49
 
51
50
  module_name, func_name = script.split(":", 1)
52
51
 
53
- try:
54
- module = importlib.import_module(module_name)
55
- func = getattr(module, func_name)
56
- except (ImportError, AttributeError):
57
- return None
52
+ module = importlib.import_module(module_name)
53
+ func = getattr(module, func_name)
58
54
 
59
55
  doc = inspect.getdoc(func)
60
56
  if not doc:
@@ -63,17 +59,25 @@ def _get_task_docstring(task_name: str) -> str | None:
63
59
  return _extract_docstring_excerpt(doc)
64
60
 
65
61
 
66
- def _get_task_tags(task_name: str) -> list[str] | None:
67
- task_variants = getattr(tasks, "_tasks", {}).get(task_name)
68
- if not task_variants:
69
- return None
62
+ def get_task_tags(task_name: str) -> list[str] | None:
63
+ """Return the tags associated with a task.
64
+
65
+ Args:
66
+ task_name: The name of the task.
67
+
68
+ Returns:
69
+ A list of tags associated with the task, or `None` if no tags are found.
70
+ """
71
+ tags = set()
72
+ for task in tasks:
73
+ if task.name != task_name:
74
+ continue
75
+
76
+ for tag in task.tags:
77
+ # `task-...` tags are internal synthetic tags auto-added by TaskCollection.
78
+ if not tag.startswith("task-"):
79
+ tags.add(tag)
70
80
 
71
- tags = {
72
- tag
73
- for variant in task_variants
74
- for tag in getattr(variant, "tags", ())
75
- if isinstance(tag, str) and not tag.startswith("task-")
76
- }
77
81
  return sorted(tags) or None
78
82
 
79
83
 
@@ -81,8 +85,8 @@ def print_available_tasks(internal: bool = False, include_docs: bool = False) ->
81
85
  """Print available tasks.
82
86
 
83
87
  Args:
84
- internal: Whether or not to include internal task names starting with '_'.
85
- include_docs: Whether or not to include task docstrings.
88
+ internal: If `True`, include internal task names starting with '_'.
89
+ include_docs: If `True`, include task docstrings.
86
90
  """
87
91
  print("\nAvailable tasks in this release:\n")
88
92
  for task_name in get_available_tasks(internal=internal):
@@ -0,0 +1,11 @@
1
+ {
2
+ "tests/test_exec_handoff.py::test_build_compose_exec_script_signal_traps": true,
3
+ "tests/test_exec_handoff.py::test_build_compose_exec_script_full_integration": true,
4
+ "tests/test_release_script.py::test_main_pre_phase_replaces_placeholder_and_rebuilds_table": true,
5
+ "tests/test_release_script.py::test_main_post_phase_restores_placeholder_without_rebuilding_table": true,
6
+ "tests/test_release_script.py::test_main_pre_phase_fails_when_placeholder_is_missing": true,
7
+ "tests/test_release_script.py::test_main_post_phase_fails_when_release_version_is_missing": true,
8
+ "tests/test_release_script.py::test_main_post_phase_leaves_readme_unchanged_without_rebuilding_table": true,
9
+ "tests/test_release_script.py::test_main_post_phase_leaves_readme_unchanged_when_release_version_is_missing": true,
10
+ "tests/test_release_script.py::test_release_script_dry_run_env_post_phase_prints_summary_without_modifying_files": true
11
+ }
@@ -5,6 +5,8 @@
5
5
  "tests/test_build_image.py::TestBuildDepsImage::test_builds_deps_image_with_no_cache_uses_cache_id_suffix_arg",
6
6
  "tests/test_build_image.py::TestBuildDepsImage::test_same_content_gives_same_tag",
7
7
  "tests/test_build_image.py::TestBuildDepsImage::test_tag_varies_with_content",
8
+ "tests/test_build_image.py::TestBuildExtensionImage::test_extension_image_passes_extra_build_args",
9
+ "tests/test_build_image.py::TestBuildExtensionImage::test_extension_image_uses_base_and_omits_target",
8
10
  "tests/test_build_image.py::TestBuildImageLatestTag::test_latest_tag_when_no_future_tags",
9
11
  "tests/test_build_image.py::TestBuildImageLatestTag::test_no_latest_tag_when_future_tags_exist",
10
12
  "tests/test_build_image.py::TestBuildImageWithDeps::test_container_deps_move_script_env_overrides_mappings",
@@ -57,6 +59,7 @@
57
59
  "tests/test_build_image.py::test_build_image_passes_required_dependency_versions",
58
60
  "tests/test_build_image.py::test_build_image_reads_container_envfile_path_overrides_dotcontainerenv",
59
61
  "tests/test_build_image.py::test_build_image_reads_multiple_container_envfiles_in_order",
62
+ "tests/test_build_image.py::test_build_image_removes_non_empty_dist_directory_before_build",
60
63
  "tests/test_build_image.py::test_build_with_multiple_extensions",
61
64
  "tests/test_build_image.py::test_container_shell_fails_when_image_has_no_shells",
62
65
  "tests/test_build_image.py::test_container_shell_fails_when_no_images",
@@ -77,9 +80,55 @@
77
80
  "tests/test_compose.py::test_get_compose_type_returns_env_value",
78
81
  "tests/test_compose.py::test_get_required_vars_for_files_includes_workdir_path_for_compose_db",
79
82
  "tests/test_compose.py::test_read_dotenv_parses_simple_env_file",
83
+ "tests/test_docker.py::TestBuildDepsImage::test_builds_deps_image_and_returns_tag",
84
+ "tests/test_docker.py::TestBuildDepsImage::test_builds_deps_image_from_dockerfile_path",
85
+ "tests/test_docker.py::TestBuildDepsImage::test_builds_deps_image_from_multiple_dockerfile_paths",
86
+ "tests/test_docker.py::TestBuildDepsImage::test_builds_deps_image_with_no_cache_uses_cache_id_suffix_arg",
87
+ "tests/test_docker.py::TestBuildDepsImage::test_same_content_gives_same_tag",
88
+ "tests/test_docker.py::TestBuildDepsImage::test_tag_varies_with_content",
89
+ "tests/test_docker.py::TestBuildExtensionImage::test_extension_image_passes_extra_build_args",
90
+ "tests/test_docker.py::TestBuildExtensionImage::test_extension_image_uses_base_and_omits_target",
91
+ "tests/test_docker.py::TestBuildImageLatestTag::test_latest_tag_when_no_future_tags",
92
+ "tests/test_docker.py::TestBuildImageLatestTag::test_no_latest_tag_when_future_tags_exist",
93
+ "tests/test_docker.py::TestDockerignoreHandling::test_dockerignore_content_from_builtin",
94
+ "tests/test_docker.py::TestDockerignoreHandling::test_preserves_existing_dockerignore",
95
+ "tests/test_docker.py::TestDockerignoreHandling::test_temporary_dockerignore_cleanup_on_error",
96
+ "tests/test_docker.py::TestDockerignoreHandling::test_temporary_dockerignore_created_and_removed",
97
+ "tests/test_docker.py::TestDockerignoreHandling::test_uses_builtin_dockerignore_when_missing",
80
98
  "tests/test_docker.py::test_build_forwards_container_build_options",
99
+ "tests/test_docker.py::test_build_image_removes_non_empty_dist_directory_before_build",
81
100
  "tests/test_docker.py::test_build_with_containers_calls_task_build_image",
82
101
  "tests/test_docker.py::test_build_without_containers_skips_task_build_image",
102
+ "tests/test_docker_compose.py::test_ensure_secrets_generated_creates_keys",
103
+ "tests/test_docker_compose.py::test_get_compose_env_includes_workdir_path_default",
104
+ "tests/test_docker_compose.py::test_get_compose_env_uses_workdir_path_from_env",
105
+ "tests/test_docker_compose.py::test_get_compose_type_returns_default_fastapi",
106
+ "tests/test_docker_compose.py::test_get_compose_type_returns_env_value",
107
+ "tests/test_docker_compose.py::test_get_required_vars_for_files_includes_workdir_path_for_compose_db",
108
+ "tests/test_docker_compose.py::test_read_dotenv_parses_simple_env_file",
109
+ "tests/test_env.py::TestParseContainerDeps::test_content_overrides_file_source",
110
+ "tests/test_env.py::TestParseContainerDeps::test_empty_content_returns_none",
111
+ "tests/test_env.py::TestParseContainerDeps::test_file_missing_raises",
112
+ "tests/test_env.py::TestParseContainerDeps::test_file_source_returns_path_when_no_content",
113
+ "tests/test_env.py::TestParseContainerDeps::test_file_source_returns_paths_when_multiple_files",
114
+ "tests/test_env.py::TestParseContainerDeps::test_returns_content_from_env",
115
+ "tests/test_env.py::TestParseContainerDeps::test_returns_content_from_file",
116
+ "tests/test_env.py::TestParseContainerDeps::test_returns_content_from_multiple_files",
117
+ "tests/test_env.py::TestParseContainerDeps::test_returns_none_when_unset",
118
+ "tests/test_env.py::TestParseContainerDepsMappings::test_duplicate_name_raises",
119
+ "tests/test_env.py::TestParseContainerDepsMappings::test_invalid_token_raises",
120
+ "tests/test_env.py::TestParseContainerDepsMappings::test_parses_quoted_paths",
121
+ "tests/test_env.py::TestParseContainerDepsMappings::test_parses_simple_mappings",
122
+ "tests/test_env.py::TestParseContainerDepsMappings::test_returns_none_when_unset",
123
+ "tests/test_env.py::TestParseContainerExtensions::test_empty_args_treated_as_empty_string",
124
+ "tests/test_env.py::TestParseContainerExtensions::test_mixed_parameterised_and_plain",
125
+ "tests/test_env.py::TestParseContainerExtensions::test_multiple_extension_files",
126
+ "tests/test_env.py::TestParseContainerExtensions::test_parameterised_bundle",
127
+ "tests/test_env.py::TestParseContainerExtensions::test_plain_bundle_name",
128
+ "tests/test_env.py::TestRenderDepsMoveScript::test_script_contains_expected_move_logic",
129
+ "tests/test_env.py::TestResolveExtensionContent::test_file_extension_without_placeholder_and_no_args",
130
+ "tests/test_env.py::TestResolveExtensionContent::test_missing_args_do_not_affect_content",
131
+ "tests/test_env.py::TestResolveExtensionContent::test_template_contains_build_arg_variable",
83
132
  "tests/test_env.py::test_append_dotenv_creates_header_comment",
84
133
  "tests/test_env.py::test_get_or_generate_secret_generates_and_stores_new_secret",
85
134
  "tests/test_env.py::test_get_or_generate_secret_returns_existing_env_var",
@@ -110,6 +159,7 @@
110
159
  "tests/test_exec_handoff.py::test_build_exec_script_quotes_special_chars",
111
160
  "tests/test_exec_handoff.py::test_build_exec_script_teardown_command",
112
161
  "tests/test_exec_handoff.py::test_exec_script_calls_execvpe",
162
+ "tests/test_git.py::TestComputeNextReleaseVersion::test_accepts_enum_inputs",
113
163
  "tests/test_git.py::TestComputeNextReleaseVersion::test_alpha_stage",
114
164
  "tests/test_git.py::TestComputeNextReleaseVersion::test_auto_delegates_to_git_cliff",
115
165
  "tests/test_git.py::TestComputeNextReleaseVersion::test_auto_forces_patch_for_pre_production",
@@ -134,6 +184,16 @@
134
184
  "tests/test_git.py::TestHasTagsLaterInHistory::test_no_tags_exist",
135
185
  "tests/test_git.py::TestHasTagsLaterInHistory::test_no_tags_later_in_history",
136
186
  "tests/test_git.py::test_infer_bump_component_from_git_cliff_returns_expected_bump",
187
+ "tests/test_github.py::test_get_github_api_base_url_defaults_to_github_com",
188
+ "tests/test_github.py::test_get_github_api_base_url_uses_enterprise_api_path",
189
+ "tests/test_github.py::test_get_github_api_base_url_uses_environment_variable",
190
+ "tests/test_github.py::test_get_github_token_falls_back_to_gh_cli",
191
+ "tests/test_github.py::test_get_github_token_uses_environment_variable",
192
+ "tests/test_github.py::test_get_github_token_warns_when_gh_cli_fails",
193
+ "tests/test_github.py::test_get_github_token_warns_when_gh_cli_is_missing",
194
+ "tests/test_github.py::test_github_client_build_url_uses_selected_endpoint",
195
+ "tests/test_project.py::test_extract_poe_script_tags_falls_back_to_exclude_tags_in_include_script",
196
+ "tests/test_project.py::test_extract_poe_script_tags_with_include_and_exclude_scripts",
137
197
  "tests/test_project.py::test_get_authors_reads_pyproject",
138
198
  "tests/test_project.py::test_get_installed_requirement_version_returns_none_when_not_installed",
139
199
  "tests/test_project.py::test_get_installed_requirement_version_strips_extras_and_normalizes_name",
@@ -153,10 +213,17 @@
153
213
  "tests/test_project.py::test_resolve_container_entrypoint_command_uses_package_script_when_available",
154
214
  "tests/test_release_script.py::test_main_dry_run_is_controlled_by_release_script_dry_run_env_pre_phase",
155
215
  "tests/test_release_script.py::test_main_dry_run_prints_transformed_readme",
216
+ "tests/test_release_script.py::test_main_fails_for_invalid_release_phase_when_release_version_is_missing",
217
+ "tests/test_release_script.py::test_main_fails_for_invalid_release_phase_without_running_git",
156
218
  "tests/test_release_script.py::test_main_post_phase_fails_when_release_version_is_missing",
219
+ "tests/test_release_script.py::test_main_post_phase_leaves_readme_unchanged_when_release_version_is_missing",
220
+ "tests/test_release_script.py::test_main_post_phase_leaves_readme_unchanged_without_rebuilding_table",
157
221
  "tests/test_release_script.py::test_main_post_phase_restores_placeholder_without_rebuilding_table",
222
+ "tests/test_release_script.py::test_main_pre_phase_fails_when_latest_tagged_version_is_missing",
158
223
  "tests/test_release_script.py::test_main_pre_phase_fails_when_placeholder_is_missing",
224
+ "tests/test_release_script.py::test_main_pre_phase_replaces_latest_tagged_version_and_rebuilds_table",
159
225
  "tests/test_release_script.py::test_main_pre_phase_replaces_placeholder_and_rebuilds_table",
226
+ "tests/test_release_script.py::test_release_script_dry_run_env_post_phase_fails_before_git_commands",
160
227
  "tests/test_release_script.py::test_release_script_dry_run_env_post_phase_prints_summary_without_modifying_files",
161
228
  "tests/test_release_script.py::test_release_script_dry_run_env_pre_phase_prints_summary_without_modifying_files",
162
229
  "tests/test_tasks.py::TestBumpVersion::test_bump_from_dev_version",
@@ -176,6 +243,7 @@
176
243
  "tests/test_tasks.py::TestBumpVersion::test_bump_with_stage_param",
177
244
  "tests/test_tasks.py::TestBumpVersion::test_case_insensitive_component",
178
245
  "tests/test_tasks.py::TestBumpVersion::test_case_insensitive_stage",
246
+ "tests/test_tasks.py::TestBumpVersion::test_dirty_repo_can_be_allowed",
179
247
  "tests/test_tasks.py::TestBumpVersion::test_dirty_repo_fails",
180
248
  "tests/test_tasks.py::TestBumpVersion::test_dry_run_no_tags",
181
249
  "tests/test_tasks.py::TestBumpVersion::test_dry_run_with_existing_tag",
@@ -185,8 +253,10 @@
185
253
  "tests/test_tasks.py::TestBumpVersion::test_release_dry_run_only_bumps_version",
186
254
  "tests/test_tasks.py::TestBumpVersion::test_release_runs_container_steps",
187
255
  "tests/test_tasks.py::TestBumpVersion::test_release_without_containers_accepts_none_stage_string",
256
+ "tests/test_tasks.py::TestBumpVersion::test_release_without_containers_allows_dirty_repo_without_prompt_env",
188
257
  "tests/test_tasks.py::TestBumpVersion::test_release_without_containers_can_disable_changelog_updates",
189
258
  "tests/test_tasks.py::TestBumpVersion::test_release_without_containers_fails_when_not_on_default_branch",
259
+ "tests/test_tasks.py::TestBumpVersion::test_release_without_containers_prompts_once_for_dirty_repo",
190
260
  "tests/test_tasks.py::TestBumpVersion::test_release_without_containers_runs_pre_and_post_scripts",
191
261
  "tests/test_tasks.py::TestBumpVersion::test_release_without_containers_uses_phase_configured_release_script_hooks",
192
262
  "tests/test_tasks.py::TestGitHubReleasePublishing::test_publish_github_release_creates_release_when_missing",
@@ -208,13 +278,26 @@
208
278
  "tests/test_tasks.py::TestReleaseTask::test_release_without_containers_runs_packaging_flow",
209
279
  "tests/test_tasks.py::TestReleaseTask::test_release_without_containers_runs_pre_and_post_scripts",
210
280
  "tests/test_tasks.py::TestReleaseTask::test_release_without_containers_uses_phase_configured_release_script_hooks",
281
+ "tests/test_tasks.py::test_build_forwards_container_build_options",
282
+ "tests/test_tasks.py::test_build_with_containers_calls_task_build_image",
283
+ "tests/test_tasks.py::test_build_without_containers_skips_task_build_image",
284
+ "tests/test_tasks.py::test_clean_dist_only_removes_only_dist",
285
+ "tests/test_tasks.py::test_confirm_loops_until_valid_response",
286
+ "tests/test_tasks.py::test_container_shell_fails_when_no_images",
287
+ "tests/test_tasks.py::test_container_shell_forwards_docker_run_runtime_flags",
288
+ "tests/test_tasks.py::test_container_shell_selects_most_recent_tag",
289
+ "tests/test_tasks.py::test_container_shell_wraps_fallback_shell_with_env_dump",
290
+ "tests/test_tasks.py::test_fastapi_stack_up_passes_container_build_options",
211
291
  "tests/test_tasks.py::test_format_all_fails_when_no_tools_installed",
212
292
  "tests/test_tasks.py::test_format_all_uses_run_available_tools",
293
+ "tests/test_tasks.py::test_get_task_tags_excludes_synthetic_task_prefix_tags",
294
+ "tests/test_tasks.py::test_get_task_tags_returns_none_for_unknown_task",
213
295
  "tests/test_tasks.py::test_lint_all_fails_when_no_tools_installed",
214
296
  "tests/test_tasks.py::test_lint_all_uses_run_available_tools",
215
297
  "tests/test_tasks.py::test_print_available_tasks_includes_docstrings",
216
298
  "tests/test_tasks.py::test_public_tasks_defaults_to_common_profile",
217
299
  "tests/test_tasks.py::test_public_tasks_supports_explicit_empty_include_for_all_tasks",
300
+ "tests/test_tasks.py::test_run_container_passes_docker_runtime_options",
218
301
  "tests/test_tasks.py::test_task_decorator_does_not_log_top_level_task",
219
302
  "tests/test_tasks.py::test_task_decorator_logs_nested_task_only",
220
303
  "tests/test_utilities.py::TestBumpVersion::test_bump_from_dev_version",
@@ -295,9 +378,11 @@
295
378
  "tests/test_utils.py::test_is_package_installed_handles_dashes",
296
379
  "tests/test_utils.py::test_is_package_installed_when_installed",
297
380
  "tests/test_utils.py::test_is_package_installed_when_not_installed",
381
+ "tests/test_utils.py::test_prepend_changelog_adds_trailing_newline_to_generated_section",
298
382
  "tests/test_utils.py::test_prepend_changelog_creates_scaffold_when_file_missing",
299
383
  "tests/test_utils.py::test_prepend_changelog_does_not_overwrite_existing_file",
300
384
  "tests/test_utils.py::test_prepend_changelog_uses_changelog_md_as_default_path",
385
+ "tests/test_utils.py::test_render_template_text_injects_extension_content",
301
386
  "tests/test_utils.py::test_require_package_fails_when_not_installed",
302
387
  "tests/test_utils.py::test_require_package_succeeds_when_installed",
303
388
  "tests/test_utils.py::test_run_available_tools_fails_when_none_installed",