tasktree 0.0.1__py3-none-any.whl
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.
- tasktree/__init__.py +42 -0
- tasktree/cli.py +502 -0
- tasktree/executor.py +365 -0
- tasktree/graph.py +139 -0
- tasktree/hasher.py +74 -0
- tasktree/parser.py +300 -0
- tasktree/state.py +119 -0
- tasktree/tasks.py +8 -0
- tasktree/types.py +130 -0
- tasktree-0.0.1.dist-info/METADATA +387 -0
- tasktree-0.0.1.dist-info/RECORD +13 -0
- tasktree-0.0.1.dist-info/WHEEL +4 -0
- tasktree-0.0.1.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tasktree
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A task automation tool with intelligent incremental execution
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: click>=8.1.0
|
|
7
|
+
Requires-Dist: colorama>=0.4.6
|
|
8
|
+
Requires-Dist: pyyaml>=6.0
|
|
9
|
+
Requires-Dist: rich>=13.0.0
|
|
10
|
+
Requires-Dist: typer>=0.9.0
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# Task Tree (tt)
|
|
16
|
+
|
|
17
|
+
[](https://github.com/kevinchannon/tasktree/actions/workflows/test.yml)
|
|
18
|
+
|
|
19
|
+
A task automation tool that combines simple command execution with intelligent dependency tracking and incremental execution.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
### From PyPI (Recommended)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pipx install tasktree
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### From Source
|
|
30
|
+
|
|
31
|
+
For the latest unreleased version from GitHub:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pipx install git+https://github.com/kevinchannon/tasktree.git
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or to install from a local clone:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
git clone https://github.com/kevinchannon/tasktree.git
|
|
41
|
+
cd tasktree
|
|
42
|
+
pipx install .
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
Create a `tasktree.yaml` (or `tt.yaml`) in your project:
|
|
48
|
+
|
|
49
|
+
```yaml
|
|
50
|
+
build:
|
|
51
|
+
desc: Compile the application
|
|
52
|
+
outputs: [target/release/bin]
|
|
53
|
+
cmd: cargo build --release
|
|
54
|
+
|
|
55
|
+
test:
|
|
56
|
+
desc: Run tests
|
|
57
|
+
deps: [build]
|
|
58
|
+
cmd: cargo test
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Run tasks:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
tt build # Build the application
|
|
65
|
+
tt test # Run tests (builds first if needed)
|
|
66
|
+
tt --list # Show all available tasks
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Core Concepts
|
|
70
|
+
|
|
71
|
+
### Intelligent Incremental Execution
|
|
72
|
+
|
|
73
|
+
Task Tree only runs tasks when necessary. A task executes if:
|
|
74
|
+
|
|
75
|
+
- Its definition (command, outputs, working directory) has changed
|
|
76
|
+
- Any input files have changed since the last run
|
|
77
|
+
- Any dependencies have re-run
|
|
78
|
+
- It has never been executed before
|
|
79
|
+
- It has no inputs or outputs (always runs)
|
|
80
|
+
|
|
81
|
+
### Automatic Input Inheritance
|
|
82
|
+
|
|
83
|
+
Tasks automatically inherit inputs from dependencies, eliminating redundant declarations:
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
build:
|
|
87
|
+
outputs: [dist/app]
|
|
88
|
+
cmd: go build -o dist/app
|
|
89
|
+
|
|
90
|
+
package:
|
|
91
|
+
deps: [build]
|
|
92
|
+
outputs: [dist/app.tar.gz]
|
|
93
|
+
cmd: tar czf dist/app.tar.gz dist/app
|
|
94
|
+
# Automatically tracks dist/app as an input
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Single State File
|
|
98
|
+
|
|
99
|
+
All state lives in `.tasktree-state` at your project root. Stale entries are automatically pruned—no manual cleanup needed.
|
|
100
|
+
|
|
101
|
+
## Task Definition
|
|
102
|
+
|
|
103
|
+
### Basic Structure
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
task-name:
|
|
107
|
+
desc: Human-readable description (optional)
|
|
108
|
+
deps: [other-task] # Task dependencies
|
|
109
|
+
inputs: [src/**/*.go] # Explicit input files (glob patterns)
|
|
110
|
+
outputs: [dist/binary] # Output files (glob patterns)
|
|
111
|
+
working_dir: subproject/ # Execution directory (default: project root)
|
|
112
|
+
args: [param1, param2:path=default] # Task parameters
|
|
113
|
+
cmd: go build -o dist/binary # Command to execute
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Commands
|
|
117
|
+
|
|
118
|
+
Multi-line commands using YAML literal blocks:
|
|
119
|
+
|
|
120
|
+
```yaml
|
|
121
|
+
deploy:
|
|
122
|
+
cmd: |
|
|
123
|
+
mkdir -p dist
|
|
124
|
+
cp build/* dist/
|
|
125
|
+
rsync -av dist/ server:/opt/app/
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Or folded blocks for long single-line commands:
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
compile:
|
|
132
|
+
cmd: >
|
|
133
|
+
gcc -o bin/app
|
|
134
|
+
src/*.c
|
|
135
|
+
-I include
|
|
136
|
+
-L lib -lm
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Parameterised Tasks
|
|
140
|
+
|
|
141
|
+
Tasks can accept arguments with optional defaults:
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
deploy:
|
|
145
|
+
args: [environment, region=eu-west-1]
|
|
146
|
+
deps: [build]
|
|
147
|
+
cmd: |
|
|
148
|
+
aws s3 cp dist/app.zip s3://{{environment}}-{{region}}/
|
|
149
|
+
aws lambda update-function-code --function-name app-{{environment}}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Invoke with: `tt deploy production` or `tt deploy staging us-east-1` or `tt deploy staging region=us-east-1`.
|
|
153
|
+
|
|
154
|
+
Arguments may be typed, or not and have a default, or not. Valid argument types are:
|
|
155
|
+
|
|
156
|
+
* int - an integer value (e.g. 0, 10, 123, -9)
|
|
157
|
+
* float - a floating point value (e.g. 1.234, -3.1415, 2e-4)
|
|
158
|
+
* bool - Boolean-ish value (e.g. true, false, yes, no, 1, 0, etc)
|
|
159
|
+
* str - a string
|
|
160
|
+
* path - a pathlike string
|
|
161
|
+
* datetime - a datetime in the format 2025-12-17T16:56:12
|
|
162
|
+
* ip - an ip address (v4 or v6)
|
|
163
|
+
* ipv4 - an IPv4 value
|
|
164
|
+
* ipv6 - an IPv6 value
|
|
165
|
+
* email - String validated, but not positively confirmed to be a reachable address.
|
|
166
|
+
* hostname - looks like a hostname, resolution of the name is not attempted as part of the validation
|
|
167
|
+
|
|
168
|
+
Different argument values are tracked separately—tasks re-run when invoked with new arguments.
|
|
169
|
+
|
|
170
|
+
## File Imports
|
|
171
|
+
|
|
172
|
+
Split task definitions across multiple files for better organisation:
|
|
173
|
+
|
|
174
|
+
```yaml
|
|
175
|
+
# tasktree.yaml
|
|
176
|
+
import:
|
|
177
|
+
- file: build/tasks.yml
|
|
178
|
+
as: build
|
|
179
|
+
- file: deploy/tasks.yml
|
|
180
|
+
as: deploy
|
|
181
|
+
|
|
182
|
+
test:
|
|
183
|
+
deps: [build.compile, build.test-compile]
|
|
184
|
+
cmd: ./run-tests.sh
|
|
185
|
+
|
|
186
|
+
ci:
|
|
187
|
+
deps: [build.all, test, deploy.staging]
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Imported tasks are namespaced and can be referenced as dependencies. Each imported file is self-contained—it cannot depend on tasks in the importing file.
|
|
191
|
+
|
|
192
|
+
## Glob Patterns
|
|
193
|
+
|
|
194
|
+
Input and output patterns support standard glob syntax:
|
|
195
|
+
|
|
196
|
+
- `src/*.rs` — All Rust files in `src/`
|
|
197
|
+
- `src/**/*.rs` — All Rust files recursively
|
|
198
|
+
- `{file1,file2}` — Specific files
|
|
199
|
+
- `**/*.{js,ts}` — Multiple extensions recursively
|
|
200
|
+
|
|
201
|
+
## State Management
|
|
202
|
+
|
|
203
|
+
### How State Works
|
|
204
|
+
|
|
205
|
+
Each task is identified by a hash of its definition (command, outputs, working directory). State tracks:
|
|
206
|
+
|
|
207
|
+
- When the task last ran
|
|
208
|
+
- Timestamps of input files at that time
|
|
209
|
+
|
|
210
|
+
Tasks are re-run when their definition changes or inputs are newer than the last run.
|
|
211
|
+
|
|
212
|
+
### What's Not In The Hash
|
|
213
|
+
|
|
214
|
+
Changes to these don't invalidate cached state:
|
|
215
|
+
|
|
216
|
+
- Task name (tasks can be renamed freely)
|
|
217
|
+
- Description
|
|
218
|
+
- Dependencies (only affects execution order)
|
|
219
|
+
- Explicit inputs (tracked by timestamp, not definition)
|
|
220
|
+
|
|
221
|
+
### Automatic Cleanup
|
|
222
|
+
|
|
223
|
+
At the start of each invocation, state is checked for invalid task hashes and non-existent ones are automatically removed. Delete a task from your recipe file and its state disappears the next time you run `tt <cmd>`
|
|
224
|
+
|
|
225
|
+
## Example: Full Build Pipeline
|
|
226
|
+
|
|
227
|
+
```yaml
|
|
228
|
+
imports:
|
|
229
|
+
- file: common/docker.yml
|
|
230
|
+
as: docker
|
|
231
|
+
|
|
232
|
+
compile:
|
|
233
|
+
desc: Build application binaries
|
|
234
|
+
outputs: [target/release/app]
|
|
235
|
+
cmd: cargo build --release
|
|
236
|
+
|
|
237
|
+
test-unit:
|
|
238
|
+
desc: Run unit tests
|
|
239
|
+
deps: [compile]
|
|
240
|
+
cmd: cargo test
|
|
241
|
+
|
|
242
|
+
package:
|
|
243
|
+
desc: Create distribution archive
|
|
244
|
+
deps: [compile]
|
|
245
|
+
outputs: [dist/app-{{version}}.tar.gz]
|
|
246
|
+
args: [version]
|
|
247
|
+
cmd: |
|
|
248
|
+
mkdir -p dist
|
|
249
|
+
tar czf dist/app-{{version}}.tar.gz \
|
|
250
|
+
target/release/app \
|
|
251
|
+
config/ \
|
|
252
|
+
migrations/
|
|
253
|
+
|
|
254
|
+
deploy:
|
|
255
|
+
desc: Deploy to environment
|
|
256
|
+
deps: [package, docker.build-runtime]
|
|
257
|
+
args: [environment, version]
|
|
258
|
+
cmd: |
|
|
259
|
+
scp dist/app-{{version}}.tar.gz {{environment}}:/opt/
|
|
260
|
+
ssh {{environment}} /opt/deploy.sh {{version}}
|
|
261
|
+
|
|
262
|
+
integration-test:
|
|
263
|
+
desc: Run integration tests against deployed environment
|
|
264
|
+
deps: [deploy]
|
|
265
|
+
args: [environment, version]
|
|
266
|
+
cmd: pytest tests/integration/ --env={{environment}}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Run the full pipeline:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
tt integration-test staging version=1.2.3
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
This will:
|
|
276
|
+
1. Compile if sources have changed
|
|
277
|
+
2. Run unit tests if compilation ran
|
|
278
|
+
3. Package if compilation ran or version argument is new
|
|
279
|
+
4. Build Docker runtime (from imported file) if needed
|
|
280
|
+
5. Deploy if package or Docker image changed
|
|
281
|
+
6. Run integration tests (always runs)
|
|
282
|
+
|
|
283
|
+
## Implementation Notes
|
|
284
|
+
|
|
285
|
+
Built with Python 3.11+ using:
|
|
286
|
+
|
|
287
|
+
- **PyYAML** for recipe parsing
|
|
288
|
+
- **Typer**, **Click**, **Rich** for CLI
|
|
289
|
+
- **graphlib.TopologicalSorter** for dependency resolution
|
|
290
|
+
- **pathlib** for file operations and glob expansion
|
|
291
|
+
|
|
292
|
+
State file uses JSON format for simplicity and standard library compatibility.
|
|
293
|
+
|
|
294
|
+
## Development
|
|
295
|
+
|
|
296
|
+
### Setup Development Environment
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
# Clone repository
|
|
300
|
+
git clone https://github.com/kevinchannon/tasktree.git
|
|
301
|
+
cd tasktree
|
|
302
|
+
|
|
303
|
+
# Install uv (if not already installed)
|
|
304
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
305
|
+
|
|
306
|
+
# Install dependencies
|
|
307
|
+
uv sync
|
|
308
|
+
|
|
309
|
+
# Install in editable mode
|
|
310
|
+
pipx install -e .
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Running Tests
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
# Run all tests
|
|
317
|
+
uv run pytest
|
|
318
|
+
|
|
319
|
+
# Run with verbose output
|
|
320
|
+
uv run pytest -v
|
|
321
|
+
|
|
322
|
+
# Run specific test file
|
|
323
|
+
uv run pytest tests/unit/test_executor.py
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Using Task Tree for Development
|
|
327
|
+
|
|
328
|
+
The repository includes a `tasktree.yaml` with development tasks:
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
tt test # Run tests
|
|
332
|
+
tt build # Build wheel package
|
|
333
|
+
tt install-dev # Install package in development mode
|
|
334
|
+
tt clean # Remove build artifacts
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Releasing
|
|
338
|
+
|
|
339
|
+
New releases are created by pushing version tags to GitHub. The release workflow automatically:
|
|
340
|
+
- Builds wheel and source distributions
|
|
341
|
+
- Creates a GitHub Release with artifacts
|
|
342
|
+
- Publishes to PyPI via trusted publishing
|
|
343
|
+
|
|
344
|
+
### Release Process
|
|
345
|
+
|
|
346
|
+
1. Ensure main branch is ready:
|
|
347
|
+
```bash
|
|
348
|
+
git checkout main
|
|
349
|
+
git pull
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
2. Create and push a version tag:
|
|
353
|
+
```bash
|
|
354
|
+
git tag v1.0.0
|
|
355
|
+
git push origin v1.0.0
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
3. GitHub Actions will automatically:
|
|
359
|
+
- Extract version from tag (e.g., `v1.0.0` → `1.0.0`)
|
|
360
|
+
- Update `pyproject.toml` with the version
|
|
361
|
+
- Build wheel and sdist
|
|
362
|
+
- Create GitHub Release
|
|
363
|
+
- Publish to PyPI
|
|
364
|
+
|
|
365
|
+
4. Verify the release:
|
|
366
|
+
- GitHub: https://github.com/kevinchannon/tasktree/releases
|
|
367
|
+
- PyPI: https://pypi.org/project/tasktree/
|
|
368
|
+
- Test: `pipx install --force tasktree`
|
|
369
|
+
|
|
370
|
+
### Version Numbering
|
|
371
|
+
|
|
372
|
+
Follow semantic versioning:
|
|
373
|
+
- `v1.0.0` - Major release (breaking changes)
|
|
374
|
+
- `v1.1.0` - Minor release (new features, backward compatible)
|
|
375
|
+
- `v1.1.1` - Patch release (bug fixes)
|
|
376
|
+
|
|
377
|
+
### PyPI Trusted Publishing Setup
|
|
378
|
+
|
|
379
|
+
Before the first release, configure trusted publishing on PyPI:
|
|
380
|
+
|
|
381
|
+
1. Go to https://pypi.org/manage/account/publishing/
|
|
382
|
+
2. Add a new publisher:
|
|
383
|
+
- **PyPI Project Name**: `tasktree`
|
|
384
|
+
- **Owner**: `kevinchannon`
|
|
385
|
+
- **Repository name**: `tasktree`
|
|
386
|
+
- **Workflow name**: `release.yml`
|
|
387
|
+
- **Environment name**: (leave blank)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
tasktree/__init__.py,sha256=MVmdvKb3JdqLlo0x2_TPGMfgFC0HsDnP79HAzGnFnjI,1081
|
|
2
|
+
tasktree/cli.py,sha256=hI9jqHE2YIb_6lYUjxVobLJOE88kOlunfAg52TsD1sQ,15681
|
|
3
|
+
tasktree/executor.py,sha256=TH59xTR6MNBEek9Q8yjYhgC-h0H4mjNsP-yjAcVgS-E,11838
|
|
4
|
+
tasktree/graph.py,sha256=9ngfg93y7EkOIN_lUQa0u-JhnwiMN1UdQQvIFw8RYCE,4181
|
|
5
|
+
tasktree/hasher.py,sha256=jvXBvIfFH9g6AOBHHd012v8nG5JOgVnHH35mIEtVkwc,2133
|
|
6
|
+
tasktree/parser.py,sha256=F2LbB84NinvBQT8zLONxn036R9Rued2y4XB3WGZ8PLk,9150
|
|
7
|
+
tasktree/state.py,sha256=rxKtS3SbsPtAuraHbN807RGWfoYYkQ3pe8CxUstwo2k,3535
|
|
8
|
+
tasktree/tasks.py,sha256=2QdQZtJAX2rSGbyXKG1z9VF_siz1DUzdvzCgPkykxtU,173
|
|
9
|
+
tasktree/types.py,sha256=wrBzO-Z2ebCTRjWyOWNvuCjqAq-74Zyb9E4FQ4beF38,3751
|
|
10
|
+
tasktree-0.0.1.dist-info/METADATA,sha256=aE9ihAgGTRvrNh7TarEA2vEFftKg3FV1DVlMAN0M9LU,9522
|
|
11
|
+
tasktree-0.0.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
12
|
+
tasktree-0.0.1.dist-info/entry_points.txt,sha256=lQINlvRYnimvteBbnhH84A9clTg8NnpEjCWqWkqg8KE,40
|
|
13
|
+
tasktree-0.0.1.dist-info/RECORD,,
|