tasktree 0.0.3__tar.gz → 0.0.5__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 (44) hide show
  1. {tasktree-0.0.3 → tasktree-0.0.5}/.gitignore +0 -3
  2. tasktree-0.0.5/.python-version +1 -0
  3. {tasktree-0.0.3 → tasktree-0.0.5}/CLAUDE.md +9 -8
  4. {tasktree-0.0.3 → tasktree-0.0.5}/PKG-INFO +246 -89
  5. {tasktree-0.0.3 → tasktree-0.0.5}/README.md +245 -88
  6. tasktree-0.0.5/example/tasktree.yaml +16 -0
  7. {tasktree-0.0.3 → tasktree-0.0.5}/pyproject.toml +1 -1
  8. {tasktree-0.0.3/requirements/future → tasktree-0.0.5/requirements/implemented}/shell-environment-requirements.md +37 -36
  9. {tasktree-0.0.3 → tasktree-0.0.5}/src/tasktree/cli.py +30 -70
  10. tasktree-0.0.5/src/tasktree/hasher.py +27 -0
  11. {tasktree-0.0.3 → tasktree-0.0.5}/src/tasktree/parser.py +41 -7
  12. {tasktree-0.0.3 → tasktree-0.0.5}/src/tasktree/types.py +7 -7
  13. tasktree-0.0.5/tasktree.yaml +48 -0
  14. {tasktree-0.0.3 → tasktree-0.0.5}/tests/integration/test_clean_state.py +19 -15
  15. {tasktree-0.0.3 → tasktree-0.0.5}/tests/integration/test_cli_options.py +233 -81
  16. {tasktree-0.0.3 → tasktree-0.0.5}/tests/integration/test_dependency_execution.py +43 -40
  17. {tasktree-0.0.3 → tasktree-0.0.5}/tests/integration/test_end_to_end.py +8 -6
  18. {tasktree-0.0.3 → tasktree-0.0.5}/tests/integration/test_input_detection.py +15 -12
  19. {tasktree-0.0.3 → tasktree-0.0.5}/tests/integration/test_nested_imports.py +65 -55
  20. {tasktree-0.0.3 → tasktree-0.0.5}/tests/integration/test_state_persistence.py +14 -11
  21. {tasktree-0.0.3 → tasktree-0.0.5}/tests/integration/test_working_directory.py +10 -8
  22. {tasktree-0.0.3 → tasktree-0.0.5}/tests/unit/test_executor.py +13 -9
  23. {tasktree-0.0.3 → tasktree-0.0.5}/tests/unit/test_parser.py +349 -164
  24. tasktree-0.0.3/example/tasktree.yaml +0 -15
  25. tasktree-0.0.3/src/tasktree/hasher.py +0 -77
  26. tasktree-0.0.3/tasktree.yaml +0 -48
  27. {tasktree-0.0.3 → tasktree-0.0.5}/.github/workflows/release.yml +0 -0
  28. {tasktree-0.0.3 → tasktree-0.0.5}/.github/workflows/test.yml +0 -0
  29. {tasktree-0.0.3 → tasktree-0.0.5}/example/source.txt +0 -0
  30. {tasktree-0.0.3 → tasktree-0.0.5}/requirements/future/docker-task-environments.md +0 -0
  31. {tasktree-0.0.3 → tasktree-0.0.5}/src/__init__.py +0 -0
  32. {tasktree-0.0.3 → tasktree-0.0.5}/src/tasktree/__init__.py +0 -0
  33. {tasktree-0.0.3 → tasktree-0.0.5}/src/tasktree/executor.py +0 -0
  34. {tasktree-0.0.3 → tasktree-0.0.5}/src/tasktree/graph.py +0 -0
  35. {tasktree-0.0.3 → tasktree-0.0.5}/src/tasktree/state.py +0 -0
  36. {tasktree-0.0.3 → tasktree-0.0.5}/src/tasktree/tasks.py +0 -0
  37. {tasktree-0.0.3 → tasktree-0.0.5}/tests/integration/test_missing_outputs.py +0 -0
  38. {tasktree-0.0.3 → tasktree-0.0.5}/tests/unit/test_cli.py +0 -0
  39. {tasktree-0.0.3 → tasktree-0.0.5}/tests/unit/test_graph.py +0 -0
  40. {tasktree-0.0.3 → tasktree-0.0.5}/tests/unit/test_hasher.py +0 -0
  41. {tasktree-0.0.3 → tasktree-0.0.5}/tests/unit/test_state.py +0 -0
  42. {tasktree-0.0.3 → tasktree-0.0.5}/tests/unit/test_tasks.py +0 -0
  43. {tasktree-0.0.3 → tasktree-0.0.5}/tests/unit/test_types.py +0 -0
  44. {tasktree-0.0.3 → tasktree-0.0.5}/uv.lock +0 -0
@@ -80,9 +80,6 @@ target/
80
80
  profile_default/
81
81
  ipython_config.py
82
82
 
83
- # pyenv
84
- .python-version
85
-
86
83
  # pipenv
87
84
  Pipfile.lock
88
85
 
@@ -0,0 +1 @@
1
+ 3.12
@@ -58,12 +58,13 @@ The project uses Python's built-in `unittest` framework with mocking via `unitte
58
58
 
59
59
  Tasks are defined in YAML with the following structure:
60
60
  ```yaml
61
- task-name:
62
- desc: Description (optional)
63
- deps: [dependency-tasks]
64
- inputs: [glob-patterns]
65
- outputs: [glob-patterns]
66
- working_dir: execution-directory
67
- args: [typed-parameters]
68
- cmd: shell-command
61
+ tasks:
62
+ task-name:
63
+ desc: Description (optional)
64
+ deps: [dependency-tasks]
65
+ inputs: [glob-patterns]
66
+ outputs: [glob-patterns]
67
+ working_dir: execution-directory
68
+ args: [typed-parameters]
69
+ cmd: shell-command
69
70
  ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tasktree
3
- Version: 0.0.3
3
+ Version: 0.0.5
4
4
  Summary: A task automation tool with incremental execution
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: click>=8.1.0
@@ -18,6 +18,142 @@ Description-Content-Type: text/markdown
18
18
 
19
19
  A task automation tool that combines simple command execution with dependency tracking and incremental execution.
20
20
 
21
+ ## Motivation
22
+ In any project of even moderate size, various scripts inevitably come into being along the way. These scripts often must be run in a particular order, or at a particular time. For historical reasons, this almost certainly a problem if your project is developed in a Linux environment; in Windows, an IDE like Visual Studio may be taking care of a significant proportion of your build, packaging and deployment tasks. Then again, it may not...
23
+
24
+ The various incantations that have to be issued to build, package, test and deploy a project can build up and then all of a sudden there's only a few people that remember which to invoke and when and then people start making helpful readme guides on what to do with the scripts and then those become out of date and start telling lies about things and so on.
25
+
26
+ Then there's the scripts themselves. In Linux, they're probably a big pile of Bash and Python, or something (Ruby, Perl, you name it). You can bet the house on people solving the problem of passing parameters to their scripts in a whole bunch of different and inconsistent ways.
27
+
28
+ ```bash
29
+ #!/usr/bin/env bash
30
+ # It's an environment variable defined.... somewhere?
31
+ echo "FOO is: $FOO"
32
+ ```
33
+ ```bash
34
+ #!/usr/bin/env bash
35
+ # Using simple positional arguments... guess what means what when you're invoking it!
36
+ echo "First: $1, Second: $2"
37
+ ```
38
+ ```bash
39
+ #!/usr/bin/env bash
40
+ # Oooooh fancy "make me look like a proper app" named option parsing... don't try and do --foo=bar though!
41
+ FOO=""
42
+ while [[ $# -gt 0 ]]; do
43
+ case "$1" in
44
+ --foo) FOO=$2; shift ;;
45
+ --) break ;;
46
+ *) echo "Unknown: $1";;
47
+ esac
48
+ shift
49
+ done
50
+ ```
51
+ ```bash
52
+ #!/usr/bin/env bash
53
+ # This thing...
54
+ ARGS=$(getopt -o f:b --long foo:,bar: -n 'myscript' -- "$@")
55
+ eval set -- "$ARGS"
56
+ while true; do
57
+ case "$1" in
58
+ -b|--bar) echo "Bar: $2"; shift 2 ;;
59
+ -f|--foo) echo "Foo: $2"; shift 2 ;;
60
+ --) shift; break ;;
61
+ *) break ;;
62
+ esac
63
+ done
64
+ ```
65
+
66
+ What about help info? Who has time to wire that in?
67
+
68
+ ### The point
69
+ Is this just whining and moaning? Should we just man up and revel in our own ability to memorize all the right incantations like some kind of scripting shaman?
70
+
71
+ ... No. That's **a dumb idea**.
72
+
73
+ Task Tree allows you to pile all the knowledge of **what** to run, **when** to run it, **where** to run it and **how** to run it into a single, readable place. Then you can delete all the scripts that no-one knows how to use and all the readme docs that lie to the few people that actually waste their time reading them.
74
+
75
+ The tasks you need to perform to deliver your project become summarised in an executable file that looks like:
76
+ ```yaml
77
+ tasks:
78
+ build:
79
+ desc: Compile stuff
80
+ outputs: [target/release/bin]
81
+ cmd: cargo build --release
82
+
83
+ package:
84
+ desc: build installers
85
+ deps: [build]
86
+ outputs: [awesome.deb]
87
+ cmd: |
88
+ for bin in target/release/*; do
89
+ if [[ -x "$bin" && ! -d "$bin" ]]; then
90
+ install -Dm755 "$bin" "debian/awesome/usr/bin/$(basename "$bin")"
91
+ fi
92
+ done
93
+
94
+ dpkg-buildpackage -us -uc
95
+
96
+ test:
97
+ desc: Run tests
98
+ deps: [package]
99
+ inputs: [tests/**/*.py]
100
+ cmd: PYTHONPATH=src python3 -m pytest tests/ -v
101
+ ```
102
+
103
+ If you want to run the tests then:
104
+ ```bash
105
+ tt test
106
+ ```
107
+ Boom! Done. `build` will always run, because there's no sensible way to know what Cargo did. However, if Cargo decided that nothing needed to be done and didn't touch the binaries, then `package` will realize that and not do anything. Then `test` will just run with the new tests that you just wrote. If you then immediately run `test` again, then `test` will figure out that none of the dependencies did anything and that none of the test files have changed and then just _do nothing_ - as it should.
108
+
109
+ This is a toy example, but you can image how it plays out on a more complex project.
110
+
111
+ ## Migrating from v1.x to v2.0
112
+
113
+ Version 2.0 requires all task definitions to be under a top-level `tasks:` key.
114
+
115
+ ### Quick Migration
116
+
117
+ Wrap your existing tasks in a `tasks:` block:
118
+
119
+ ```yaml
120
+ # Before (v1.x)
121
+ build:
122
+ cmd: cargo build
123
+
124
+ # After (v2.0)
125
+ tasks:
126
+ build:
127
+ cmd: cargo build
128
+ ```
129
+
130
+ ### Why This Change?
131
+
132
+ 1. **Clearer structure**: Explicit separation of tasks from configuration
133
+ 2. **No naming conflicts**: You can now create tasks named "imports" or "environments"
134
+ 3. **Better error messages**: More helpful validation errors
135
+ 4. **Consistency**: All recipe files use the same format
136
+
137
+ ### Error Messages
138
+
139
+ If you forget to update, you'll see a clear error:
140
+
141
+ ```
142
+ Invalid recipe format in tasktree.yaml
143
+
144
+ Task definitions must be under a top-level "tasks:" key.
145
+
146
+ Found these keys at root level: build, test
147
+
148
+ Did you mean:
149
+
150
+ tasks:
151
+ build:
152
+ cmd: ...
153
+ test:
154
+ cmd: ...
155
+ ```
156
+
21
157
  ## Installation
22
158
 
23
159
  ### From PyPI (Recommended)
@@ -26,6 +162,16 @@ A task automation tool that combines simple command execution with dependency tr
26
162
  pipx install tasktree
27
163
  ```
28
164
 
165
+ If you have multiple Python interpreter versions installed, and the _default_ interpreter is a version <3.11, then you can use `pipx`'s `--python` option to specify an interpreter with a version >=3.11:
166
+
167
+ ```bash
168
+ # If the target version is on the PATH
169
+ pipx install --python python3.12 tasktree
170
+
171
+ # With a path to an interpreter
172
+ pipx install --python /path/to/python3.12 tasktree
173
+ ```
174
+
29
175
  ### From Source
30
176
 
31
177
  For the latest unreleased version from GitHub:
@@ -47,23 +193,26 @@ pipx install .
47
193
  Create a `tasktree.yaml` (or `tt.yaml`) in your project:
48
194
 
49
195
  ```yaml
50
- build:
51
- desc: Compile the application
52
- outputs: [target/release/bin]
53
- cmd: cargo build --release
196
+ tasks:
197
+ build:
198
+ desc: Compile the application
199
+ outputs: [target/release/bin]
200
+ cmd: cargo build --release
54
201
 
55
- test:
56
- desc: Run tests
57
- deps: [build]
58
- cmd: cargo test
202
+ test:
203
+ desc: Run tests
204
+ deps: [build]
205
+ cmd: cargo test
59
206
  ```
60
207
 
61
208
  Run tasks:
62
209
 
63
210
  ```bash
64
- tt build # Build the application
65
- tt test # Run tests (builds first if needed)
211
+ tt # Print the help
212
+ tt --help # ...also print the help
66
213
  tt --list # Show all available tasks
214
+ tt build # Build the application (assuming this is in your tasktree.yaml)
215
+ tt test # Run tests (builds first if needed)
67
216
  ```
68
217
 
69
218
  ## Core Concepts
@@ -84,15 +233,16 @@ Task Tree only runs tasks when necessary. A task executes if:
84
233
  Tasks automatically inherit inputs from dependencies, eliminating redundant declarations:
85
234
 
86
235
  ```yaml
87
- build:
88
- outputs: [dist/app]
89
- cmd: go build -o dist/app
90
-
91
- package:
92
- deps: [build]
93
- outputs: [dist/app.tar.gz]
94
- cmd: tar czf dist/app.tar.gz dist/app
95
- # Automatically tracks dist/app as an input
236
+ tasks:
237
+ build:
238
+ outputs: [dist/app]
239
+ cmd: go build -o dist/app
240
+
241
+ package:
242
+ deps: [build]
243
+ outputs: [dist/app.tar.gz]
244
+ cmd: tar czf dist/app.tar.gz dist/app
245
+ # Automatically tracks dist/app as an input
96
246
  ```
97
247
 
98
248
  ### Single State File
@@ -104,15 +254,16 @@ All state lives in `.tasktree-state` at your project root. Stale entries are aut
104
254
  ### Basic Structure
105
255
 
106
256
  ```yaml
107
- task-name:
108
- desc: Human-readable description (optional)
109
- deps: [other-task] # Task dependencies
110
- inputs: [src/**/*.go] # Explicit input files (glob patterns)
111
- outputs: [dist/binary] # Output files (glob patterns)
112
- working_dir: subproject/ # Execution directory (default: project root)
113
- env: bash-strict # Execution environment (optional)
114
- args: [param1, param2:path=default] # Task parameters
115
- cmd: go build -o dist/binary # Command to execute
257
+ tasks:
258
+ task-name:
259
+ desc: Human-readable description (optional)
260
+ deps: [other-task] # Task dependencies
261
+ inputs: [src/**/*.go] # Explicit input files (glob patterns)
262
+ outputs: [dist/binary] # Output files (glob patterns)
263
+ working_dir: subproject/ # Execution directory (default: project root)
264
+ env: bash-strict # Execution environment (optional)
265
+ args: [param1, param2:path=default] # Task parameters
266
+ cmd: go build -o dist/binary # Command to execute
116
267
  ```
117
268
 
118
269
  ### Commands
@@ -120,18 +271,20 @@ task-name:
120
271
  **Single-line commands** are executed directly via the configured shell:
121
272
 
122
273
  ```yaml
123
- build:
124
- cmd: cargo build --release
274
+ tasks:
275
+ build:
276
+ cmd: cargo build --release
125
277
  ```
126
278
 
127
279
  **Multi-line commands** are written to temporary script files for proper execution:
128
280
 
129
281
  ```yaml
130
- deploy:
131
- cmd: |
132
- mkdir -p dist
133
- cp build/* dist/
134
- rsync -av dist/ server:/opt/app/
282
+ tasks:
283
+ deploy:
284
+ cmd: |
285
+ mkdir -p dist
286
+ cp build/* dist/
287
+ rsync -av dist/ server:/opt/app/
135
288
  ```
136
289
 
137
290
  Multi-line commands preserve shell syntax (line continuations, heredocs, etc.) and support shebangs on Unix/macOS.
@@ -139,12 +292,13 @@ Multi-line commands preserve shell syntax (line continuations, heredocs, etc.) a
139
292
  Or use folded blocks for long single-line commands:
140
293
 
141
294
  ```yaml
142
- compile:
143
- cmd: >
144
- gcc -o bin/app
145
- src/*.c
146
- -I include
147
- -L lib -lm
295
+ tasks:
296
+ compile:
297
+ cmd: >
298
+ gcc -o bin/app
299
+ src/*.c
300
+ -I include
301
+ -L lib -lm
148
302
  ```
149
303
 
150
304
  ### Execution Environments
@@ -204,12 +358,13 @@ tasks:
204
358
  Tasks can accept arguments with optional defaults:
205
359
 
206
360
  ```yaml
207
- deploy:
208
- args: [environment, region=eu-west-1]
209
- deps: [build]
210
- cmd: |
211
- aws s3 cp dist/app.zip s3://{{environment}}-{{region}}/
212
- aws lambda update-function-code --function-name app-{{environment}}
361
+ tasks:
362
+ deploy:
363
+ args: [environment, region=eu-west-1]
364
+ deps: [build]
365
+ cmd: |
366
+ aws s3 cp dist/app.zip s3://{{environment}}-{{region}}/
367
+ aws lambda update-function-code --function-name app-{{environment}}
213
368
  ```
214
369
 
215
370
  Invoke with: `tt deploy production` or `tt deploy staging us-east-1` or `tt deploy staging region=us-east-1`.
@@ -236,18 +391,19 @@ Split task definitions across multiple files for better organisation:
236
391
 
237
392
  ```yaml
238
393
  # tasktree.yaml
239
- import:
394
+ imports:
240
395
  - file: build/tasks.yml
241
396
  as: build
242
397
  - file: deploy/tasks.yml
243
398
  as: deploy
244
399
 
245
- test:
246
- deps: [build.compile, build.test-compile]
247
- cmd: ./run-tests.sh
400
+ tasks:
401
+ test:
402
+ deps: [build.compile, build.test-compile]
403
+ cmd: ./run-tests.sh
248
404
 
249
- ci:
250
- deps: [build.all, test, deploy.staging]
405
+ ci:
406
+ deps: [build.all, test, deploy.staging]
251
407
  ```
252
408
 
253
409
  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.
@@ -365,41 +521,42 @@ imports:
365
521
  - file: common/docker.yml
366
522
  as: docker
367
523
 
368
- compile:
369
- desc: Build application binaries
370
- outputs: [target/release/app]
371
- cmd: cargo build --release
372
-
373
- test-unit:
374
- desc: Run unit tests
375
- deps: [compile]
376
- cmd: cargo test
377
-
378
- package:
379
- desc: Create distribution archive
380
- deps: [compile]
381
- outputs: [dist/app-{{version}}.tar.gz]
382
- args: [version]
383
- cmd: |
384
- mkdir -p dist
385
- tar czf dist/app-{{version}}.tar.gz \
386
- target/release/app \
387
- config/ \
388
- migrations/
389
-
390
- deploy:
391
- desc: Deploy to environment
392
- deps: [package, docker.build-runtime]
393
- args: [environment, version]
394
- cmd: |
395
- scp dist/app-{{version}}.tar.gz {{environment}}:/opt/
396
- ssh {{environment}} /opt/deploy.sh {{version}}
397
-
398
- integration-test:
399
- desc: Run integration tests against deployed environment
400
- deps: [deploy]
401
- args: [environment, version]
402
- cmd: pytest tests/integration/ --env={{environment}}
524
+ tasks:
525
+ compile:
526
+ desc: Build application binaries
527
+ outputs: [target/release/app]
528
+ cmd: cargo build --release
529
+
530
+ test-unit:
531
+ desc: Run unit tests
532
+ deps: [compile]
533
+ cmd: cargo test
534
+
535
+ package:
536
+ desc: Create distribution archive
537
+ deps: [compile]
538
+ outputs: [dist/app-{{version}}.tar.gz]
539
+ args: [version]
540
+ cmd: |
541
+ mkdir -p dist
542
+ tar czf dist/app-{{version}}.tar.gz \
543
+ target/release/app \
544
+ config/ \
545
+ migrations/
546
+
547
+ deploy:
548
+ desc: Deploy to environment
549
+ deps: [package, docker.build-runtime]
550
+ args: [environment, version]
551
+ cmd: |
552
+ scp dist/app-{{version}}.tar.gz {{environment}}:/opt/
553
+ ssh {{environment}} /opt/deploy.sh {{version}}
554
+
555
+ integration-test:
556
+ desc: Run integration tests against deployed environment
557
+ deps: [deploy]
558
+ args: [environment, version]
559
+ cmd: pytest tests/integration/ --env={{environment}}
403
560
  ```
404
561
 
405
562
  Run the full pipeline: