invoke-tasklib 0.0.1__tar.gz → 0.0.2__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.
- invoke_tasklib-0.0.2/PKG-INFO +246 -0
- invoke_tasklib-0.0.2/README.md +218 -0
- {invoke_tasklib-0.0.1 → invoke_tasklib-0.0.2}/invoke_tasklib/__init__.py +26 -3
- invoke_tasklib-0.0.2/invoke_tasklib/clean.py +55 -0
- {invoke_tasklib-0.0.1 → invoke_tasklib-0.0.2}/invoke_tasklib/config.py +15 -0
- {invoke_tasklib-0.0.1 → invoke_tasklib-0.0.2}/invoke_tasklib/doc.py +2 -2
- {invoke_tasklib-0.0.1 → invoke_tasklib-0.0.2}/invoke_tasklib/env.py +22 -14
- {invoke_tasklib-0.0.1 → invoke_tasklib-0.0.2}/invoke_tasklib/lint.py +13 -0
- invoke_tasklib-0.0.2/invoke_tasklib/security.py +22 -0
- {invoke_tasklib-0.0.1 → invoke_tasklib-0.0.2}/invoke_tasklib/test.py +47 -2
- invoke_tasklib-0.0.2/pyproject.toml +279 -0
- invoke_tasklib-0.0.1/PKG-INFO +0 -163
- invoke_tasklib-0.0.1/README.md +0 -135
- invoke_tasklib-0.0.1/pyproject.toml +0 -80
- {invoke_tasklib-0.0.1 → invoke_tasklib-0.0.2}/.gitignore +0 -0
- {invoke_tasklib-0.0.1 → invoke_tasklib-0.0.2}/LICENSE +0 -0
- {invoke_tasklib-0.0.1 → invoke_tasklib-0.0.2}/invoke_tasklib/format.py +0 -0
- {invoke_tasklib-0.0.1 → invoke_tasklib-0.0.2}/invoke_tasklib/py.typed +0 -0
- {invoke_tasklib-0.0.1 → invoke_tasklib-0.0.2}/invoke_tasklib/release.py +0 -0
- {invoke_tasklib-0.0.1 → invoke_tasklib-0.0.2}/invoke_tasklib/types.py +0 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: invoke-tasklib
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Reusable Invoke tasks shared across Python projects.
|
|
5
|
+
Project-URL: Homepage, https://github.com/durandtibo/invoke-tasklib
|
|
6
|
+
Project-URL: Repository, https://github.com/durandtibo/invoke-tasklib
|
|
7
|
+
Project-URL: Documentation, https://durandtibo.github.io/invoke-tasklib/
|
|
8
|
+
Project-URL: Changelog, https://github.com/durandtibo/invoke-tasklib/releases
|
|
9
|
+
Project-URL: Issues, https://github.com/durandtibo/invoke-tasklib/issues
|
|
10
|
+
Author-email: Thibaut Durand <durand.tibo+gh@gmail.com>
|
|
11
|
+
License-Expression: BSD-3-Clause
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: automation,devtools,invoke,tasks
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: invoke>=3.0
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# invoke-tasklib
|
|
30
|
+
|
|
31
|
+
<p align="center">
|
|
32
|
+
<a href="https://github.com/durandtibo/invoke-tasklib/actions/workflows/ci.yaml">
|
|
33
|
+
<img alt="CI" src="https://github.com/durandtibo/invoke-tasklib/actions/workflows/ci.yaml/badge.svg">
|
|
34
|
+
</a>
|
|
35
|
+
<a href="https://github.com/durandtibo/invoke-tasklib/actions/workflows/nightly-package.yaml">
|
|
36
|
+
<img alt="Nightly Package Tests" src="https://github.com/durandtibo/invoke-tasklib/actions/workflows/nightly-package.yaml/badge.svg">
|
|
37
|
+
</a>
|
|
38
|
+
<a href="https://codecov.io/gh/durandtibo/invoke-tasklib">
|
|
39
|
+
<img alt="Codecov" src="https://codecov.io/gh/durandtibo/invoke-tasklib/branch/main/graph/badge.svg">
|
|
40
|
+
</a>
|
|
41
|
+
<br/>
|
|
42
|
+
<a href="https://durandtibo.github.io/invoke-tasklib/">
|
|
43
|
+
<img alt="Documentation" src="https://github.com/durandtibo/invoke-tasklib/actions/workflows/release-docs.yaml/badge.svg">
|
|
44
|
+
</a>
|
|
45
|
+
<a href="https://durandtibo.github.io/invoke-tasklib/dev/">
|
|
46
|
+
<img alt="Documentation" src="https://github.com/durandtibo/invoke-tasklib/actions/workflows/release-docs-dev.yaml/badge.svg">
|
|
47
|
+
</a>
|
|
48
|
+
<br/>
|
|
49
|
+
<a href="https://github.com/psf/black">
|
|
50
|
+
<img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg">
|
|
51
|
+
</a>
|
|
52
|
+
<a href="https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings">
|
|
53
|
+
<img alt="Doc style: google" src="https://img.shields.io/badge/%20style-google-3666d6.svg">
|
|
54
|
+
</a>
|
|
55
|
+
<a href="https://github.com/astral-sh/ruff">
|
|
56
|
+
<img alt="Ruff" src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json">
|
|
57
|
+
</a>
|
|
58
|
+
<a href="https://github.com/guilatrova/tryceratops">
|
|
59
|
+
<img alt="try/except style: tryceratops" src="https://img.shields.io/badge/try%2Fexcept%20style-tryceratops%20%F0%9F%A6%96%E2%9C%A8-black">
|
|
60
|
+
</a>
|
|
61
|
+
<br/>
|
|
62
|
+
<a href="https://pypi.org/project/invoke-tasklib/">
|
|
63
|
+
<img alt="PYPI version" src="https://img.shields.io/pypi/v/invoke-tasklib">
|
|
64
|
+
</a>
|
|
65
|
+
<a href="https://pypi.org/project/invoke-tasklib/">
|
|
66
|
+
<img alt="Python" src="https://img.shields.io/pypi/pyversions/invoke-tasklib.svg">
|
|
67
|
+
</a>
|
|
68
|
+
<a href="https://opensource.org/licenses/BSD-3-Clause">
|
|
69
|
+
<img alt="BSD-3-Clause" src="https://img.shields.io/pypi/l/invoke-tasklib">
|
|
70
|
+
</a>
|
|
71
|
+
<br/>
|
|
72
|
+
<a href="https://pepy.tech/project/invoke-tasklib">
|
|
73
|
+
<img alt="Downloads" src="https://static.pepy.tech/badge/invoke-tasklib">
|
|
74
|
+
</a>
|
|
75
|
+
<a href="https://pepy.tech/project/invoke-tasklib">
|
|
76
|
+
<img alt="Monthly downloads" src="https://static.pepy.tech/badge/invoke-tasklib/month">
|
|
77
|
+
</a>
|
|
78
|
+
<a href="https://github.com/durandtibo/invoke-tasklib/stargazers">
|
|
79
|
+
<img alt="GitHub stars" src="https://img.shields.io/github/stars/durandtibo/invoke-tasklib">
|
|
80
|
+
</a>
|
|
81
|
+
<br/>
|
|
82
|
+
<a href="https://www.bestpractices.dev/projects/14387">
|
|
83
|
+
<img src="https://www.bestpractices.dev/projects/14387/badge">
|
|
84
|
+
</a>
|
|
85
|
+
<a href="https://scorecard.dev/viewer/?uri=github.com/durandtibo/invoke-tasklib">
|
|
86
|
+
<img alt="OpenSSF Scorecard" src="https://img.shields.io/badge/dynamic/json?url=https://api.scorecard.dev/projects/github.com/durandtibo/invoke-tasklib&label=openssf%20scorecard&query=score">
|
|
87
|
+
</a>
|
|
88
|
+
</p>
|
|
89
|
+
|
|
90
|
+
Reusable [Invoke](https://www.pyinvoke.org/) tasks shared across Python
|
|
91
|
+
projects: formatting, linting, type-checking, testing, environment setup,
|
|
92
|
+
security auditing, releasing, documentation, and cleanup — all as a single
|
|
93
|
+
importable package instead of a `tasks.py` you copy-paste and let drift
|
|
94
|
+
between repositories.
|
|
95
|
+
|
|
96
|
+
:book: **Full documentation:** <https://durandtibo.github.io/invoke-tasklib/>
|
|
97
|
+
|
|
98
|
+
## Install
|
|
99
|
+
|
|
100
|
+
```shell
|
|
101
|
+
uv add --dev invoke-tasklib
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
or with `pip`:
|
|
105
|
+
|
|
106
|
+
```shell
|
|
107
|
+
pip install invoke-tasklib
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Quickstart
|
|
111
|
+
|
|
112
|
+
**1. Add a `tasks.py`** at the root of your project:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
# tasks.py
|
|
116
|
+
from invoke_tasklib import ns
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**2. Add an `invoke.yaml`** with, at minimum, your package name:
|
|
120
|
+
|
|
121
|
+
```yaml
|
|
122
|
+
tasklib:
|
|
123
|
+
package:
|
|
124
|
+
name: my_package
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**3. List and run the tasks:**
|
|
128
|
+
|
|
129
|
+
```shell
|
|
130
|
+
invoke --list
|
|
131
|
+
invoke format.check-python lint.check-lint
|
|
132
|
+
invoke test.unit
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
If a task fails because a tool like `ruff` or `pytest` isn't installed yet,
|
|
136
|
+
run `invoke env.install` first.
|
|
137
|
+
|
|
138
|
+
See the [Get Started guide](https://durandtibo.github.io/invoke-tasklib/get_started/)
|
|
139
|
+
for a full walkthrough.
|
|
140
|
+
|
|
141
|
+
## Tasks
|
|
142
|
+
|
|
143
|
+
Tasks are grouped into namespaces, one per module. Each task either
|
|
144
|
+
**checks/reports** (read-only, exits non-zero on violations) or
|
|
145
|
+
**mutates** (formats, builds, publishes) — the table below flags which.
|
|
146
|
+
|
|
147
|
+
| Namespace | Task | Behavior | Mutates? |
|
|
148
|
+
| ------------- | ----------------------------- | ---------------------------------------------------------------------------------------- | :------: |
|
|
149
|
+
| `format` | `format.check-python` | Check Python formatting with ruff | ❌ |
|
|
150
|
+
| `format` | `format.fix-python` | Format Python code with ruff | ✅ |
|
|
151
|
+
| `format` | `format.check-docstrings` | Check docstring formatting with docformatter | ❌ |
|
|
152
|
+
| `format` | `format.fix-docstrings` | Format docstrings with docformatter | ✅ |
|
|
153
|
+
| `format` | `format.check-shell` | Check shell scripts with shellcheck | ❌ |
|
|
154
|
+
| `format` | `format.fix-shell` | Format shell scripts with shfmt | ✅ |
|
|
155
|
+
| `lint` | `lint.check-lint` | Check linting with ruff | ❌ |
|
|
156
|
+
| `lint` | `lint.fix` | Fix auto-fixable linting issues with ruff | ✅ |
|
|
157
|
+
| `types` | `types.check` | Check type hints with pyright | ❌ |
|
|
158
|
+
| `test` | `test.doctest` | Run doctests on source code and markdown files | ❌ |
|
|
159
|
+
| `test` | `test.doctest-src` | Run doctests on source code | ❌ |
|
|
160
|
+
| `test` | `test.doctest-markdown` | Run doctests on Python examples in markdown files | ❌ |
|
|
161
|
+
| `test` | `test.unit` | Run unit tests | ❌ |
|
|
162
|
+
| `test` | `test.integration` | Run integration tests | ❌ |
|
|
163
|
+
| `test` | `test.functional` | Run functional tests | ❌ |
|
|
164
|
+
| `test` | `test.all` | Run unit, integration, and functional tests | ❌ |
|
|
165
|
+
| `test` | `test.coverage-report` | Generate an HTML/terminal report from existing coverage data | ❌ |
|
|
166
|
+
| `test` | `test.benchmark` | Run performance benchmarks | ❌ |
|
|
167
|
+
| `env` | `env.create-venv` | Create a virtual environment and install invoke | ✅ |
|
|
168
|
+
| `env` | `env.install` | Install project dependencies and the package (editable) | ✅ |
|
|
169
|
+
| `env` | `env.update` | Update dependencies and pre-commit hooks | ✅ |
|
|
170
|
+
| `env` | `env.show-installed-packages` | Show the installed packages | ❌ |
|
|
171
|
+
| `env` | `env.show-python-config` | Show the Python configuration | ❌ |
|
|
172
|
+
| `release` | `release.build` | Build the package and verify installation (`--check` also validates metadata with twine) | ✅ |
|
|
173
|
+
| `release` | `release.pypi` | Build and publish the package to PyPI | ✅ |
|
|
174
|
+
| `doc` | `doc.publish-dev` | Publish development (unstable) docs | ✅ |
|
|
175
|
+
| `doc` | `doc.publish-latest` | Publish latest (stable) docs | ✅ |
|
|
176
|
+
| `security` | `security.audit` | Audit installed dependencies for known vulnerabilities with pip-audit | ❌ |
|
|
177
|
+
| _(top-level)_ | `clean` | Remove build artifacts and caches | ✅ |
|
|
178
|
+
|
|
179
|
+
`format.*` and `lint.*` follow a naming convention: read-only checks are
|
|
180
|
+
named `check_<target>`, and the matching in-place fixer is named
|
|
181
|
+
`fix_<target>` (e.g. `check_python`/`fix_python`), so the counterpart of a
|
|
182
|
+
task is always easy to find. Follow this convention when adding new tasks
|
|
183
|
+
to these namespaces.
|
|
184
|
+
|
|
185
|
+
For task-by-task details, options (like `--cov` on `test.*` tasks), and the
|
|
186
|
+
tools each one wraps, see the
|
|
187
|
+
[User Guide](https://durandtibo.github.io/invoke-tasklib/uguide/).
|
|
188
|
+
|
|
189
|
+
## Configuration
|
|
190
|
+
|
|
191
|
+
Only `tasklib.package.name` is required — everything else has a sensible
|
|
192
|
+
default derived from it:
|
|
193
|
+
|
|
194
|
+
```yaml
|
|
195
|
+
tasklib:
|
|
196
|
+
package:
|
|
197
|
+
name: my_package # required
|
|
198
|
+
python_version: "3.14" # used by env.create-venv
|
|
199
|
+
paths:
|
|
200
|
+
src: src/my_package # default: src/<package.name>
|
|
201
|
+
tests: tests
|
|
202
|
+
unit_tests: tests/unit # default: <tests>/unit
|
|
203
|
+
integration_tests: tests/integration # default: <tests>/integration
|
|
204
|
+
functional_tests: tests/functional # default: <tests>/functional
|
|
205
|
+
benchmarks: tests/benchmarks # default: <tests>/benchmarks
|
|
206
|
+
docs_config: docs/mkdocs.yml
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
See the [Config reference](https://durandtibo.github.io/invoke-tasklib/uguide/config/)
|
|
210
|
+
for how each path default is derived.
|
|
211
|
+
|
|
212
|
+
## Composing a custom subset of tasks
|
|
213
|
+
|
|
214
|
+
Need a different set of tasks, or a one-off task alongside the shared ones?
|
|
215
|
+
Import individual task modules instead of the pre-built `ns`:
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
from invoke import Collection
|
|
219
|
+
from invoke_tasklib import lint, test
|
|
220
|
+
|
|
221
|
+
from . import my_custom_task
|
|
222
|
+
|
|
223
|
+
ns = Collection(lint, test, my_custom_task)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Prefer adding a config knob to a shared task over forking it; reserve custom
|
|
227
|
+
composition for things that are genuinely one-off to a single project.
|
|
228
|
+
|
|
229
|
+
## API Stability
|
|
230
|
+
|
|
231
|
+
:warning: `invoke-tasklib` is under active development and its API is not
|
|
232
|
+
yet stable — pin a specific version in your project's dependencies for
|
|
233
|
+
consistent behavior across releases.
|
|
234
|
+
|
|
235
|
+
## Contributing
|
|
236
|
+
|
|
237
|
+
Contributions are welcome! Please open an issue first to discuss
|
|
238
|
+
significant changes. See the
|
|
239
|
+
[developer guide](https://durandtibo.github.io/invoke-tasklib/dev/development/)
|
|
240
|
+
for how to set up a development environment.
|
|
241
|
+
|
|
242
|
+
## License
|
|
243
|
+
|
|
244
|
+
`invoke-tasklib` is licensed under the BSD 3-Clause "New" or "Revised"
|
|
245
|
+
license available in
|
|
246
|
+
[LICENSE](https://github.com/durandtibo/invoke-tasklib/blob/main/LICENSE).
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# invoke-tasklib
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://github.com/durandtibo/invoke-tasklib/actions/workflows/ci.yaml">
|
|
5
|
+
<img alt="CI" src="https://github.com/durandtibo/invoke-tasklib/actions/workflows/ci.yaml/badge.svg">
|
|
6
|
+
</a>
|
|
7
|
+
<a href="https://github.com/durandtibo/invoke-tasklib/actions/workflows/nightly-package.yaml">
|
|
8
|
+
<img alt="Nightly Package Tests" src="https://github.com/durandtibo/invoke-tasklib/actions/workflows/nightly-package.yaml/badge.svg">
|
|
9
|
+
</a>
|
|
10
|
+
<a href="https://codecov.io/gh/durandtibo/invoke-tasklib">
|
|
11
|
+
<img alt="Codecov" src="https://codecov.io/gh/durandtibo/invoke-tasklib/branch/main/graph/badge.svg">
|
|
12
|
+
</a>
|
|
13
|
+
<br/>
|
|
14
|
+
<a href="https://durandtibo.github.io/invoke-tasklib/">
|
|
15
|
+
<img alt="Documentation" src="https://github.com/durandtibo/invoke-tasklib/actions/workflows/release-docs.yaml/badge.svg">
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://durandtibo.github.io/invoke-tasklib/dev/">
|
|
18
|
+
<img alt="Documentation" src="https://github.com/durandtibo/invoke-tasklib/actions/workflows/release-docs-dev.yaml/badge.svg">
|
|
19
|
+
</a>
|
|
20
|
+
<br/>
|
|
21
|
+
<a href="https://github.com/psf/black">
|
|
22
|
+
<img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg">
|
|
23
|
+
</a>
|
|
24
|
+
<a href="https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings">
|
|
25
|
+
<img alt="Doc style: google" src="https://img.shields.io/badge/%20style-google-3666d6.svg">
|
|
26
|
+
</a>
|
|
27
|
+
<a href="https://github.com/astral-sh/ruff">
|
|
28
|
+
<img alt="Ruff" src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json">
|
|
29
|
+
</a>
|
|
30
|
+
<a href="https://github.com/guilatrova/tryceratops">
|
|
31
|
+
<img alt="try/except style: tryceratops" src="https://img.shields.io/badge/try%2Fexcept%20style-tryceratops%20%F0%9F%A6%96%E2%9C%A8-black">
|
|
32
|
+
</a>
|
|
33
|
+
<br/>
|
|
34
|
+
<a href="https://pypi.org/project/invoke-tasklib/">
|
|
35
|
+
<img alt="PYPI version" src="https://img.shields.io/pypi/v/invoke-tasklib">
|
|
36
|
+
</a>
|
|
37
|
+
<a href="https://pypi.org/project/invoke-tasklib/">
|
|
38
|
+
<img alt="Python" src="https://img.shields.io/pypi/pyversions/invoke-tasklib.svg">
|
|
39
|
+
</a>
|
|
40
|
+
<a href="https://opensource.org/licenses/BSD-3-Clause">
|
|
41
|
+
<img alt="BSD-3-Clause" src="https://img.shields.io/pypi/l/invoke-tasklib">
|
|
42
|
+
</a>
|
|
43
|
+
<br/>
|
|
44
|
+
<a href="https://pepy.tech/project/invoke-tasklib">
|
|
45
|
+
<img alt="Downloads" src="https://static.pepy.tech/badge/invoke-tasklib">
|
|
46
|
+
</a>
|
|
47
|
+
<a href="https://pepy.tech/project/invoke-tasklib">
|
|
48
|
+
<img alt="Monthly downloads" src="https://static.pepy.tech/badge/invoke-tasklib/month">
|
|
49
|
+
</a>
|
|
50
|
+
<a href="https://github.com/durandtibo/invoke-tasklib/stargazers">
|
|
51
|
+
<img alt="GitHub stars" src="https://img.shields.io/github/stars/durandtibo/invoke-tasklib">
|
|
52
|
+
</a>
|
|
53
|
+
<br/>
|
|
54
|
+
<a href="https://www.bestpractices.dev/projects/14387">
|
|
55
|
+
<img src="https://www.bestpractices.dev/projects/14387/badge">
|
|
56
|
+
</a>
|
|
57
|
+
<a href="https://scorecard.dev/viewer/?uri=github.com/durandtibo/invoke-tasklib">
|
|
58
|
+
<img alt="OpenSSF Scorecard" src="https://img.shields.io/badge/dynamic/json?url=https://api.scorecard.dev/projects/github.com/durandtibo/invoke-tasklib&label=openssf%20scorecard&query=score">
|
|
59
|
+
</a>
|
|
60
|
+
</p>
|
|
61
|
+
|
|
62
|
+
Reusable [Invoke](https://www.pyinvoke.org/) tasks shared across Python
|
|
63
|
+
projects: formatting, linting, type-checking, testing, environment setup,
|
|
64
|
+
security auditing, releasing, documentation, and cleanup — all as a single
|
|
65
|
+
importable package instead of a `tasks.py` you copy-paste and let drift
|
|
66
|
+
between repositories.
|
|
67
|
+
|
|
68
|
+
:book: **Full documentation:** <https://durandtibo.github.io/invoke-tasklib/>
|
|
69
|
+
|
|
70
|
+
## Install
|
|
71
|
+
|
|
72
|
+
```shell
|
|
73
|
+
uv add --dev invoke-tasklib
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
or with `pip`:
|
|
77
|
+
|
|
78
|
+
```shell
|
|
79
|
+
pip install invoke-tasklib
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Quickstart
|
|
83
|
+
|
|
84
|
+
**1. Add a `tasks.py`** at the root of your project:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
# tasks.py
|
|
88
|
+
from invoke_tasklib import ns
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**2. Add an `invoke.yaml`** with, at minimum, your package name:
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
tasklib:
|
|
95
|
+
package:
|
|
96
|
+
name: my_package
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**3. List and run the tasks:**
|
|
100
|
+
|
|
101
|
+
```shell
|
|
102
|
+
invoke --list
|
|
103
|
+
invoke format.check-python lint.check-lint
|
|
104
|
+
invoke test.unit
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
If a task fails because a tool like `ruff` or `pytest` isn't installed yet,
|
|
108
|
+
run `invoke env.install` first.
|
|
109
|
+
|
|
110
|
+
See the [Get Started guide](https://durandtibo.github.io/invoke-tasklib/get_started/)
|
|
111
|
+
for a full walkthrough.
|
|
112
|
+
|
|
113
|
+
## Tasks
|
|
114
|
+
|
|
115
|
+
Tasks are grouped into namespaces, one per module. Each task either
|
|
116
|
+
**checks/reports** (read-only, exits non-zero on violations) or
|
|
117
|
+
**mutates** (formats, builds, publishes) — the table below flags which.
|
|
118
|
+
|
|
119
|
+
| Namespace | Task | Behavior | Mutates? |
|
|
120
|
+
| ------------- | ----------------------------- | ---------------------------------------------------------------------------------------- | :------: |
|
|
121
|
+
| `format` | `format.check-python` | Check Python formatting with ruff | ❌ |
|
|
122
|
+
| `format` | `format.fix-python` | Format Python code with ruff | ✅ |
|
|
123
|
+
| `format` | `format.check-docstrings` | Check docstring formatting with docformatter | ❌ |
|
|
124
|
+
| `format` | `format.fix-docstrings` | Format docstrings with docformatter | ✅ |
|
|
125
|
+
| `format` | `format.check-shell` | Check shell scripts with shellcheck | ❌ |
|
|
126
|
+
| `format` | `format.fix-shell` | Format shell scripts with shfmt | ✅ |
|
|
127
|
+
| `lint` | `lint.check-lint` | Check linting with ruff | ❌ |
|
|
128
|
+
| `lint` | `lint.fix` | Fix auto-fixable linting issues with ruff | ✅ |
|
|
129
|
+
| `types` | `types.check` | Check type hints with pyright | ❌ |
|
|
130
|
+
| `test` | `test.doctest` | Run doctests on source code and markdown files | ❌ |
|
|
131
|
+
| `test` | `test.doctest-src` | Run doctests on source code | ❌ |
|
|
132
|
+
| `test` | `test.doctest-markdown` | Run doctests on Python examples in markdown files | ❌ |
|
|
133
|
+
| `test` | `test.unit` | Run unit tests | ❌ |
|
|
134
|
+
| `test` | `test.integration` | Run integration tests | ❌ |
|
|
135
|
+
| `test` | `test.functional` | Run functional tests | ❌ |
|
|
136
|
+
| `test` | `test.all` | Run unit, integration, and functional tests | ❌ |
|
|
137
|
+
| `test` | `test.coverage-report` | Generate an HTML/terminal report from existing coverage data | ❌ |
|
|
138
|
+
| `test` | `test.benchmark` | Run performance benchmarks | ❌ |
|
|
139
|
+
| `env` | `env.create-venv` | Create a virtual environment and install invoke | ✅ |
|
|
140
|
+
| `env` | `env.install` | Install project dependencies and the package (editable) | ✅ |
|
|
141
|
+
| `env` | `env.update` | Update dependencies and pre-commit hooks | ✅ |
|
|
142
|
+
| `env` | `env.show-installed-packages` | Show the installed packages | ❌ |
|
|
143
|
+
| `env` | `env.show-python-config` | Show the Python configuration | ❌ |
|
|
144
|
+
| `release` | `release.build` | Build the package and verify installation (`--check` also validates metadata with twine) | ✅ |
|
|
145
|
+
| `release` | `release.pypi` | Build and publish the package to PyPI | ✅ |
|
|
146
|
+
| `doc` | `doc.publish-dev` | Publish development (unstable) docs | ✅ |
|
|
147
|
+
| `doc` | `doc.publish-latest` | Publish latest (stable) docs | ✅ |
|
|
148
|
+
| `security` | `security.audit` | Audit installed dependencies for known vulnerabilities with pip-audit | ❌ |
|
|
149
|
+
| _(top-level)_ | `clean` | Remove build artifacts and caches | ✅ |
|
|
150
|
+
|
|
151
|
+
`format.*` and `lint.*` follow a naming convention: read-only checks are
|
|
152
|
+
named `check_<target>`, and the matching in-place fixer is named
|
|
153
|
+
`fix_<target>` (e.g. `check_python`/`fix_python`), so the counterpart of a
|
|
154
|
+
task is always easy to find. Follow this convention when adding new tasks
|
|
155
|
+
to these namespaces.
|
|
156
|
+
|
|
157
|
+
For task-by-task details, options (like `--cov` on `test.*` tasks), and the
|
|
158
|
+
tools each one wraps, see the
|
|
159
|
+
[User Guide](https://durandtibo.github.io/invoke-tasklib/uguide/).
|
|
160
|
+
|
|
161
|
+
## Configuration
|
|
162
|
+
|
|
163
|
+
Only `tasklib.package.name` is required — everything else has a sensible
|
|
164
|
+
default derived from it:
|
|
165
|
+
|
|
166
|
+
```yaml
|
|
167
|
+
tasklib:
|
|
168
|
+
package:
|
|
169
|
+
name: my_package # required
|
|
170
|
+
python_version: "3.14" # used by env.create-venv
|
|
171
|
+
paths:
|
|
172
|
+
src: src/my_package # default: src/<package.name>
|
|
173
|
+
tests: tests
|
|
174
|
+
unit_tests: tests/unit # default: <tests>/unit
|
|
175
|
+
integration_tests: tests/integration # default: <tests>/integration
|
|
176
|
+
functional_tests: tests/functional # default: <tests>/functional
|
|
177
|
+
benchmarks: tests/benchmarks # default: <tests>/benchmarks
|
|
178
|
+
docs_config: docs/mkdocs.yml
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
See the [Config reference](https://durandtibo.github.io/invoke-tasklib/uguide/config/)
|
|
182
|
+
for how each path default is derived.
|
|
183
|
+
|
|
184
|
+
## Composing a custom subset of tasks
|
|
185
|
+
|
|
186
|
+
Need a different set of tasks, or a one-off task alongside the shared ones?
|
|
187
|
+
Import individual task modules instead of the pre-built `ns`:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from invoke import Collection
|
|
191
|
+
from invoke_tasklib import lint, test
|
|
192
|
+
|
|
193
|
+
from . import my_custom_task
|
|
194
|
+
|
|
195
|
+
ns = Collection(lint, test, my_custom_task)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Prefer adding a config knob to a shared task over forking it; reserve custom
|
|
199
|
+
composition for things that are genuinely one-off to a single project.
|
|
200
|
+
|
|
201
|
+
## API Stability
|
|
202
|
+
|
|
203
|
+
:warning: `invoke-tasklib` is under active development and its API is not
|
|
204
|
+
yet stable — pin a specific version in your project's dependencies for
|
|
205
|
+
consistent behavior across releases.
|
|
206
|
+
|
|
207
|
+
## Contributing
|
|
208
|
+
|
|
209
|
+
Contributions are welcome! Please open an issue first to discuss
|
|
210
|
+
significant changes. See the
|
|
211
|
+
[developer guide](https://durandtibo.github.io/invoke-tasklib/dev/development/)
|
|
212
|
+
for how to set up a development environment.
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
`invoke-tasklib` is licensed under the BSD 3-Clause "New" or "Revised"
|
|
217
|
+
license available in
|
|
218
|
+
[LICENSE](https://github.com/durandtibo/invoke-tasklib/blob/main/LICENSE).
|
|
@@ -23,9 +23,30 @@ from __future__ import annotations
|
|
|
23
23
|
|
|
24
24
|
from invoke.collection import Collection
|
|
25
25
|
|
|
26
|
-
from invoke_tasklib import
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
from invoke_tasklib import (
|
|
27
|
+
clean,
|
|
28
|
+
doc,
|
|
29
|
+
env,
|
|
30
|
+
format, # noqa: A004
|
|
31
|
+
lint,
|
|
32
|
+
release,
|
|
33
|
+
security,
|
|
34
|
+
test,
|
|
35
|
+
types,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"clean",
|
|
40
|
+
"doc",
|
|
41
|
+
"env",
|
|
42
|
+
"format",
|
|
43
|
+
"lint",
|
|
44
|
+
"ns",
|
|
45
|
+
"release",
|
|
46
|
+
"security",
|
|
47
|
+
"test",
|
|
48
|
+
"types",
|
|
49
|
+
]
|
|
29
50
|
|
|
30
51
|
ns: Collection = Collection()
|
|
31
52
|
ns.add_collection(Collection.from_module(format), name="format")
|
|
@@ -35,3 +56,5 @@ ns.add_collection(Collection.from_module(test), name="test")
|
|
|
35
56
|
ns.add_collection(Collection.from_module(env), name="env")
|
|
36
57
|
ns.add_collection(Collection.from_module(release), name="release")
|
|
37
58
|
ns.add_collection(Collection.from_module(doc), name="doc")
|
|
59
|
+
ns.add_collection(Collection.from_module(security), name="security")
|
|
60
|
+
ns.add_task(clean.all, name="clean")
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
r"""Cleanup tasks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import shutil
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from invoke.tasks import task
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from invoke.context import Context
|
|
14
|
+
|
|
15
|
+
logger: logging.Logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
DIRS = (
|
|
18
|
+
"build",
|
|
19
|
+
"dist",
|
|
20
|
+
".pytest_cache",
|
|
21
|
+
".ruff_cache",
|
|
22
|
+
".coverage_html",
|
|
23
|
+
"htmlcov",
|
|
24
|
+
".benchmarks",
|
|
25
|
+
"site",
|
|
26
|
+
)
|
|
27
|
+
DIR_GLOBS = ("*.egg-info", "**/__pycache__")
|
|
28
|
+
FILES = (".coverage", "coverage.xml")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@task
|
|
32
|
+
def all(c: Context) -> None: # noqa: A001, ARG001 (task name/signature required by invoke)
|
|
33
|
+
r"""Remove build artifacts and caches (dist, __pycache__,
|
|
34
|
+
.pytest_cache, .coverage, etc.)."""
|
|
35
|
+
logger.info("🧹 Cleaning build artifacts and caches...")
|
|
36
|
+
for d in DIRS:
|
|
37
|
+
_remove_dir(Path(d))
|
|
38
|
+
for pattern in DIR_GLOBS:
|
|
39
|
+
for p in Path().glob(pattern):
|
|
40
|
+
_remove_dir(p)
|
|
41
|
+
for f in FILES:
|
|
42
|
+
_remove_file(Path(f))
|
|
43
|
+
logger.info("✅ Cleanup complete")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _remove_dir(path: Path) -> None:
|
|
47
|
+
if path.is_dir():
|
|
48
|
+
logger.info(f"🗑️ Removing {path}")
|
|
49
|
+
shutil.rmtree(path)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _remove_file(path: Path) -> None:
|
|
53
|
+
if path.is_file():
|
|
54
|
+
logger.info(f"🗑️ Removing {path}")
|
|
55
|
+
path.unlink()
|
|
@@ -40,11 +40,19 @@ class PathsConfig(TypedDict):
|
|
|
40
40
|
docs_config: str
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
class GroupsConfig(TypedDict):
|
|
44
|
+
r"""Resolved ``groups`` config section."""
|
|
45
|
+
|
|
46
|
+
install: str
|
|
47
|
+
update: str
|
|
48
|
+
|
|
49
|
+
|
|
43
50
|
class TasklibConfig(TypedDict):
|
|
44
51
|
r"""Resolved tasklib config."""
|
|
45
52
|
|
|
46
53
|
package: PackageConfig
|
|
47
54
|
paths: PathsConfig
|
|
55
|
+
groups: GroupsConfig
|
|
48
56
|
|
|
49
57
|
|
|
50
58
|
DEFAULT_PACKAGE: dict[str, str | None] = {
|
|
@@ -62,6 +70,11 @@ DEFAULT_PATHS: dict[str, str | None] = {
|
|
|
62
70
|
"docs_config": "docs/mkdocs.yml",
|
|
63
71
|
}
|
|
64
72
|
|
|
73
|
+
DEFAULT_GROUPS: dict[str, str] = {
|
|
74
|
+
"install": "dev",
|
|
75
|
+
"update": "dev,docs",
|
|
76
|
+
}
|
|
77
|
+
|
|
65
78
|
|
|
66
79
|
def get_config(c: Context) -> TasklibConfig:
|
|
67
80
|
r"""Return the effective tasklib config, merging user overrides from
|
|
@@ -79,6 +92,7 @@ def get_config(c: Context) -> TasklibConfig:
|
|
|
79
92
|
user = dict(c.config.get("tasklib", {}))
|
|
80
93
|
package = {**DEFAULT_PACKAGE, **user.get("package", {})}
|
|
81
94
|
paths = {**DEFAULT_PATHS, **user.get("paths", {})}
|
|
95
|
+
groups = {**DEFAULT_GROUPS, **user.get("groups", {})}
|
|
82
96
|
|
|
83
97
|
if not package["name"]:
|
|
84
98
|
msg = "'tasklib.package.name' must be set in invoke.yaml"
|
|
@@ -106,4 +120,5 @@ def get_config(c: Context) -> TasklibConfig:
|
|
|
106
120
|
benchmarks=paths["benchmarks"],
|
|
107
121
|
docs_config=paths["docs_config"],
|
|
108
122
|
),
|
|
123
|
+
"groups": GroupsConfig(install=groups["install"], update=groups["update"]),
|
|
109
124
|
}
|
|
@@ -35,8 +35,8 @@ def publish_latest(c: Context) -> None:
|
|
|
35
35
|
Requires the ``feu`` and ``packaging`` packages to determine the
|
|
36
36
|
latest version tag.
|
|
37
37
|
"""
|
|
38
|
-
from feu.local_git import get_last_version_tag_name
|
|
39
|
-
from packaging.version import Version
|
|
38
|
+
from feu.local_git import get_last_version_tag_name # noqa: PLC0415 (optional dependency)
|
|
39
|
+
from packaging.version import Version # noqa: PLC0415 (optional dependency)
|
|
40
40
|
|
|
41
41
|
cfg = get_config(c)
|
|
42
42
|
docs_config = cfg["paths"]["docs_config"]
|