tasktree 0.0.20__tar.gz → 0.0.22__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.
- {tasktree-0.0.20 → tasktree-0.0.22}/.claude/settings.local.json +3 -1
- {tasktree-0.0.20 → tasktree-0.0.22}/.github/workflows/test.yml +9 -16
- {tasktree-0.0.20 → tasktree-0.0.22}/.gitignore +1 -2
- {tasktree-0.0.20 → tasktree-0.0.22}/CLAUDE.md +22 -5
- tasktree-0.0.20/README.md → tasktree-0.0.22/PKG-INFO +161 -20
- tasktree-0.0.20/PKG-INFO → tasktree-0.0.22/README.md +144 -35
- tasktree-0.0.22/__init__.py +3 -0
- tasktree-0.0.22/example/tasktree.yaml +64 -0
- {tasktree-0.0.20 → tasktree-0.0.22}/pyproject.toml +5 -4
- {tasktree-0.0.20 → tasktree-0.0.22}/schema/README.md +24 -6
- {tasktree-0.0.20 → tasktree-0.0.22}/schema/tasktree-schema.json +20 -5
- tasktree-0.0.22/src/__init__.py +4 -0
- {tasktree-0.0.20 → tasktree-0.0.22}/src/tasktree/__init__.py +4 -1
- {tasktree-0.0.20 → tasktree-0.0.22}/src/tasktree/cli.py +198 -60
- {tasktree-0.0.20 → tasktree-0.0.22}/src/tasktree/docker.py +105 -64
- {tasktree-0.0.20 → tasktree-0.0.22}/src/tasktree/executor.py +427 -310
- {tasktree-0.0.20 → tasktree-0.0.22}/src/tasktree/graph.py +138 -82
- {tasktree-0.0.20 → tasktree-0.0.22}/src/tasktree/hasher.py +81 -25
- {tasktree-0.0.20 → tasktree-0.0.22}/src/tasktree/parser.py +554 -344
- {tasktree-0.0.20 → tasktree-0.0.22}/src/tasktree/state.py +50 -22
- {tasktree-0.0.20 → tasktree-0.0.22}/src/tasktree/substitution.py +188 -117
- {tasktree-0.0.20 → tasktree-0.0.22}/src/tasktree/types.py +80 -25
- {tasktree-0.0.20 → tasktree-0.0.22}/tasktree.yaml +29 -26
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/e2e/__init__.py +9 -2
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/e2e/test_docker_basic.py +27 -13
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/e2e/test_docker_environment.py +29 -10
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/e2e/test_docker_ownership.py +22 -10
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/e2e/test_docker_volumes.py +32 -19
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/e2e/test_non_docker.py +24 -16
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_arg_choices.py +71 -21
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_arg_min_max.py +65 -18
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_builtin_variables.py +155 -69
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_clean_state.py +30 -9
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_cli_options.py +188 -82
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_dependency_execution.py +48 -24
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_dependency_outputs.py +52 -36
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_docker_build_args.py +12 -3
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_end_to_end.py +75 -14
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_exported_args.py +78 -61
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_input_detection.py +27 -9
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_missing_outputs.py +20 -4
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_nested_imports.py +52 -16
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_parameterized_deps_execution.py +26 -8
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_parameterized_deps_templates.py +42 -16
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_private_tasks_execution.py +54 -16
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_self_references.py +700 -90
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_state_persistence.py +26 -8
- tasktree-0.0.22/tests/integration/test_unified_execution.py +215 -0
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_variables.py +108 -27
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_working_directory.py +39 -11
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/unit/test_cli.py +134 -63
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/unit/test_dependency_parsing.py +76 -21
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/unit/test_docker.py +244 -61
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/unit/test_environment_tracking.py +119 -44
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/unit/test_executor.py +761 -237
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/unit/test_graph.py +156 -44
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/unit/test_hasher.py +44 -10
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/unit/test_list_formatting.py +225 -89
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/unit/test_parameterized_graph.py +44 -11
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/unit/test_parser.py +1395 -620
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/unit/test_private_tasks.py +28 -7
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/unit/test_state.py +40 -11
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/unit/test_substitution.py +706 -152
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/unit/test_types.py +144 -36
- {tasktree-0.0.20 → tasktree-0.0.22}/uv.lock +126 -3
- tasktree-0.0.20/example/tasktree.yaml +0 -37
- tasktree-0.0.20/src/__init__.py +0 -0
- {tasktree-0.0.20 → tasktree-0.0.22}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.22}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.22}/.github/workflows/claude-code-review.yml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.22}/.github/workflows/claude.yml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.22}/.github/workflows/release.yml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.22}/.github/workflows/validate-pipx-install.yml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.22}/.python-version +0 -0
- {tasktree-0.0.20 → tasktree-0.0.22}/example/source.txt +0 -0
- {tasktree-0.0.20 → tasktree-0.0.22}/schema/vscode-settings-snippet.json +0 -0
- {tasktree-0.0.20 → tasktree-0.0.22}/tests/integration/test_parameterized_dependencies.yaml +0 -0
|
@@ -27,31 +27,26 @@ jobs:
|
|
|
27
27
|
uses: astral-sh/setup-uv@v4
|
|
28
28
|
|
|
29
29
|
- name: Install dependencies
|
|
30
|
-
run:
|
|
31
|
-
uv pip install --system -e ".[dev]"
|
|
30
|
+
run: uv sync --extra dev
|
|
32
31
|
|
|
33
32
|
- name: Pre-pull Docker images for E2E tests
|
|
34
33
|
if: runner.os == 'Linux'
|
|
35
|
-
run:
|
|
36
|
-
docker pull alpine:latest
|
|
34
|
+
run: docker pull alpine:latest
|
|
37
35
|
|
|
38
36
|
- name: Run unit tests
|
|
39
|
-
run:
|
|
40
|
-
python -m pytest tests/unit/ -v --tb=short
|
|
37
|
+
run: uv run pytest tests/unit/ --tb=short
|
|
41
38
|
|
|
42
39
|
- name: Run integration tests
|
|
43
|
-
run:
|
|
44
|
-
python -m pytest tests/integration/ -v --tb=short
|
|
40
|
+
run: uv run pytest tests/integration/ --tb=short
|
|
45
41
|
|
|
46
42
|
- name: Run E2E Docker tests
|
|
47
43
|
if: runner.os == 'Linux'
|
|
48
|
-
run:
|
|
49
|
-
python -m pytest tests/e2e/ -v --tb=short
|
|
44
|
+
run: uv run pytest tests/e2e/ --tb=short
|
|
50
45
|
|
|
51
46
|
- name: Test CLI commands
|
|
52
47
|
run: |
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
uv run tt --help
|
|
49
|
+
uv run tt --list || true
|
|
55
50
|
|
|
56
51
|
lint:
|
|
57
52
|
runs-on: ubuntu-latest
|
|
@@ -67,9 +62,7 @@ jobs:
|
|
|
67
62
|
uses: astral-sh/setup-uv@v4
|
|
68
63
|
|
|
69
64
|
- name: Install dependencies
|
|
70
|
-
run:
|
|
71
|
-
uv pip install --system -e ".[dev]"
|
|
65
|
+
run: uv sync --extra dev
|
|
72
66
|
|
|
73
67
|
- name: Check code can be imported
|
|
74
|
-
run:
|
|
75
|
-
python -c "from tasktree import Executor, Recipe, Task; print('Import successful')"
|
|
68
|
+
run: uv run python -c "from tasktree import Executor, Recipe, Task; print('Import successful')"
|
|
@@ -74,15 +74,30 @@ Your sponsor is not made of money! Try to minimise token useage, so that we can
|
|
|
74
74
|
|
|
75
75
|
## Development Commands
|
|
76
76
|
|
|
77
|
+
## IMPORTANT! Tool use
|
|
78
|
+
### Athena Code Knowledge
|
|
79
|
+
This repo uses Athena to store and retrieve knowledge about the codebase. YOU SHOULD USE THE TOOL TO MAKE INQUIRIES ABOUT THE CODE.
|
|
80
|
+
|
|
81
|
+
1. Find a function/class: `athena locate <function/class name>`
|
|
82
|
+
2. Find information about what a function/class does:
|
|
83
|
+
- `athena info path/to/module.py:ClassName`
|
|
84
|
+
- `athena info path/to/module.py:ClassName.method`
|
|
85
|
+
- `athena info path/to/module.py:some_function`
|
|
86
|
+
3. Search for relevant code information:
|
|
87
|
+
- `athena serch "generate JWT authentication tokens`
|
|
88
|
+
4. AFTER code changes, run `athena status` to check which docs should have been updated
|
|
89
|
+
5. AFTER checking/updating docs, run `athena sync` to register the changes
|
|
90
|
+
|
|
91
|
+
**prefer this workflow to using native `grep` and `find` tools** for understanding the code.
|
|
92
|
+
|
|
77
93
|
### Testing
|
|
78
94
|
```bash
|
|
79
95
|
python3 -m pytest tests/
|
|
80
96
|
```
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
- **
|
|
84
|
-
- **
|
|
85
|
-
- **E2E tests** (`tests/e2e/`): 5 test files for Docker volumes, ownership, environment, and basic functionality
|
|
97
|
+
The project has tests across three categories:
|
|
98
|
+
- **Unit tests** (`tests/unit/`): Should always be updated for any change. Makes up the bulk of code coverage
|
|
99
|
+
- **Integration tests** (`tests/integration/`): Test changes using the Typer CliRunner to cover end-to-end workflows across multiple modules
|
|
100
|
+
- **E2E tests** (`tests/e2e/`): Heavyweight tests that cover running the tool in a subprocess and/or containerized environment
|
|
86
101
|
|
|
87
102
|
### Running the Application
|
|
88
103
|
```bash
|
|
@@ -139,6 +154,8 @@ environments:
|
|
|
139
154
|
env-name:
|
|
140
155
|
default: true # Make this the default environment
|
|
141
156
|
shell: /bin/bash # Shell environment
|
|
157
|
+
preamble: | # Optional preamble prepended to all commands
|
|
158
|
+
set -euo pipefail
|
|
142
159
|
# OR
|
|
143
160
|
dockerfile: path/to/Dockerfile # Docker environment
|
|
144
161
|
context: build-context-dir
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tasktree
|
|
3
|
+
Version: 0.0.22
|
|
4
|
+
Summary: A task automation tool with incremental execution
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: click>=8.1.0
|
|
7
|
+
Requires-Dist: colorama>=0.4.6
|
|
8
|
+
Requires-Dist: pathspec>=0.11.0
|
|
9
|
+
Requires-Dist: pyyaml>=6.0
|
|
10
|
+
Requires-Dist: rich>=13.0.0
|
|
11
|
+
Requires-Dist: typer>=0.9.0
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: black>=26.1.0; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
15
|
+
Requires-Dist: ruff>=0.14.14; extra == 'dev'
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
1
18
|
# Task Tree (tt)
|
|
2
19
|
|
|
3
20
|
[](https://github.com/kevinchannon/task-tree/actions/workflows/test.yml)
|
|
@@ -240,20 +257,19 @@ tasks:
|
|
|
240
257
|
cmd: go build -o dist/binary # Command to execute
|
|
241
258
|
```
|
|
242
259
|
|
|
260
|
+
**Task name constraints:**
|
|
261
|
+
- Task names cannot contain dots (`.`) - they are reserved for namespacing imported tasks
|
|
262
|
+
- Example: `build.release` is invalid as a task name, but valid as a reference to task `release` in namespace `build`
|
|
263
|
+
|
|
243
264
|
### Commands
|
|
244
265
|
|
|
245
|
-
|
|
266
|
+
All commands are executed by writing them to temporary script files. This provides consistent behavior and better shell syntax support:
|
|
246
267
|
|
|
247
268
|
```yaml
|
|
248
269
|
tasks:
|
|
249
270
|
build:
|
|
250
271
|
cmd: cargo build --release
|
|
251
|
-
```
|
|
252
272
|
|
|
253
|
-
**Multi-line commands** are written to temporary script files for proper execution:
|
|
254
|
-
|
|
255
|
-
```yaml
|
|
256
|
-
tasks:
|
|
257
273
|
deploy:
|
|
258
274
|
cmd: |
|
|
259
275
|
mkdir -p dist
|
|
@@ -261,7 +277,7 @@ tasks:
|
|
|
261
277
|
rsync -av dist/ server:/opt/app/
|
|
262
278
|
```
|
|
263
279
|
|
|
264
|
-
|
|
280
|
+
Commands preserve shell syntax (line continuations, heredocs, etc.) and support shebangs on Unix/macOS.
|
|
265
281
|
|
|
266
282
|
Or use folded blocks for long single-line commands:
|
|
267
283
|
|
|
@@ -277,7 +293,7 @@ tasks:
|
|
|
277
293
|
|
|
278
294
|
### Execution Environments
|
|
279
295
|
|
|
280
|
-
Configure custom shell environments for task execution:
|
|
296
|
+
Configure custom shell environments for task execution. Use the `preamble` field to add initialization code to all commands:
|
|
281
297
|
|
|
282
298
|
```yaml
|
|
283
299
|
environments:
|
|
@@ -285,17 +301,14 @@ environments:
|
|
|
285
301
|
|
|
286
302
|
bash-strict:
|
|
287
303
|
shell: bash
|
|
288
|
-
|
|
289
|
-
preamble: | # For multi-line: prepended to script
|
|
304
|
+
preamble: | # Prepended to all commands
|
|
290
305
|
set -euo pipefail
|
|
291
306
|
|
|
292
307
|
python:
|
|
293
308
|
shell: python
|
|
294
|
-
args: ['-c']
|
|
295
309
|
|
|
296
310
|
powershell:
|
|
297
311
|
shell: powershell
|
|
298
|
-
args: ['-ExecutionPolicy', 'Bypass', '-Command']
|
|
299
312
|
preamble: |
|
|
300
313
|
$ErrorActionPreference = 'Stop'
|
|
301
314
|
|
|
@@ -324,8 +337,8 @@ tasks:
|
|
|
324
337
|
4. Platform default (bash on Unix, cmd on Windows)
|
|
325
338
|
|
|
326
339
|
**Platform defaults** when no environments are configured:
|
|
327
|
-
- **Unix/macOS**: bash
|
|
328
|
-
- **Windows**: cmd
|
|
340
|
+
- **Unix/macOS**: bash
|
|
341
|
+
- **Windows**: cmd
|
|
329
342
|
|
|
330
343
|
### Docker Environments
|
|
331
344
|
|
|
@@ -820,7 +833,7 @@ Hint: Define named outputs like: outputs: [{ missing: 'path/to/file' }]
|
|
|
820
833
|
|
|
821
834
|
### Self-References
|
|
822
835
|
|
|
823
|
-
Tasks can reference their own
|
|
836
|
+
Tasks can reference their own inputs and outputs using `{{ self.inputs.name }}` (named access) or `{{ self.inputs.0 }}` (positional access) templates. This eliminates repetition when paths contain variables or when tasks have multiple inputs/outputs.
|
|
824
837
|
|
|
825
838
|
**Named Inputs and Outputs:**
|
|
826
839
|
|
|
@@ -847,8 +860,10 @@ tasks:
|
|
|
847
860
|
|
|
848
861
|
- **Defining named inputs**: `inputs: [{ name: "path/to/file" }]`
|
|
849
862
|
- **Defining named outputs**: `outputs: [{ name: "path/to/file" }]`
|
|
850
|
-
- **
|
|
851
|
-
- **
|
|
863
|
+
- **Defining anonymous inputs**: `inputs: ["path/to/file"]`
|
|
864
|
+
- **Defining anonymous outputs**: `outputs: ["path/to/file"]`
|
|
865
|
+
- **Referencing by name**: `{{ self.inputs.input_name }}` or `{{ self.outputs.output_name }}`
|
|
866
|
+
- **Referencing by index**: `{{ self.inputs.0 }}` or `{{ self.outputs.1 }}` (0-based)
|
|
852
867
|
- **Mixed format**: Can combine named and anonymous inputs/outputs in the same task
|
|
853
868
|
|
|
854
869
|
**Why Use Self-References?**
|
|
@@ -957,6 +972,129 @@ tasks:
|
|
|
957
972
|
cmd: build-tool --config {{ self.inputs.config }} --output {{ self.outputs.binary }}
|
|
958
973
|
```
|
|
959
974
|
|
|
975
|
+
**Positional Index References:**
|
|
976
|
+
|
|
977
|
+
In addition to named references, you can access inputs and outputs by their positional index using `{{ self.inputs.0 }}`, `{{ self.inputs.1 }}`, etc. This provides an alternative way to reference items, especially useful for:
|
|
978
|
+
|
|
979
|
+
- **Anonymous inputs/outputs**: Reference items that don't have names
|
|
980
|
+
- **Simple sequential access**: When order is more important than naming
|
|
981
|
+
- **Mixed with named access**: Use both styles in the same task
|
|
982
|
+
|
|
983
|
+
**Syntax:**
|
|
984
|
+
|
|
985
|
+
```yaml
|
|
986
|
+
tasks:
|
|
987
|
+
process:
|
|
988
|
+
inputs: ["file1.txt", "file2.txt", "file3.txt"]
|
|
989
|
+
outputs: ["output1.txt", "output2.txt"]
|
|
990
|
+
cmd: |
|
|
991
|
+
cat {{ self.inputs.0 }} {{ self.inputs.1 }} > {{ self.outputs.0 }}
|
|
992
|
+
cat {{ self.inputs.2 }} > {{ self.outputs.1 }}
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
Indices follow YAML declaration order, starting from 0 (zero-based indexing):
|
|
996
|
+
- First input/output = index 0
|
|
997
|
+
- Second input/output = index 1
|
|
998
|
+
- Third input/output = index 2, etc.
|
|
999
|
+
|
|
1000
|
+
**Works with Both Named and Anonymous:**
|
|
1001
|
+
|
|
1002
|
+
```yaml
|
|
1003
|
+
tasks:
|
|
1004
|
+
build:
|
|
1005
|
+
inputs:
|
|
1006
|
+
- config: "build.yaml" # Index 0, also accessible as {{ self.inputs.config }}
|
|
1007
|
+
- "src/**/*.c" # Index 1, ONLY accessible as {{ self.inputs.1 }}
|
|
1008
|
+
- headers: "include/*.h" # Index 2, also accessible as {{ self.inputs.headers }}
|
|
1009
|
+
outputs:
|
|
1010
|
+
- "dist/app.js" # Index 0
|
|
1011
|
+
- bundle: "dist/bundle.js" # Index 1, also accessible as {{ self.outputs.bundle }}
|
|
1012
|
+
cmd: |
|
|
1013
|
+
# Mix positional and named references
|
|
1014
|
+
build-tool \
|
|
1015
|
+
--config {{ self.inputs.0 }} \
|
|
1016
|
+
--sources {{ self.inputs.1 }} \
|
|
1017
|
+
--headers {{ self.inputs.headers }} \
|
|
1018
|
+
--output {{ self.outputs.bundle }}
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
**Same Item, Multiple Ways:**
|
|
1022
|
+
|
|
1023
|
+
Named items can be accessed by both name and index:
|
|
1024
|
+
|
|
1025
|
+
```yaml
|
|
1026
|
+
tasks:
|
|
1027
|
+
copy:
|
|
1028
|
+
inputs:
|
|
1029
|
+
- source: data.txt
|
|
1030
|
+
cmd: |
|
|
1031
|
+
# These are equivalent:
|
|
1032
|
+
cat {{ self.inputs.source }} > copy1.txt
|
|
1033
|
+
cat {{ self.inputs.0 }} > copy2.txt
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
**With Variables and Arguments:**
|
|
1037
|
+
|
|
1038
|
+
Positional references work with variable-expanded paths:
|
|
1039
|
+
|
|
1040
|
+
```yaml
|
|
1041
|
+
variables:
|
|
1042
|
+
version: "1.0"
|
|
1043
|
+
|
|
1044
|
+
tasks:
|
|
1045
|
+
package:
|
|
1046
|
+
args: [platform]
|
|
1047
|
+
inputs:
|
|
1048
|
+
- "dist/app-{{ var.version }}.js"
|
|
1049
|
+
- "dist/lib-{{ arg.platform }}.so"
|
|
1050
|
+
outputs: ["release-{{ var.version }}-{{ arg.platform }}.tar.gz"]
|
|
1051
|
+
cmd: tar czf {{ self.outputs.0 }} {{ self.inputs.0 }} {{ self.inputs.1 }}
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
**Index Boundaries:**
|
|
1055
|
+
|
|
1056
|
+
Indices are validated before execution:
|
|
1057
|
+
|
|
1058
|
+
```yaml
|
|
1059
|
+
tasks:
|
|
1060
|
+
build:
|
|
1061
|
+
inputs: ["file1.txt", "file2.txt"]
|
|
1062
|
+
cmd: cat {{ self.inputs.5 }} # Error: index 5 out of bounds!
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
Error message:
|
|
1066
|
+
```
|
|
1067
|
+
Task 'build' references input index '5' but only has 2 inputs (indices 0-1)
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
**Empty Lists:**
|
|
1071
|
+
|
|
1072
|
+
Referencing an index when no inputs/outputs exist:
|
|
1073
|
+
|
|
1074
|
+
```yaml
|
|
1075
|
+
tasks:
|
|
1076
|
+
generate:
|
|
1077
|
+
cmd: echo "test" > {{ self.outputs.0 }} # Error: no outputs defined!
|
|
1078
|
+
```
|
|
1079
|
+
|
|
1080
|
+
Error message:
|
|
1081
|
+
```
|
|
1082
|
+
Task 'generate' references output index '0' but has no outputs defined
|
|
1083
|
+
```
|
|
1084
|
+
|
|
1085
|
+
**When to Use Index References:**
|
|
1086
|
+
|
|
1087
|
+
- **Anonymous items**: Only way to reference inputs/outputs without names
|
|
1088
|
+
- **Order-based processing**: When the sequence matters more than naming
|
|
1089
|
+
- **Simple tasks**: Quick access without defining names
|
|
1090
|
+
- **Compatibility**: Accessing items in legacy YAML that uses anonymous format
|
|
1091
|
+
|
|
1092
|
+
**When to Use Named References:**
|
|
1093
|
+
|
|
1094
|
+
- **Clarity**: Names make commands more readable (`{{ self.inputs.config }}` vs `{{ self.inputs.2 }}`)
|
|
1095
|
+
- **Maintainability**: Adding/removing items doesn't break indices
|
|
1096
|
+
- **Complex tasks**: Many inputs/outputs are easier to manage with names
|
|
1097
|
+
|
|
960
1098
|
**Error Messages:**
|
|
961
1099
|
|
|
962
1100
|
If you reference a non-existent input or output:
|
|
@@ -996,18 +1134,21 @@ Hint: Define named inputs like: inputs: [{ src: 'file.txt' }]
|
|
|
996
1134
|
|
|
997
1135
|
**Key Behaviors:**
|
|
998
1136
|
|
|
1137
|
+
- **Two access methods**: Reference by name (`{{ self.inputs.name }}`) or by index (`{{ self.inputs.0 }}`)
|
|
999
1138
|
- **Template resolution**: Self-references are resolved during dependency graph planning
|
|
1000
1139
|
- **Substitution order**: Variables → Dependency outputs → Self-references → Arguments/Environment
|
|
1001
|
-
- **Fail-fast validation**: Errors are caught before execution begins
|
|
1002
|
-
- **Clear error messages**: Lists available names if reference doesn't exist
|
|
1140
|
+
- **Fail-fast validation**: Errors are caught before execution begins (missing names, out-of-bounds indices)
|
|
1141
|
+
- **Clear error messages**: Lists available names/indices if reference doesn't exist
|
|
1003
1142
|
- **Backward compatible**: Existing anonymous inputs/outputs work unchanged
|
|
1004
1143
|
- **State tracking**: Works correctly with incremental execution and freshness checks
|
|
1144
|
+
- **Index order**: Positional indices follow YAML declaration order (0-based)
|
|
1005
1145
|
|
|
1006
1146
|
**Limitations:**
|
|
1007
1147
|
|
|
1008
|
-
- **Anonymous not referenceable**:
|
|
1148
|
+
- **Anonymous not referenceable by name**: Anonymous inputs/outputs cannot be referenced by name (use positional index instead: `{{ self.inputs.0 }}`)
|
|
1009
1149
|
- **Case sensitive**: `{{ self.inputs.Src }}` and `{{ self.inputs.src }}` are different
|
|
1010
1150
|
- **Argument defaults**: Self-references in argument defaults are not supported (arguments are evaluated before self-references)
|
|
1151
|
+
- **No negative indices**: Python-style negative indexing (`{{ self.inputs.-1 }}`) is not supported
|
|
1011
1152
|
|
|
1012
1153
|
**Use Cases:**
|
|
1013
1154
|
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: tasktree
|
|
3
|
-
Version: 0.0.20
|
|
4
|
-
Summary: A task automation tool with incremental execution
|
|
5
|
-
Requires-Python: >=3.11
|
|
6
|
-
Requires-Dist: click>=8.1.0
|
|
7
|
-
Requires-Dist: colorama>=0.4.6
|
|
8
|
-
Requires-Dist: pathspec>=0.11.0
|
|
9
|
-
Requires-Dist: pyyaml>=6.0
|
|
10
|
-
Requires-Dist: rich>=13.0.0
|
|
11
|
-
Requires-Dist: typer>=0.9.0
|
|
12
|
-
Provides-Extra: dev
|
|
13
|
-
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
14
|
-
Description-Content-Type: text/markdown
|
|
15
|
-
|
|
16
1
|
# Task Tree (tt)
|
|
17
2
|
|
|
18
3
|
[](https://github.com/kevinchannon/task-tree/actions/workflows/test.yml)
|
|
@@ -255,20 +240,19 @@ tasks:
|
|
|
255
240
|
cmd: go build -o dist/binary # Command to execute
|
|
256
241
|
```
|
|
257
242
|
|
|
243
|
+
**Task name constraints:**
|
|
244
|
+
- Task names cannot contain dots (`.`) - they are reserved for namespacing imported tasks
|
|
245
|
+
- Example: `build.release` is invalid as a task name, but valid as a reference to task `release` in namespace `build`
|
|
246
|
+
|
|
258
247
|
### Commands
|
|
259
248
|
|
|
260
|
-
|
|
249
|
+
All commands are executed by writing them to temporary script files. This provides consistent behavior and better shell syntax support:
|
|
261
250
|
|
|
262
251
|
```yaml
|
|
263
252
|
tasks:
|
|
264
253
|
build:
|
|
265
254
|
cmd: cargo build --release
|
|
266
|
-
```
|
|
267
255
|
|
|
268
|
-
**Multi-line commands** are written to temporary script files for proper execution:
|
|
269
|
-
|
|
270
|
-
```yaml
|
|
271
|
-
tasks:
|
|
272
256
|
deploy:
|
|
273
257
|
cmd: |
|
|
274
258
|
mkdir -p dist
|
|
@@ -276,7 +260,7 @@ tasks:
|
|
|
276
260
|
rsync -av dist/ server:/opt/app/
|
|
277
261
|
```
|
|
278
262
|
|
|
279
|
-
|
|
263
|
+
Commands preserve shell syntax (line continuations, heredocs, etc.) and support shebangs on Unix/macOS.
|
|
280
264
|
|
|
281
265
|
Or use folded blocks for long single-line commands:
|
|
282
266
|
|
|
@@ -292,7 +276,7 @@ tasks:
|
|
|
292
276
|
|
|
293
277
|
### Execution Environments
|
|
294
278
|
|
|
295
|
-
Configure custom shell environments for task execution:
|
|
279
|
+
Configure custom shell environments for task execution. Use the `preamble` field to add initialization code to all commands:
|
|
296
280
|
|
|
297
281
|
```yaml
|
|
298
282
|
environments:
|
|
@@ -300,17 +284,14 @@ environments:
|
|
|
300
284
|
|
|
301
285
|
bash-strict:
|
|
302
286
|
shell: bash
|
|
303
|
-
|
|
304
|
-
preamble: | # For multi-line: prepended to script
|
|
287
|
+
preamble: | # Prepended to all commands
|
|
305
288
|
set -euo pipefail
|
|
306
289
|
|
|
307
290
|
python:
|
|
308
291
|
shell: python
|
|
309
|
-
args: ['-c']
|
|
310
292
|
|
|
311
293
|
powershell:
|
|
312
294
|
shell: powershell
|
|
313
|
-
args: ['-ExecutionPolicy', 'Bypass', '-Command']
|
|
314
295
|
preamble: |
|
|
315
296
|
$ErrorActionPreference = 'Stop'
|
|
316
297
|
|
|
@@ -339,8 +320,8 @@ tasks:
|
|
|
339
320
|
4. Platform default (bash on Unix, cmd on Windows)
|
|
340
321
|
|
|
341
322
|
**Platform defaults** when no environments are configured:
|
|
342
|
-
- **Unix/macOS**: bash
|
|
343
|
-
- **Windows**: cmd
|
|
323
|
+
- **Unix/macOS**: bash
|
|
324
|
+
- **Windows**: cmd
|
|
344
325
|
|
|
345
326
|
### Docker Environments
|
|
346
327
|
|
|
@@ -835,7 +816,7 @@ Hint: Define named outputs like: outputs: [{ missing: 'path/to/file' }]
|
|
|
835
816
|
|
|
836
817
|
### Self-References
|
|
837
818
|
|
|
838
|
-
Tasks can reference their own
|
|
819
|
+
Tasks can reference their own inputs and outputs using `{{ self.inputs.name }}` (named access) or `{{ self.inputs.0 }}` (positional access) templates. This eliminates repetition when paths contain variables or when tasks have multiple inputs/outputs.
|
|
839
820
|
|
|
840
821
|
**Named Inputs and Outputs:**
|
|
841
822
|
|
|
@@ -862,8 +843,10 @@ tasks:
|
|
|
862
843
|
|
|
863
844
|
- **Defining named inputs**: `inputs: [{ name: "path/to/file" }]`
|
|
864
845
|
- **Defining named outputs**: `outputs: [{ name: "path/to/file" }]`
|
|
865
|
-
- **
|
|
866
|
-
- **
|
|
846
|
+
- **Defining anonymous inputs**: `inputs: ["path/to/file"]`
|
|
847
|
+
- **Defining anonymous outputs**: `outputs: ["path/to/file"]`
|
|
848
|
+
- **Referencing by name**: `{{ self.inputs.input_name }}` or `{{ self.outputs.output_name }}`
|
|
849
|
+
- **Referencing by index**: `{{ self.inputs.0 }}` or `{{ self.outputs.1 }}` (0-based)
|
|
867
850
|
- **Mixed format**: Can combine named and anonymous inputs/outputs in the same task
|
|
868
851
|
|
|
869
852
|
**Why Use Self-References?**
|
|
@@ -972,6 +955,129 @@ tasks:
|
|
|
972
955
|
cmd: build-tool --config {{ self.inputs.config }} --output {{ self.outputs.binary }}
|
|
973
956
|
```
|
|
974
957
|
|
|
958
|
+
**Positional Index References:**
|
|
959
|
+
|
|
960
|
+
In addition to named references, you can access inputs and outputs by their positional index using `{{ self.inputs.0 }}`, `{{ self.inputs.1 }}`, etc. This provides an alternative way to reference items, especially useful for:
|
|
961
|
+
|
|
962
|
+
- **Anonymous inputs/outputs**: Reference items that don't have names
|
|
963
|
+
- **Simple sequential access**: When order is more important than naming
|
|
964
|
+
- **Mixed with named access**: Use both styles in the same task
|
|
965
|
+
|
|
966
|
+
**Syntax:**
|
|
967
|
+
|
|
968
|
+
```yaml
|
|
969
|
+
tasks:
|
|
970
|
+
process:
|
|
971
|
+
inputs: ["file1.txt", "file2.txt", "file3.txt"]
|
|
972
|
+
outputs: ["output1.txt", "output2.txt"]
|
|
973
|
+
cmd: |
|
|
974
|
+
cat {{ self.inputs.0 }} {{ self.inputs.1 }} > {{ self.outputs.0 }}
|
|
975
|
+
cat {{ self.inputs.2 }} > {{ self.outputs.1 }}
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
Indices follow YAML declaration order, starting from 0 (zero-based indexing):
|
|
979
|
+
- First input/output = index 0
|
|
980
|
+
- Second input/output = index 1
|
|
981
|
+
- Third input/output = index 2, etc.
|
|
982
|
+
|
|
983
|
+
**Works with Both Named and Anonymous:**
|
|
984
|
+
|
|
985
|
+
```yaml
|
|
986
|
+
tasks:
|
|
987
|
+
build:
|
|
988
|
+
inputs:
|
|
989
|
+
- config: "build.yaml" # Index 0, also accessible as {{ self.inputs.config }}
|
|
990
|
+
- "src/**/*.c" # Index 1, ONLY accessible as {{ self.inputs.1 }}
|
|
991
|
+
- headers: "include/*.h" # Index 2, also accessible as {{ self.inputs.headers }}
|
|
992
|
+
outputs:
|
|
993
|
+
- "dist/app.js" # Index 0
|
|
994
|
+
- bundle: "dist/bundle.js" # Index 1, also accessible as {{ self.outputs.bundle }}
|
|
995
|
+
cmd: |
|
|
996
|
+
# Mix positional and named references
|
|
997
|
+
build-tool \
|
|
998
|
+
--config {{ self.inputs.0 }} \
|
|
999
|
+
--sources {{ self.inputs.1 }} \
|
|
1000
|
+
--headers {{ self.inputs.headers }} \
|
|
1001
|
+
--output {{ self.outputs.bundle }}
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
**Same Item, Multiple Ways:**
|
|
1005
|
+
|
|
1006
|
+
Named items can be accessed by both name and index:
|
|
1007
|
+
|
|
1008
|
+
```yaml
|
|
1009
|
+
tasks:
|
|
1010
|
+
copy:
|
|
1011
|
+
inputs:
|
|
1012
|
+
- source: data.txt
|
|
1013
|
+
cmd: |
|
|
1014
|
+
# These are equivalent:
|
|
1015
|
+
cat {{ self.inputs.source }} > copy1.txt
|
|
1016
|
+
cat {{ self.inputs.0 }} > copy2.txt
|
|
1017
|
+
```
|
|
1018
|
+
|
|
1019
|
+
**With Variables and Arguments:**
|
|
1020
|
+
|
|
1021
|
+
Positional references work with variable-expanded paths:
|
|
1022
|
+
|
|
1023
|
+
```yaml
|
|
1024
|
+
variables:
|
|
1025
|
+
version: "1.0"
|
|
1026
|
+
|
|
1027
|
+
tasks:
|
|
1028
|
+
package:
|
|
1029
|
+
args: [platform]
|
|
1030
|
+
inputs:
|
|
1031
|
+
- "dist/app-{{ var.version }}.js"
|
|
1032
|
+
- "dist/lib-{{ arg.platform }}.so"
|
|
1033
|
+
outputs: ["release-{{ var.version }}-{{ arg.platform }}.tar.gz"]
|
|
1034
|
+
cmd: tar czf {{ self.outputs.0 }} {{ self.inputs.0 }} {{ self.inputs.1 }}
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
**Index Boundaries:**
|
|
1038
|
+
|
|
1039
|
+
Indices are validated before execution:
|
|
1040
|
+
|
|
1041
|
+
```yaml
|
|
1042
|
+
tasks:
|
|
1043
|
+
build:
|
|
1044
|
+
inputs: ["file1.txt", "file2.txt"]
|
|
1045
|
+
cmd: cat {{ self.inputs.5 }} # Error: index 5 out of bounds!
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
Error message:
|
|
1049
|
+
```
|
|
1050
|
+
Task 'build' references input index '5' but only has 2 inputs (indices 0-1)
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
**Empty Lists:**
|
|
1054
|
+
|
|
1055
|
+
Referencing an index when no inputs/outputs exist:
|
|
1056
|
+
|
|
1057
|
+
```yaml
|
|
1058
|
+
tasks:
|
|
1059
|
+
generate:
|
|
1060
|
+
cmd: echo "test" > {{ self.outputs.0 }} # Error: no outputs defined!
|
|
1061
|
+
```
|
|
1062
|
+
|
|
1063
|
+
Error message:
|
|
1064
|
+
```
|
|
1065
|
+
Task 'generate' references output index '0' but has no outputs defined
|
|
1066
|
+
```
|
|
1067
|
+
|
|
1068
|
+
**When to Use Index References:**
|
|
1069
|
+
|
|
1070
|
+
- **Anonymous items**: Only way to reference inputs/outputs without names
|
|
1071
|
+
- **Order-based processing**: When the sequence matters more than naming
|
|
1072
|
+
- **Simple tasks**: Quick access without defining names
|
|
1073
|
+
- **Compatibility**: Accessing items in legacy YAML that uses anonymous format
|
|
1074
|
+
|
|
1075
|
+
**When to Use Named References:**
|
|
1076
|
+
|
|
1077
|
+
- **Clarity**: Names make commands more readable (`{{ self.inputs.config }}` vs `{{ self.inputs.2 }}`)
|
|
1078
|
+
- **Maintainability**: Adding/removing items doesn't break indices
|
|
1079
|
+
- **Complex tasks**: Many inputs/outputs are easier to manage with names
|
|
1080
|
+
|
|
975
1081
|
**Error Messages:**
|
|
976
1082
|
|
|
977
1083
|
If you reference a non-existent input or output:
|
|
@@ -1011,18 +1117,21 @@ Hint: Define named inputs like: inputs: [{ src: 'file.txt' }]
|
|
|
1011
1117
|
|
|
1012
1118
|
**Key Behaviors:**
|
|
1013
1119
|
|
|
1120
|
+
- **Two access methods**: Reference by name (`{{ self.inputs.name }}`) or by index (`{{ self.inputs.0 }}`)
|
|
1014
1121
|
- **Template resolution**: Self-references are resolved during dependency graph planning
|
|
1015
1122
|
- **Substitution order**: Variables → Dependency outputs → Self-references → Arguments/Environment
|
|
1016
|
-
- **Fail-fast validation**: Errors are caught before execution begins
|
|
1017
|
-
- **Clear error messages**: Lists available names if reference doesn't exist
|
|
1123
|
+
- **Fail-fast validation**: Errors are caught before execution begins (missing names, out-of-bounds indices)
|
|
1124
|
+
- **Clear error messages**: Lists available names/indices if reference doesn't exist
|
|
1018
1125
|
- **Backward compatible**: Existing anonymous inputs/outputs work unchanged
|
|
1019
1126
|
- **State tracking**: Works correctly with incremental execution and freshness checks
|
|
1127
|
+
- **Index order**: Positional indices follow YAML declaration order (0-based)
|
|
1020
1128
|
|
|
1021
1129
|
**Limitations:**
|
|
1022
1130
|
|
|
1023
|
-
- **Anonymous not referenceable**:
|
|
1131
|
+
- **Anonymous not referenceable by name**: Anonymous inputs/outputs cannot be referenced by name (use positional index instead: `{{ self.inputs.0 }}`)
|
|
1024
1132
|
- **Case sensitive**: `{{ self.inputs.Src }}` and `{{ self.inputs.src }}` are different
|
|
1025
1133
|
- **Argument defaults**: Self-references in argument defaults are not supported (arguments are evaluated before self-references)
|
|
1134
|
+
- **No negative indices**: Python-style negative indexing (`{{ self.inputs.-1 }}`) is not supported
|
|
1026
1135
|
|
|
1027
1136
|
**Use Cases:**
|
|
1028
1137
|
|