tasktree 0.0.2__tar.gz → 0.0.4__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.2 → tasktree-0.0.4}/.github/workflows/test.yml +1 -1
  2. {tasktree-0.0.2 → tasktree-0.0.4}/.gitignore +0 -3
  3. tasktree-0.0.4/.python-version +1 -0
  4. {tasktree-0.0.2 → tasktree-0.0.4}/PKG-INFO +251 -14
  5. tasktree-0.0.4/README.md +598 -0
  6. {tasktree-0.0.2 → tasktree-0.0.4}/pyproject.toml +1 -1
  7. tasktree-0.0.4/requirements/future/docker-task-environments.md +1002 -0
  8. tasktree-0.0.4/requirements/implemented/shell-environment-requirements.md +392 -0
  9. {tasktree-0.0.2 → tasktree-0.0.4}/src/tasktree/cli.py +43 -138
  10. {tasktree-0.0.2 → tasktree-0.0.4}/src/tasktree/executor.py +202 -16
  11. tasktree-0.0.4/src/tasktree/hasher.py +27 -0
  12. {tasktree-0.0.2 → tasktree-0.0.4}/src/tasktree/parser.py +108 -5
  13. {tasktree-0.0.2 → tasktree-0.0.4}/src/tasktree/types.py +7 -7
  14. tasktree-0.0.4/tasktree.yaml +48 -0
  15. {tasktree-0.0.2 → tasktree-0.0.4}/tests/integration/test_cli_options.py +149 -1
  16. {tasktree-0.0.2 → tasktree-0.0.4}/tests/integration/test_dependency_execution.py +4 -4
  17. {tasktree-0.0.2 → tasktree-0.0.4}/tests/integration/test_missing_outputs.py +7 -7
  18. {tasktree-0.0.2 → tasktree-0.0.4}/tests/integration/test_working_directory.py +1 -1
  19. {tasktree-0.0.2 → tasktree-0.0.4}/tests/unit/test_executor.py +375 -5
  20. {tasktree-0.0.2 → tasktree-0.0.4}/tests/unit/test_parser.py +90 -0
  21. tasktree-0.0.2/README.md +0 -361
  22. tasktree-0.0.2/src/tasktree/hasher.py +0 -74
  23. tasktree-0.0.2/tasktree.yaml +0 -48
  24. {tasktree-0.0.2 → tasktree-0.0.4}/.github/workflows/release.yml +0 -0
  25. {tasktree-0.0.2 → tasktree-0.0.4}/CLAUDE.md +0 -0
  26. {tasktree-0.0.2 → tasktree-0.0.4}/example/source.txt +0 -0
  27. {tasktree-0.0.2 → tasktree-0.0.4}/example/tasktree.yaml +0 -0
  28. {tasktree-0.0.2 → tasktree-0.0.4}/src/__init__.py +0 -0
  29. {tasktree-0.0.2 → tasktree-0.0.4}/src/tasktree/__init__.py +0 -0
  30. {tasktree-0.0.2 → tasktree-0.0.4}/src/tasktree/graph.py +0 -0
  31. {tasktree-0.0.2 → tasktree-0.0.4}/src/tasktree/state.py +0 -0
  32. {tasktree-0.0.2 → tasktree-0.0.4}/src/tasktree/tasks.py +0 -0
  33. {tasktree-0.0.2 → tasktree-0.0.4}/tests/integration/test_clean_state.py +0 -0
  34. {tasktree-0.0.2 → tasktree-0.0.4}/tests/integration/test_end_to_end.py +0 -0
  35. {tasktree-0.0.2 → tasktree-0.0.4}/tests/integration/test_input_detection.py +0 -0
  36. {tasktree-0.0.2 → tasktree-0.0.4}/tests/integration/test_nested_imports.py +0 -0
  37. {tasktree-0.0.2 → tasktree-0.0.4}/tests/integration/test_state_persistence.py +0 -0
  38. {tasktree-0.0.2 → tasktree-0.0.4}/tests/unit/test_cli.py +0 -0
  39. {tasktree-0.0.2 → tasktree-0.0.4}/tests/unit/test_graph.py +0 -0
  40. {tasktree-0.0.2 → tasktree-0.0.4}/tests/unit/test_hasher.py +0 -0
  41. {tasktree-0.0.2 → tasktree-0.0.4}/tests/unit/test_state.py +0 -0
  42. {tasktree-0.0.2 → tasktree-0.0.4}/tests/unit/test_tasks.py +0 -0
  43. {tasktree-0.0.2 → tasktree-0.0.4}/tests/unit/test_types.py +0 -0
  44. {tasktree-0.0.2 → tasktree-0.0.4}/uv.lock +0 -0
@@ -12,7 +12,7 @@ jobs:
12
12
  strategy:
13
13
  fail-fast: false
14
14
  matrix:
15
- os: [ubuntu-latest, macos-latest, windows-latest]
15
+ os: [ubuntu-latest, macos-latest]
16
16
  python-version: ['3.11', '3.12']
17
17
 
18
18
  steps:
@@ -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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tasktree
3
- Version: 0.0.2
3
+ Version: 0.0.4
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,95 @@ 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
+ build:
78
+ desc: Compile stuff
79
+ outputs: [target/release/bin]
80
+ cmd: cargo build --release
81
+
82
+ package:
83
+ desc: build installers
84
+ deps: [build]
85
+ outputs: [awesome.deb]
86
+ cmd: |
87
+ for bin in target/release/*; do
88
+ if [[ -x "$bin" && ! -d "$bin" ]]; then
89
+ install -Dm755 "$bin" "debian/awesome/usr/bin/$(basename "$bin")"
90
+ fi
91
+ done
92
+
93
+ dpkg-buildpackage -us -uc
94
+
95
+ test:
96
+ desc: Run tests
97
+ deps: [package]
98
+ inputs: [tests/**/*.py]
99
+ cmd: PYTHONPATH=src python3 -m pytest tests/ -v
100
+ ```
101
+
102
+ If you want to run the tests then:
103
+ ```bash
104
+ tt test
105
+ ```
106
+ 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.
107
+
108
+ This is a toy example, but you can image how it plays out on a more complex project.
109
+
21
110
  ## Installation
22
111
 
23
112
  ### From PyPI (Recommended)
@@ -26,6 +115,16 @@ A task automation tool that combines simple command execution with dependency tr
26
115
  pipx install tasktree
27
116
  ```
28
117
 
118
+ 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:
119
+
120
+ ```bash
121
+ # If the target version is on the PATH
122
+ pipx install --python python3.12 tasktree
123
+
124
+ # With a path to an interpreter
125
+ pipx install --python /path/to/python3.12 tasktree
126
+ ```
127
+
29
128
  ### From Source
30
129
 
31
130
  For the latest unreleased version from GitHub:
@@ -61,9 +160,11 @@ test:
61
160
  Run tasks:
62
161
 
63
162
  ```bash
64
- tt build # Build the application
65
- tt test # Run tests (builds first if needed)
163
+ tt # Print the help
164
+ tt --help # ...also print the help
66
165
  tt --list # Show all available tasks
166
+ tt build # Build the application (assuming this is in your tasktree.yaml)
167
+ tt test # Run tests (builds first if needed)
67
168
  ```
68
169
 
69
170
  ## Core Concepts
@@ -72,11 +173,12 @@ tt --list # Show all available tasks
72
173
 
73
174
  Task Tree only runs tasks when necessary. A task executes if:
74
175
 
75
- - Its definition (command, outputs, working directory) has changed
176
+ - Its definition (command, outputs, working directory, environment) has changed
76
177
  - Any input files have changed since the last run
77
178
  - Any dependencies have re-run
78
179
  - It has never been executed before
79
180
  - It has no inputs or outputs (always runs)
181
+ - The execution environment has changed (CLI override or environment config change)
80
182
 
81
183
  ### Automatic Input Inheritance
82
184
 
@@ -105,17 +207,25 @@ All state lives in `.tasktree-state` at your project root. Stale entries are aut
105
207
  ```yaml
106
208
  task-name:
107
209
  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
210
+ deps: [other-task] # Task dependencies
211
+ inputs: [src/**/*.go] # Explicit input files (glob patterns)
212
+ outputs: [dist/binary] # Output files (glob patterns)
213
+ working_dir: subproject/ # Execution directory (default: project root)
214
+ env: bash-strict # Execution environment (optional)
215
+ args: [param1, param2:path=default] # Task parameters
216
+ cmd: go build -o dist/binary # Command to execute
114
217
  ```
115
218
 
116
219
  ### Commands
117
220
 
118
- Multi-line commands using YAML literal blocks:
221
+ **Single-line commands** are executed directly via the configured shell:
222
+
223
+ ```yaml
224
+ build:
225
+ cmd: cargo build --release
226
+ ```
227
+
228
+ **Multi-line commands** are written to temporary script files for proper execution:
119
229
 
120
230
  ```yaml
121
231
  deploy:
@@ -125,7 +235,9 @@ deploy:
125
235
  rsync -av dist/ server:/opt/app/
126
236
  ```
127
237
 
128
- Or folded blocks for long single-line commands:
238
+ Multi-line commands preserve shell syntax (line continuations, heredocs, etc.) and support shebangs on Unix/macOS.
239
+
240
+ Or use folded blocks for long single-line commands:
129
241
 
130
242
  ```yaml
131
243
  compile:
@@ -136,6 +248,58 @@ compile:
136
248
  -L lib -lm
137
249
  ```
138
250
 
251
+ ### Execution Environments
252
+
253
+ Configure custom shell environments for task execution:
254
+
255
+ ```yaml
256
+ environments:
257
+ default: bash-strict
258
+
259
+ bash-strict:
260
+ shell: bash
261
+ args: ['-c'] # For single-line: bash -c "command"
262
+ preamble: | # For multi-line: prepended to script
263
+ set -euo pipefail
264
+
265
+ python:
266
+ shell: python
267
+ args: ['-c']
268
+
269
+ powershell:
270
+ shell: powershell
271
+ args: ['-ExecutionPolicy', 'Bypass', '-Command']
272
+ preamble: |
273
+ $ErrorActionPreference = 'Stop'
274
+
275
+ tasks:
276
+ build:
277
+ # Uses 'default' environment (bash-strict)
278
+ cmd: cargo build --release
279
+
280
+ analyze:
281
+ env: python
282
+ cmd: |
283
+ import sys
284
+ print(f"Analyzing with Python {sys.version}")
285
+ # ... analysis code ...
286
+
287
+ windows-task:
288
+ env: powershell
289
+ cmd: |
290
+ Compress-Archive -Path dist/* -DestinationPath package.zip
291
+ ```
292
+
293
+ **Environment resolution priority:**
294
+ 1. CLI override: `tt --env python build`
295
+ 2. Task's `env` field
296
+ 3. Recipe's `default` environment
297
+ 4. Platform default (bash on Unix, cmd on Windows)
298
+
299
+ **Platform defaults** when no environments are configured:
300
+ - **Unix/macOS**: bash with `-c` args
301
+ - **Windows**: cmd with `/c` args
302
+
139
303
  ### Parameterised Tasks
140
304
 
141
305
  Tasks can accept arguments with optional defaults:
@@ -202,12 +366,19 @@ Input and output patterns support standard glob syntax:
202
366
 
203
367
  ### How State Works
204
368
 
205
- Each task is identified by a hash of its definition (command, outputs, working directory). State tracks:
369
+ Each task is identified by a hash of its definition. The hash includes:
370
+
371
+ - Command to execute
372
+ - Output patterns
373
+ - Working directory
374
+ - Argument definitions
375
+ - Execution environment
206
376
 
377
+ State tracks:
207
378
  - When the task last ran
208
379
  - Timestamps of input files at that time
209
380
 
210
- Tasks are re-run when their definition changes or inputs are newer than the last run.
381
+ Tasks are re-run when their definition changes, inputs are newer than the last run, or the environment changes.
211
382
 
212
383
  ### What's Not In The Hash
213
384
 
@@ -222,6 +393,72 @@ Changes to these don't invalidate cached state:
222
393
 
223
394
  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
395
 
396
+ ## Command-Line Options
397
+
398
+ Task Tree provides several command-line options for controlling task execution:
399
+
400
+ ### Execution Control
401
+
402
+ ```bash
403
+ # Force re-run (ignore freshness checks)
404
+ tt --force build
405
+ tt -f build
406
+
407
+ # Run only the specified task, skip dependencies (implies --force)
408
+ tt --only deploy
409
+ tt -o deploy
410
+
411
+ # Override environment for all tasks
412
+ tt --env python analyze
413
+ tt -e powershell build
414
+ ```
415
+
416
+ ### Information Commands
417
+
418
+ ```bash
419
+ # List all available tasks
420
+ tt --list
421
+ tt -l
422
+
423
+ # Show detailed task definition
424
+ tt --show build
425
+
426
+ # Show dependency tree (without execution)
427
+ tt --tree deploy
428
+
429
+ # Show version
430
+ tt --version
431
+ tt -v
432
+
433
+ # Create a blank recipe file
434
+ tt --init
435
+ ```
436
+
437
+ ### State Management
438
+
439
+ ```bash
440
+ # Remove state file (reset task cache)
441
+ tt --clean
442
+ tt --clean-state
443
+ tt --reset
444
+ ```
445
+
446
+ ### Common Workflows
447
+
448
+ ```bash
449
+ # Fresh build of everything
450
+ tt --force build
451
+
452
+ # Run a task without rebuilding dependencies
453
+ tt --only test
454
+
455
+ # Test with a different shell/environment
456
+ tt --env python test
457
+
458
+ # Force rebuild and deploy
459
+ tt --force deploy production
460
+ ```
461
+
225
462
  ## Example: Full Build Pipeline
226
463
 
227
464
  ```yaml