common-python-tasks 0.0.3__tar.gz → 0.1.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 (34) hide show
  1. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/PKG-INFO +56 -43
  2. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/README.md +53 -40
  3. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/pyproject.toml +10 -4
  4. common_python_tasks-0.1.0/src/common_python_tasks/__init__.py +27 -0
  5. common_python_tasks-0.1.0/src/common_python_tasks/__main__.py +108 -0
  6. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/generic/.pytest_cache/v/cache/nodeids +84 -1
  7. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/git.py +61 -22
  8. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/github.py +25 -0
  9. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/project.py +3 -3
  10. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/tasks.py +275 -127
  11. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/utils.py +43 -9
  12. common_python_tasks-0.0.3/src/common_python_tasks/__init__.py +0 -16
  13. common_python_tasks-0.0.3/src/common_python_tasks/__main__.py +0 -35
  14. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/LICENSE +0 -0
  15. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/compose.py +0 -0
  16. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/dockerfile_extensions/.gitkeep +0 -0
  17. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/fastapi/alembic.ini.j2 +0 -0
  18. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/fastapi/compose-base.yml.j2 +0 -0
  19. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/fastapi/compose-db-debug.yml.j2 +0 -0
  20. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/fastapi/compose-db.yml.j2 +0 -0
  21. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/fastapi/compose-debug.yml.j2 +0 -0
  22. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/generic/.coveragerc +0 -0
  23. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/generic/.dockerignore +0 -0
  24. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/generic/.flake8 +0 -0
  25. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/generic/.isort.cfg +0 -0
  26. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/generic/.pytest_cache/.gitignore +0 -0
  27. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/generic/.pytest_cache/CACHEDIR.TAG +0 -0
  28. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/generic/.pytest_cache/README.md +0 -0
  29. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/generic/.pytest_cache/v/cache/lastfailed +0 -0
  30. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/generic/Dockerfile.deps.j2 +0 -0
  31. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/generic/Dockerfile.j2 +0 -0
  32. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/data/generic/pytest.ini +0 -0
  33. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/docker.py +0 -0
  34. {common_python_tasks-0.0.3 → common_python_tasks-0.1.0}/src/common_python_tasks/env.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: common-python-tasks
3
- Version: 0.0.3
3
+ Version: 0.1.0
4
4
  Summary: Opinionated Poe the Poet tasks for Python package development.
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -16,11 +16,11 @@ Classifier: Programming Language :: Python :: 3.13
16
16
  Classifier: Programming Language :: Python :: 3.14
17
17
  Classifier: Topic :: Software Development :: Build Tools
18
18
  Requires-Dist: autoflake (>=2.3.1,<3.0.0)
19
- Requires-Dist: black (>=25.11.0,<26.0.0)
19
+ Requires-Dist: black (>=26.3.1,<27.0.0)
20
20
  Requires-Dist: dunamai (>=1.25.0,<2.0.0)
21
21
  Requires-Dist: flake8 (>=7.3.0,<8.0.0)
22
22
  Requires-Dist: git-cliff (>=2.12.0,<3.0.0)
23
- Requires-Dist: isort (>=7.0.0,<8.0.0)
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
26
  Requires-Dist: pytest (>=9.0.1,<10.0.0)
@@ -35,6 +35,8 @@ Description-Content-Type: text/markdown
35
35
 
36
36
  This package is a collection of (very) opinionated [Poe the Poet](https://poethepoet.natn.io/guides/packaged_tasks.html) Python tasks for common Python development workflows.
37
37
 
38
+ Instead of writing your own tasks for formatting, linting, testing, packaging, and more, you can use these pre-built tasks that work out of the box with reasonable defaults and support configuration overrides when needed. In the past, I found myself copying and pasting the same task definitions across projects, and this package is my attempt to DRY up that workflow and provide a single source of truth for common Python development tasks. I hope you can use them too.
39
+
38
40
  ## Quick start
39
41
 
40
42
  ### Automated setup
@@ -42,31 +44,40 @@ This package is a collection of (very) opinionated [Poe the Poet](https://poethe
42
44
  You can add `common-python-tasks` to a new project by using the handy automated installation script.
43
45
 
44
46
  ```shell
45
- curl -sSL https://api.github.com/repos/ci-sourcerer/common-python-tasks/contents/scripts/add-common-python-tasks.sh | jq -r '.content' | base64 -d | TAGS_TO_INCLUDE="format lint test" sh
47
+ curl -sSL https://api.github.com/repos/ci-sourcerer/common-python-tasks/contents/scripts/add-common-python-tasks.sh | jq -r '.content' | base64 -d | sh
48
+ ```
49
+
50
+ To install a specific release, set the environment variable `COMMON_PYTHON_TASKS_VERSION`.
51
+
52
+ ```shell
53
+ COMMON_PYTHON_TASKS_VERSION=0.1.0 \
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)"
46
55
  ```
47
56
 
48
57
  This will complete the following steps.
49
58
 
50
59
  1. Add the latest version of `common-python-tasks` to your `pyproject.toml` dependencies
51
- 2. Configure Poe the Poet to include only the tasks with the specified tags
60
+ 2. Configure Poe the Poet to expose the default common task set
52
61
  3. Install the package using Poetry
53
62
 
54
63
  **Always review scripts before running them!** Even though I believe I write good software, it's best practice to verify any script you download from the Internet.
55
64
 
56
65
  ### Manual setup
57
66
 
58
- 1. Add `common-python-tasks` to your `pyproject.toml` and configure Poe the Poet to include the desired tasks
67
+ There's no real reason to run the automated script; I just like automating everything. You can achieve the same result by following these steps.
68
+
69
+ 1. Add `common-python-tasks` to your `pyproject.toml` and configure Poe the Poet
59
70
 
60
71
  ```toml
61
72
  [project]
62
73
  name = "my-awesome-project"
63
- version = "0.0.2"
74
+ version = "0.1.0"
64
75
  dependencies = [
65
- "common-python-tasks==0.0.2", # Always pin to a specific version
76
+ "common-python-tasks==0.1.0", # Always pin to a specific version
66
77
  ]
67
78
 
68
79
  [tool.poe]
69
- include_script = "common_python_tasks:tasks(include_tags=['format', 'lint', 'test'])" # Include or exclude tasks by tags
80
+ include_script = "common_python_tasks:tasks()" # Uses the default `common` task set
70
81
  ```
71
82
 
72
83
  2. Install the package
@@ -90,26 +101,26 @@ Internal tasks are used by other tasks and are not meant to be run directly.
90
101
  <!-- tasks-table -->
91
102
  | Task | Description | Tags |
92
103
  | - | - | - |
93
- | `build` | Build the project and its containers (when `containers` tag is included) | packaging, containers |
94
- | `build-image` | Build the container image for this project using the Dockerfile template (and configured extensions) | containers, build |
95
- | `build-package` | Build the package (wheel and sdist) | packaging, build |
96
- | `bump-version` | Bump the project version, defaulting to an inferred semantic bump from git history | packaging |
97
- | `changelog` | Print the changelog for the current version based on git history | packaging, release |
98
- | `clean` | Clean up temporary files and directories | clean |
99
- | `container-shell` | Run the debug image with an interactive shell | containers, debug |
100
- | `db-shell` | Open a psql shell to the database container | web, containers, database |
101
- | `format` | Format code with autoflake, black, and isort | format |
102
- | `lint` | Lint Python code with autoflake, black, isort, and flake8 | lint |
103
- | `publish-package` | Publish the package to the PyPI server | packaging |
104
- | `publish-github-release` | Publish or update a GitHub Release and attach built distribution assets | packaging, release |
105
- | `push-image` | Push the Docker image to the container registry | containers, packaging, release |
106
- | `release` | Run package release flow and publish containers when `containers` tag is included. Supports optional `RELEASE_PRE_SCRIPT` and `RELEASE_POST_SCRIPT` hooks. | packaging, release |
107
- | `reset-db` | Reset the database by deleting the database volume | web, containers, database |
108
- | `run-container` | Run the Docker image as a container | containers |
109
- | `run-db-migrations` | Run database migrations | web, containers, database |
110
- | `stack-down` | Bring down the development stack for the application | web, containers |
111
- | `stack-up` | Bring up the development stack for the application | web, containers |
112
- | `test` | Run the test suite with coverage | test |
104
+ | `test` | Run the test suite with coverage (if `pytest-cov` is installed). | common, test |
105
+ | `clean` | Clean up temporary files and directories. | clean, common |
106
+ | `format` | Format Python code with autoflake, black, and isort. | common, format |
107
+ | `lint` | Lint Python code with autoflake, black, isort, and flake8. | common, lint |
108
+ | `build-image` | Build the container image for this project using the Dockerfile template. | build, containers |
109
+ | `run-container` | Run the Docker image as a container for this project. By default (when `tag` is `None`) this will run the most-recently-built tag for the project's image. | containers |
110
+ | `push-image` | Push the Docker image for this project to the container registry. | containers, packaging, release |
111
+ | `publish-package` | Publish the package to the PyPI server. | common, packaging |
112
+ | `publish-github-release` | Publish or update a GitHub Release for the current repository. | common, packaging, release |
113
+ | `build-package` | Build the package (wheel and sdist). | build, common, packaging |
114
+ | `bump-version` | Bump the project version. | common, packaging |
115
+ | `changelog` | Print the changelog for the current version based on git history and git-cliff. | common, packaging, release |
116
+ | `release` | Run a full release flow for package and containers. | common, containers, packaging, release |
117
+ | `build` | Build the project and its containers. | common, containers, packaging |
118
+ | `stack-up` | Bring up the development stack for the application. | containers, fastapi, web |
119
+ | `stack-down` | Bring down the development stack for the application. | containers, fastapi, web |
120
+ | `reset-db` | Reset the database by deleting the database volume. | containers, database, fastapi, web |
121
+ | `run-db-migrations` | Run database migrations. | containers, database, fastapi, web |
122
+ | `db-shell` | Open a psql shell to the database container. | containers, database, fastapi, web |
123
+ | `container-shell` | Run the debug image with an interactive shell. | containers, debug |
113
124
  <!-- end-tasks-table -->
114
125
 
115
126
  ## Docker Compose Development Stacks
@@ -237,9 +248,10 @@ The following environment variables configure package and container behavior.
237
248
  - `GITHUB_RELEASE_TAG` optional tag name to publish for the GitHub Release.
238
249
  - `GITHUB_RELEASE_NAME` optional release title to use for the GitHub Release.
239
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.
240
252
  - `RELEASE_PRE_SCRIPT` optional shell command to run before the release steps.
241
253
  - `RELEASE_POST_SCRIPT` optional shell command to run after the release completes.
242
- - Hook commands receive the following env vars: `RELEASE_TAG`, `RELEASE_VERSION`, `RELEASE_STAGE`, `RELEASE_COMPONENT`, and `RELEASE_DRY_RUN`.
254
+ - Hook commands receive the following env vars: `RELEASE_SCRIPT_PHASE`, `RELEASE_TAG`, `RELEASE_VERSION`, `RELEASE_STAGE`, `RELEASE_COMPONENT`, and `RELEASE_DRY_RUN`.
243
255
 
244
256
  #### Docker Compose settings
245
257
 
@@ -268,32 +280,32 @@ The following environment variable enables debugging output.
268
280
 
269
281
  ### Usage examples
270
282
 
271
- You can include or exclude tasks by tags in your `pyproject.toml`
283
+ By default, `tasks()` exposes the common task set. You can still include or exclude tags in your `pyproject.toml` when needed.
272
284
 
273
285
  #### Minimal setup
274
286
 
275
287
  ```toml
276
288
  [project]
277
289
  name = "simple-cli-tool"
278
- version = "0.0.1"
279
- dependencies = ["common-python-tasks==0.0.1"]
290
+ version = "0.1.0"
291
+ dependencies = ["common-python-tasks==0.1.0"]
280
292
 
281
293
  [tool.poe]
282
- include_script = "common_python_tasks:tasks(include_tags=['format', 'lint'])"
294
+ include_script = "common_python_tasks:tasks()"
283
295
  ```
284
296
 
285
- Available tasks: `format`, `lint`.
297
+ Available tasks: common defaults such as `format`, `lint`, `test`, and `build`.
286
298
 
287
299
  #### Container-based project
288
300
 
289
301
  ```toml
290
302
  [project]
291
303
  name = "containerized-app"
292
- version = "0.0.1"
293
- dependencies = ["common-python-tasks==0.0.1"]
304
+ version = "0.1.0"
305
+ dependencies = ["common-python-tasks==0.1.0"]
294
306
 
295
307
  [tool.poe]
296
- include_script = "common_python_tasks:tasks(include_tags=['format', 'lint', 'test', 'containers'])"
308
+ include_script = "common_python_tasks:tasks(include_tags=['common', 'containers'])"
297
309
 
298
310
  [tool.poe.env]
299
311
  DOCKERHUB_USERNAME = "myusername"
@@ -307,11 +319,11 @@ Available tasks: All tasks including `build-image` and `push-image`.
307
319
  ```toml
308
320
  [project]
309
321
  name = "custom-test-setup"
310
- dependencies = ["common-python-tasks==0.0.1"]
322
+ dependencies = ["common-python-tasks==0.1.0"]
311
323
  dynamic = ["version"]
312
324
 
313
325
  [tool.poe]
314
- include_script = "common_python_tasks:tasks(include_tags=['test'])"
326
+ include_script = "common_python_tasks:tasks()"
315
327
 
316
328
  [tool.pytest.ini_options]
317
329
  testpaths = ["tests", "integration"]
@@ -324,7 +336,7 @@ The `test` task will automatically use your `[tool.pytest.ini_options]` configur
324
336
 
325
337
  ### Tasks not showing up with `poe --help`
326
338
 
327
- Check your `[tool.poe]` configuration in `pyproject.toml`. Make sure you're using `include_script`, not `includes`.
339
+ Check your `[tool.poe]` configuration in `pyproject.toml`. Make sure you're using `include_script`.
328
340
 
329
341
  ```toml
330
342
  # Correct
@@ -408,8 +420,9 @@ The standard Python Dockerfile incorporates several intentional design choices.
408
420
 
409
421
  ## Notes
410
422
 
411
- - This project dogfoods itself - it uses `common-python-tasks` for its own development
412
- - `RELEASE_PRE_SCRIPT` and `RELEASE_POST_SCRIPT` hooks may not be necessary for most users, as this package promotes the use of `poetry-dynamic-versioning` and `git-cliff` to automate versioning and changelog generation based on git history. However, one advanced use case for the `RELEASE_PRE_SCRIPT` hook is to edit a file before release, such as a README that references the current stable version number. This allows you to keep the README up to date with the latest version without hardcoding it.
423
+ - This project dogfoods itself - it uses `common-python-tasks` for its own development. **That said, you must set the environment variable `PYTHONPATH=src` when running tasks locally to ensure you are using the local version of the package instead of the installed version**
424
+ - `RELEASE_UPDATE_CHANGELOG` is enabled by default for releases and prepends a tagged section into `CHANGELOG.md` before the release tag is created. Set it to a falsy value if you prefer to manage changelog commits yourself.
425
+ - `RELEASE_PRE_SCRIPT` and `RELEASE_POST_SCRIPT` hooks may not be necessary for most users, as this package promotes the use of `poetry-dynamic-versioning` and `git-cliff` to automate versioning and changelog generation based on git history. One advanced use case for the `RELEASE_PRE_SCRIPT` hook is to edit another file before release, such as a README that references the current stable version number. This allows you to keep the README up to date with the latest version without hardcoding it.
413
426
  - Contributions welcome! Open an issue/discussion to discuss changes before submitting a PR. I do not claim to have all the answers, and you can help determine the future of low-code solutions for Python. I am very interested in your feedback as I don't want to work in a vacuum
414
427
  - Alpha status: Expect breaking changes between minor versions until 1.0.0
415
428
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  This package is a collection of (very) opinionated [Poe the Poet](https://poethepoet.natn.io/guides/packaged_tasks.html) Python tasks for common Python development workflows.
4
4
 
5
+ Instead of writing your own tasks for formatting, linting, testing, packaging, and more, you can use these pre-built tasks that work out of the box with reasonable defaults and support configuration overrides when needed. In the past, I found myself copying and pasting the same task definitions across projects, and this package is my attempt to DRY up that workflow and provide a single source of truth for common Python development tasks. I hope you can use them too.
6
+
5
7
  ## Quick start
6
8
 
7
9
  ### Automated setup
@@ -9,31 +11,40 @@ This package is a collection of (very) opinionated [Poe the Poet](https://poethe
9
11
  You can add `common-python-tasks` to a new project by using the handy automated installation script.
10
12
 
11
13
  ```shell
12
- curl -sSL https://api.github.com/repos/ci-sourcerer/common-python-tasks/contents/scripts/add-common-python-tasks.sh | jq -r '.content' | base64 -d | TAGS_TO_INCLUDE="format lint test" sh
14
+ curl -sSL https://api.github.com/repos/ci-sourcerer/common-python-tasks/contents/scripts/add-common-python-tasks.sh | jq -r '.content' | base64 -d | sh
15
+ ```
16
+
17
+ To install a specific release, set the environment variable `COMMON_PYTHON_TASKS_VERSION`.
18
+
19
+ ```shell
20
+ COMMON_PYTHON_TASKS_VERSION=0.1.0 \
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)"
13
22
  ```
14
23
 
15
24
  This will complete the following steps.
16
25
 
17
26
  1. Add the latest version of `common-python-tasks` to your `pyproject.toml` dependencies
18
- 2. Configure Poe the Poet to include only the tasks with the specified tags
27
+ 2. Configure Poe the Poet to expose the default common task set
19
28
  3. Install the package using Poetry
20
29
 
21
30
  **Always review scripts before running them!** Even though I believe I write good software, it's best practice to verify any script you download from the Internet.
22
31
 
23
32
  ### Manual setup
24
33
 
25
- 1. Add `common-python-tasks` to your `pyproject.toml` and configure Poe the Poet to include the desired tasks
34
+ There's no real reason to run the automated script; I just like automating everything. You can achieve the same result by following these steps.
35
+
36
+ 1. Add `common-python-tasks` to your `pyproject.toml` and configure Poe the Poet
26
37
 
27
38
  ```toml
28
39
  [project]
29
40
  name = "my-awesome-project"
30
- version = "0.0.2"
41
+ version = "0.1.0"
31
42
  dependencies = [
32
- "common-python-tasks==0.0.2", # Always pin to a specific version
43
+ "common-python-tasks==0.1.0", # Always pin to a specific version
33
44
  ]
34
45
 
35
46
  [tool.poe]
36
- include_script = "common_python_tasks:tasks(include_tags=['format', 'lint', 'test'])" # Include or exclude tasks by tags
47
+ include_script = "common_python_tasks:tasks()" # Uses the default `common` task set
37
48
  ```
38
49
 
39
50
  2. Install the package
@@ -57,26 +68,26 @@ Internal tasks are used by other tasks and are not meant to be run directly.
57
68
  <!-- tasks-table -->
58
69
  | Task | Description | Tags |
59
70
  | - | - | - |
60
- | `build` | Build the project and its containers (when `containers` tag is included) | packaging, containers |
61
- | `build-image` | Build the container image for this project using the Dockerfile template (and configured extensions) | containers, build |
62
- | `build-package` | Build the package (wheel and sdist) | packaging, build |
63
- | `bump-version` | Bump the project version, defaulting to an inferred semantic bump from git history | packaging |
64
- | `changelog` | Print the changelog for the current version based on git history | packaging, release |
65
- | `clean` | Clean up temporary files and directories | clean |
66
- | `container-shell` | Run the debug image with an interactive shell | containers, debug |
67
- | `db-shell` | Open a psql shell to the database container | web, containers, database |
68
- | `format` | Format code with autoflake, black, and isort | format |
69
- | `lint` | Lint Python code with autoflake, black, isort, and flake8 | lint |
70
- | `publish-package` | Publish the package to the PyPI server | packaging |
71
- | `publish-github-release` | Publish or update a GitHub Release and attach built distribution assets | packaging, release |
72
- | `push-image` | Push the Docker image to the container registry | containers, packaging, release |
73
- | `release` | Run package release flow and publish containers when `containers` tag is included. Supports optional `RELEASE_PRE_SCRIPT` and `RELEASE_POST_SCRIPT` hooks. | packaging, release |
74
- | `reset-db` | Reset the database by deleting the database volume | web, containers, database |
75
- | `run-container` | Run the Docker image as a container | containers |
76
- | `run-db-migrations` | Run database migrations | web, containers, database |
77
- | `stack-down` | Bring down the development stack for the application | web, containers |
78
- | `stack-up` | Bring up the development stack for the application | web, containers |
79
- | `test` | Run the test suite with coverage | test |
71
+ | `test` | Run the test suite with coverage (if `pytest-cov` is installed). | common, test |
72
+ | `clean` | Clean up temporary files and directories. | clean, common |
73
+ | `format` | Format Python code with autoflake, black, and isort. | common, format |
74
+ | `lint` | Lint Python code with autoflake, black, isort, and flake8. | common, lint |
75
+ | `build-image` | Build the container image for this project using the Dockerfile template. | build, containers |
76
+ | `run-container` | Run the Docker image as a container for this project. By default (when `tag` is `None`) this will run the most-recently-built tag for the project's image. | containers |
77
+ | `push-image` | Push the Docker image for this project to the container registry. | containers, packaging, release |
78
+ | `publish-package` | Publish the package to the PyPI server. | common, packaging |
79
+ | `publish-github-release` | Publish or update a GitHub Release for the current repository. | common, packaging, release |
80
+ | `build-package` | Build the package (wheel and sdist). | build, common, packaging |
81
+ | `bump-version` | Bump the project version. | common, packaging |
82
+ | `changelog` | Print the changelog for the current version based on git history and git-cliff. | common, packaging, release |
83
+ | `release` | Run a full release flow for package and containers. | common, containers, packaging, release |
84
+ | `build` | Build the project and its containers. | common, containers, packaging |
85
+ | `stack-up` | Bring up the development stack for the application. | containers, fastapi, web |
86
+ | `stack-down` | Bring down the development stack for the application. | containers, fastapi, web |
87
+ | `reset-db` | Reset the database by deleting the database volume. | containers, database, fastapi, web |
88
+ | `run-db-migrations` | Run database migrations. | containers, database, fastapi, web |
89
+ | `db-shell` | Open a psql shell to the database container. | containers, database, fastapi, web |
90
+ | `container-shell` | Run the debug image with an interactive shell. | containers, debug |
80
91
  <!-- end-tasks-table -->
81
92
 
82
93
  ## Docker Compose Development Stacks
@@ -204,9 +215,10 @@ The following environment variables configure package and container behavior.
204
215
  - `GITHUB_RELEASE_TAG` optional tag name to publish for the GitHub Release.
205
216
  - `GITHUB_RELEASE_NAME` optional release title to use for the GitHub Release.
206
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.
207
219
  - `RELEASE_PRE_SCRIPT` optional shell command to run before the release steps.
208
220
  - `RELEASE_POST_SCRIPT` optional shell command to run after the release completes.
209
- - Hook commands receive the following env vars: `RELEASE_TAG`, `RELEASE_VERSION`, `RELEASE_STAGE`, `RELEASE_COMPONENT`, and `RELEASE_DRY_RUN`.
221
+ - Hook commands receive the following env vars: `RELEASE_SCRIPT_PHASE`, `RELEASE_TAG`, `RELEASE_VERSION`, `RELEASE_STAGE`, `RELEASE_COMPONENT`, and `RELEASE_DRY_RUN`.
210
222
 
211
223
  #### Docker Compose settings
212
224
 
@@ -235,32 +247,32 @@ The following environment variable enables debugging output.
235
247
 
236
248
  ### Usage examples
237
249
 
238
- You can include or exclude tasks by tags in your `pyproject.toml`
250
+ By default, `tasks()` exposes the common task set. You can still include or exclude tags in your `pyproject.toml` when needed.
239
251
 
240
252
  #### Minimal setup
241
253
 
242
254
  ```toml
243
255
  [project]
244
256
  name = "simple-cli-tool"
245
- version = "0.0.1"
246
- dependencies = ["common-python-tasks==0.0.1"]
257
+ version = "0.1.0"
258
+ dependencies = ["common-python-tasks==0.1.0"]
247
259
 
248
260
  [tool.poe]
249
- include_script = "common_python_tasks:tasks(include_tags=['format', 'lint'])"
261
+ include_script = "common_python_tasks:tasks()"
250
262
  ```
251
263
 
252
- Available tasks: `format`, `lint`.
264
+ Available tasks: common defaults such as `format`, `lint`, `test`, and `build`.
253
265
 
254
266
  #### Container-based project
255
267
 
256
268
  ```toml
257
269
  [project]
258
270
  name = "containerized-app"
259
- version = "0.0.1"
260
- dependencies = ["common-python-tasks==0.0.1"]
271
+ version = "0.1.0"
272
+ dependencies = ["common-python-tasks==0.1.0"]
261
273
 
262
274
  [tool.poe]
263
- include_script = "common_python_tasks:tasks(include_tags=['format', 'lint', 'test', 'containers'])"
275
+ include_script = "common_python_tasks:tasks(include_tags=['common', 'containers'])"
264
276
 
265
277
  [tool.poe.env]
266
278
  DOCKERHUB_USERNAME = "myusername"
@@ -274,11 +286,11 @@ Available tasks: All tasks including `build-image` and `push-image`.
274
286
  ```toml
275
287
  [project]
276
288
  name = "custom-test-setup"
277
- dependencies = ["common-python-tasks==0.0.1"]
289
+ dependencies = ["common-python-tasks==0.1.0"]
278
290
  dynamic = ["version"]
279
291
 
280
292
  [tool.poe]
281
- include_script = "common_python_tasks:tasks(include_tags=['test'])"
293
+ include_script = "common_python_tasks:tasks()"
282
294
 
283
295
  [tool.pytest.ini_options]
284
296
  testpaths = ["tests", "integration"]
@@ -291,7 +303,7 @@ The `test` task will automatically use your `[tool.pytest.ini_options]` configur
291
303
 
292
304
  ### Tasks not showing up with `poe --help`
293
305
 
294
- Check your `[tool.poe]` configuration in `pyproject.toml`. Make sure you're using `include_script`, not `includes`.
306
+ Check your `[tool.poe]` configuration in `pyproject.toml`. Make sure you're using `include_script`.
295
307
 
296
308
  ```toml
297
309
  # Correct
@@ -375,7 +387,8 @@ The standard Python Dockerfile incorporates several intentional design choices.
375
387
 
376
388
  ## Notes
377
389
 
378
- - This project dogfoods itself - it uses `common-python-tasks` for its own development
379
- - `RELEASE_PRE_SCRIPT` and `RELEASE_POST_SCRIPT` hooks may not be necessary for most users, as this package promotes the use of `poetry-dynamic-versioning` and `git-cliff` to automate versioning and changelog generation based on git history. However, one advanced use case for the `RELEASE_PRE_SCRIPT` hook is to edit a file before release, such as a README that references the current stable version number. This allows you to keep the README up to date with the latest version without hardcoding it.
390
+ - This project dogfoods itself - it uses `common-python-tasks` for its own development. **That said, you must set the environment variable `PYTHONPATH=src` when running tasks locally to ensure you are using the local version of the package instead of the installed version**
391
+ - `RELEASE_UPDATE_CHANGELOG` is enabled by default for releases and prepends a tagged section into `CHANGELOG.md` before the release tag is created. Set it to a falsy value if you prefer to manage changelog commits yourself.
392
+ - `RELEASE_PRE_SCRIPT` and `RELEASE_POST_SCRIPT` hooks may not be necessary for most users, as this package promotes the use of `poetry-dynamic-versioning` and `git-cliff` to automate versioning and changelog generation based on git history. One advanced use case for the `RELEASE_PRE_SCRIPT` hook is to edit another file before release, such as a README that references the current stable version number. This allows you to keep the README up to date with the latest version without hardcoding it.
380
393
  - Contributions welcome! Open an issue/discussion to discuss changes before submitting a PR. I do not claim to have all the answers, and you can help determine the future of low-code solutions for Python. I am very interested in your feedback as I don't want to work in a vacuum
381
394
  - Alpha status: Expect breaking changes between minor versions until 1.0.0
@@ -18,10 +18,10 @@ classifiers = [
18
18
  ]
19
19
  dependencies = [
20
20
  "autoflake (>=2.3.1,<3.0.0)",
21
- "black (>=25.11.0,<26.0.0)",
21
+ "black (>=26.3.1,<27.0.0)",
22
22
  "dunamai (>=1.25.0,<2.0.0)",
23
23
  "flake8 (>=7.3.0,<8.0.0)",
24
- "isort (>=7.0.0,<8.0.0)",
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
27
  "pytest (>=9.0.1,<10.0.0)",
@@ -30,7 +30,7 @@ dependencies = [
30
30
  "git-cliff (>=2.12.0,<3.0.0)",
31
31
  ]
32
32
  dynamic = []
33
- version = "0.0.3"
33
+ version = "0.1.0"
34
34
 
35
35
  [project.urls]
36
36
  Homepage = "http://github.com/ci-sourcerer/common-python-tasks"
@@ -38,7 +38,13 @@ Source = "http://github.com/ci-sourcerer/common-python-tasks.git"
38
38
  Issues = "http://github.com/ci-sourcerer/common-python-tasks/issues"
39
39
 
40
40
  [tool.poe]
41
- include_script = "common_python_tasks:tasks(exclude_tags=['fastapi', 'containers'])"
41
+ include_script = "common_python_tasks:tasks(exclude_tags=['containers'])"
42
+
43
+ [tool.poe.env]
44
+ PYTHONPATH = "src"
45
+ RELEASE_UPDATE_CHANGELOG = "1"
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"
42
48
 
43
49
  [tool.poetry.requires-plugins]
44
50
  poetry-dynamic-versioning = { version = ">=1.0.0,<2.0.0", extras = ["plugin"] }
@@ -0,0 +1,27 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ if TYPE_CHECKING:
4
+ from typing import Sequence
5
+
6
+ from poethepoet_tasks import TaskCollection
7
+
8
+ __all__ = ["TaskCollection"]
9
+
10
+
11
+ def tasks(
12
+ include_tags: "Sequence[str] | None" = None,
13
+ exclude_tags: "Sequence[str]" = tuple(),
14
+ ) -> dict:
15
+ from .tasks import tasks as task_collection
16
+
17
+ if include_tags is None and not exclude_tags:
18
+ include_tags = ("common",)
19
+ elif include_tags is None:
20
+ include_tags = tuple()
21
+
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
@@ -0,0 +1,108 @@
1
+ import importlib
2
+ import inspect
3
+ import os
4
+ import re
5
+ import sys
6
+
7
+ from .tasks import tasks
8
+
9
+ __all__ = ["get_available_tasks", "print_available_tasks"]
10
+
11
+
12
+ def get_available_tasks(internal: bool = False) -> list[str]:
13
+ """Return available task names for this package.
14
+
15
+ Args:
16
+ internal: When True, include internal task names starting with '_'.
17
+
18
+ Returns:
19
+ A list of task names.
20
+ """
21
+ return [
22
+ task_name
23
+ for task_name in tasks()["tasks"]
24
+ if internal or not task_name.startswith("_")
25
+ ]
26
+
27
+
28
+ def _extract_docstring_excerpt(doc: str) -> str:
29
+ excerpt = []
30
+ for line in doc.strip().splitlines():
31
+ if re.match(r"^(Args?|Arguments?|Parameters?):\s*$", line):
32
+ break
33
+ excerpt.append(line)
34
+
35
+ while excerpt and excerpt[-1].strip() == "":
36
+ excerpt.pop()
37
+
38
+ return "\n".join(excerpt)
39
+
40
+
41
+ def _get_task_docstring(task_name: str) -> str | None:
42
+
43
+ task_config = tasks()["tasks"].get(task_name)
44
+ if not isinstance(task_config, dict):
45
+ return None
46
+
47
+ script = task_config.get("script")
48
+ if not isinstance(script, str) or ":" not in script:
49
+ return None
50
+
51
+ module_name, func_name = script.split(":", 1)
52
+
53
+ try:
54
+ module = importlib.import_module(module_name)
55
+ func = getattr(module, func_name)
56
+ except (ImportError, AttributeError):
57
+ return None
58
+
59
+ doc = inspect.getdoc(func)
60
+ if not doc:
61
+ return None
62
+
63
+ return _extract_docstring_excerpt(doc)
64
+
65
+
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
70
+
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
+ return sorted(tags) or None
78
+
79
+
80
+ def print_available_tasks(internal: bool = False, include_docs: bool = False) -> None:
81
+ """Print available tasks.
82
+
83
+ Args:
84
+ internal: Whether or not to include internal task names starting with '_'.
85
+ include_docs: Whether or not to include task docstrings.
86
+ """
87
+ print("\nAvailable tasks in this release:\n")
88
+ for task_name in get_available_tasks(internal=internal):
89
+ print(f" - {task_name}")
90
+ if include_docs:
91
+ doc = _get_task_docstring(task_name)
92
+ if doc:
93
+ for line in doc.splitlines():
94
+ print(f" {line}")
95
+ print()
96
+
97
+
98
+ if __name__ == "__main__":
99
+ print(
100
+ "common_python_tasks is not intended to be run as a standalone script. Invoke a task via poethepoet.",
101
+ file=sys.stderr if len(sys.argv) > 1 else sys.stdout,
102
+ )
103
+
104
+ if len(sys.argv) == 1:
105
+ debug = os.getenv("COMMON_PYTHON_TASKS_LOG_LEVEL", "info").lower() == "debug"
106
+ print_available_tasks(include_docs=debug)
107
+ else:
108
+ sys.exit(1)