tasktree 0.0.20__tar.gz → 0.0.21__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.21}/.claude/settings.local.json +3 -1
- {tasktree-0.0.20 → tasktree-0.0.21}/.gitignore +1 -2
- {tasktree-0.0.20 → tasktree-0.0.21}/CLAUDE.md +20 -5
- {tasktree-0.0.20 → tasktree-0.0.21}/PKG-INFO +135 -7
- {tasktree-0.0.20 → tasktree-0.0.21}/README.md +134 -6
- tasktree-0.0.21/__init__.py +3 -0
- tasktree-0.0.21/example/tasktree.yaml +64 -0
- {tasktree-0.0.20 → tasktree-0.0.21}/pyproject.toml +1 -1
- {tasktree-0.0.20 → tasktree-0.0.21}/schema/README.md +24 -6
- {tasktree-0.0.20 → tasktree-0.0.21}/schema/tasktree-schema.json +20 -5
- tasktree-0.0.21/src/__init__.py +4 -0
- {tasktree-0.0.20 → tasktree-0.0.21}/src/tasktree/__init__.py +4 -1
- {tasktree-0.0.20 → tasktree-0.0.21}/src/tasktree/cli.py +107 -29
- {tasktree-0.0.20 → tasktree-0.0.21}/src/tasktree/docker.py +87 -53
- {tasktree-0.0.20 → tasktree-0.0.21}/src/tasktree/executor.py +194 -129
- {tasktree-0.0.20 → tasktree-0.0.21}/src/tasktree/graph.py +123 -72
- {tasktree-0.0.20 → tasktree-0.0.21}/src/tasktree/hasher.py +68 -19
- {tasktree-0.0.20 → tasktree-0.0.21}/src/tasktree/parser.py +340 -229
- {tasktree-0.0.20 → tasktree-0.0.21}/src/tasktree/state.py +46 -17
- {tasktree-0.0.20 → tasktree-0.0.21}/src/tasktree/substitution.py +162 -103
- {tasktree-0.0.20 → tasktree-0.0.21}/src/tasktree/types.py +50 -12
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/e2e/__init__.py +4 -1
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/e2e/test_docker_basic.py +20 -5
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/e2e/test_docker_environment.py +20 -5
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/e2e/test_docker_ownership.py +16 -4
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/e2e/test_docker_volumes.py +24 -6
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/e2e/test_non_docker.py +16 -4
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_arg_choices.py +48 -12
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_arg_min_max.py +60 -15
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_builtin_variables.py +52 -13
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_clean_state.py +28 -7
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_cli_options.py +143 -36
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_dependency_execution.py +30 -8
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_dependency_outputs.py +38 -8
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_docker_build_args.py +12 -3
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_end_to_end.py +20 -5
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_exported_args.py +51 -13
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_input_detection.py +24 -6
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_missing_outputs.py +9 -2
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_nested_imports.py +24 -6
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_parameterized_deps_execution.py +24 -6
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_parameterized_deps_templates.py +32 -8
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_private_tasks_execution.py +40 -10
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_self_references.py +544 -46
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_state_persistence.py +24 -6
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_variables.py +108 -27
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_working_directory.py +24 -6
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/unit/test_cli.py +92 -23
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/unit/test_dependency_parsing.py +76 -19
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/unit/test_docker.py +183 -46
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/unit/test_environment_tracking.py +84 -21
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/unit/test_executor.py +197 -47
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/unit/test_graph.py +94 -22
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/unit/test_hasher.py +43 -10
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/unit/test_list_formatting.py +164 -42
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/unit/test_parameterized_graph.py +44 -11
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/unit/test_parser.py +1031 -222
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/unit/test_private_tasks.py +28 -7
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/unit/test_state.py +34 -7
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/unit/test_substitution.py +618 -115
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/unit/test_types.py +144 -36
- 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.21}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.21}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.21}/.github/workflows/claude-code-review.yml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.21}/.github/workflows/claude.yml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.21}/.github/workflows/release.yml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.21}/.github/workflows/test.yml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.21}/.github/workflows/validate-pipx-install.yml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.21}/.python-version +0 -0
- {tasktree-0.0.20 → tasktree-0.0.21}/example/source.txt +0 -0
- {tasktree-0.0.20 → tasktree-0.0.21}/schema/vscode-settings-snippet.json +0 -0
- {tasktree-0.0.20 → tasktree-0.0.21}/tasktree.yaml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.21}/tests/integration/test_parameterized_dependencies.yaml +0 -0
- {tasktree-0.0.20 → tasktree-0.0.21}/uv.lock +0 -0
|
@@ -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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tasktree
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.21
|
|
4
4
|
Summary: A task automation tool with incremental execution
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Requires-Dist: click>=8.1.0
|
|
@@ -835,7 +835,7 @@ Hint: Define named outputs like: outputs: [{ missing: 'path/to/file' }]
|
|
|
835
835
|
|
|
836
836
|
### Self-References
|
|
837
837
|
|
|
838
|
-
Tasks can reference their own
|
|
838
|
+
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
839
|
|
|
840
840
|
**Named Inputs and Outputs:**
|
|
841
841
|
|
|
@@ -862,8 +862,10 @@ tasks:
|
|
|
862
862
|
|
|
863
863
|
- **Defining named inputs**: `inputs: [{ name: "path/to/file" }]`
|
|
864
864
|
- **Defining named outputs**: `outputs: [{ name: "path/to/file" }]`
|
|
865
|
-
- **
|
|
866
|
-
- **
|
|
865
|
+
- **Defining anonymous inputs**: `inputs: ["path/to/file"]`
|
|
866
|
+
- **Defining anonymous outputs**: `outputs: ["path/to/file"]`
|
|
867
|
+
- **Referencing by name**: `{{ self.inputs.input_name }}` or `{{ self.outputs.output_name }}`
|
|
868
|
+
- **Referencing by index**: `{{ self.inputs.0 }}` or `{{ self.outputs.1 }}` (0-based)
|
|
867
869
|
- **Mixed format**: Can combine named and anonymous inputs/outputs in the same task
|
|
868
870
|
|
|
869
871
|
**Why Use Self-References?**
|
|
@@ -972,6 +974,129 @@ tasks:
|
|
|
972
974
|
cmd: build-tool --config {{ self.inputs.config }} --output {{ self.outputs.binary }}
|
|
973
975
|
```
|
|
974
976
|
|
|
977
|
+
**Positional Index References:**
|
|
978
|
+
|
|
979
|
+
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:
|
|
980
|
+
|
|
981
|
+
- **Anonymous inputs/outputs**: Reference items that don't have names
|
|
982
|
+
- **Simple sequential access**: When order is more important than naming
|
|
983
|
+
- **Mixed with named access**: Use both styles in the same task
|
|
984
|
+
|
|
985
|
+
**Syntax:**
|
|
986
|
+
|
|
987
|
+
```yaml
|
|
988
|
+
tasks:
|
|
989
|
+
process:
|
|
990
|
+
inputs: ["file1.txt", "file2.txt", "file3.txt"]
|
|
991
|
+
outputs: ["output1.txt", "output2.txt"]
|
|
992
|
+
cmd: |
|
|
993
|
+
cat {{ self.inputs.0 }} {{ self.inputs.1 }} > {{ self.outputs.0 }}
|
|
994
|
+
cat {{ self.inputs.2 }} > {{ self.outputs.1 }}
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
Indices follow YAML declaration order, starting from 0 (zero-based indexing):
|
|
998
|
+
- First input/output = index 0
|
|
999
|
+
- Second input/output = index 1
|
|
1000
|
+
- Third input/output = index 2, etc.
|
|
1001
|
+
|
|
1002
|
+
**Works with Both Named and Anonymous:**
|
|
1003
|
+
|
|
1004
|
+
```yaml
|
|
1005
|
+
tasks:
|
|
1006
|
+
build:
|
|
1007
|
+
inputs:
|
|
1008
|
+
- config: "build.yaml" # Index 0, also accessible as {{ self.inputs.config }}
|
|
1009
|
+
- "src/**/*.c" # Index 1, ONLY accessible as {{ self.inputs.1 }}
|
|
1010
|
+
- headers: "include/*.h" # Index 2, also accessible as {{ self.inputs.headers }}
|
|
1011
|
+
outputs:
|
|
1012
|
+
- "dist/app.js" # Index 0
|
|
1013
|
+
- bundle: "dist/bundle.js" # Index 1, also accessible as {{ self.outputs.bundle }}
|
|
1014
|
+
cmd: |
|
|
1015
|
+
# Mix positional and named references
|
|
1016
|
+
build-tool \
|
|
1017
|
+
--config {{ self.inputs.0 }} \
|
|
1018
|
+
--sources {{ self.inputs.1 }} \
|
|
1019
|
+
--headers {{ self.inputs.headers }} \
|
|
1020
|
+
--output {{ self.outputs.bundle }}
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
**Same Item, Multiple Ways:**
|
|
1024
|
+
|
|
1025
|
+
Named items can be accessed by both name and index:
|
|
1026
|
+
|
|
1027
|
+
```yaml
|
|
1028
|
+
tasks:
|
|
1029
|
+
copy:
|
|
1030
|
+
inputs:
|
|
1031
|
+
- source: data.txt
|
|
1032
|
+
cmd: |
|
|
1033
|
+
# These are equivalent:
|
|
1034
|
+
cat {{ self.inputs.source }} > copy1.txt
|
|
1035
|
+
cat {{ self.inputs.0 }} > copy2.txt
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
**With Variables and Arguments:**
|
|
1039
|
+
|
|
1040
|
+
Positional references work with variable-expanded paths:
|
|
1041
|
+
|
|
1042
|
+
```yaml
|
|
1043
|
+
variables:
|
|
1044
|
+
version: "1.0"
|
|
1045
|
+
|
|
1046
|
+
tasks:
|
|
1047
|
+
package:
|
|
1048
|
+
args: [platform]
|
|
1049
|
+
inputs:
|
|
1050
|
+
- "dist/app-{{ var.version }}.js"
|
|
1051
|
+
- "dist/lib-{{ arg.platform }}.so"
|
|
1052
|
+
outputs: ["release-{{ var.version }}-{{ arg.platform }}.tar.gz"]
|
|
1053
|
+
cmd: tar czf {{ self.outputs.0 }} {{ self.inputs.0 }} {{ self.inputs.1 }}
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
**Index Boundaries:**
|
|
1057
|
+
|
|
1058
|
+
Indices are validated before execution:
|
|
1059
|
+
|
|
1060
|
+
```yaml
|
|
1061
|
+
tasks:
|
|
1062
|
+
build:
|
|
1063
|
+
inputs: ["file1.txt", "file2.txt"]
|
|
1064
|
+
cmd: cat {{ self.inputs.5 }} # Error: index 5 out of bounds!
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
Error message:
|
|
1068
|
+
```
|
|
1069
|
+
Task 'build' references input index '5' but only has 2 inputs (indices 0-1)
|
|
1070
|
+
```
|
|
1071
|
+
|
|
1072
|
+
**Empty Lists:**
|
|
1073
|
+
|
|
1074
|
+
Referencing an index when no inputs/outputs exist:
|
|
1075
|
+
|
|
1076
|
+
```yaml
|
|
1077
|
+
tasks:
|
|
1078
|
+
generate:
|
|
1079
|
+
cmd: echo "test" > {{ self.outputs.0 }} # Error: no outputs defined!
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
Error message:
|
|
1083
|
+
```
|
|
1084
|
+
Task 'generate' references output index '0' but has no outputs defined
|
|
1085
|
+
```
|
|
1086
|
+
|
|
1087
|
+
**When to Use Index References:**
|
|
1088
|
+
|
|
1089
|
+
- **Anonymous items**: Only way to reference inputs/outputs without names
|
|
1090
|
+
- **Order-based processing**: When the sequence matters more than naming
|
|
1091
|
+
- **Simple tasks**: Quick access without defining names
|
|
1092
|
+
- **Compatibility**: Accessing items in legacy YAML that uses anonymous format
|
|
1093
|
+
|
|
1094
|
+
**When to Use Named References:**
|
|
1095
|
+
|
|
1096
|
+
- **Clarity**: Names make commands more readable (`{{ self.inputs.config }}` vs `{{ self.inputs.2 }}`)
|
|
1097
|
+
- **Maintainability**: Adding/removing items doesn't break indices
|
|
1098
|
+
- **Complex tasks**: Many inputs/outputs are easier to manage with names
|
|
1099
|
+
|
|
975
1100
|
**Error Messages:**
|
|
976
1101
|
|
|
977
1102
|
If you reference a non-existent input or output:
|
|
@@ -1011,18 +1136,21 @@ Hint: Define named inputs like: inputs: [{ src: 'file.txt' }]
|
|
|
1011
1136
|
|
|
1012
1137
|
**Key Behaviors:**
|
|
1013
1138
|
|
|
1139
|
+
- **Two access methods**: Reference by name (`{{ self.inputs.name }}`) or by index (`{{ self.inputs.0 }}`)
|
|
1014
1140
|
- **Template resolution**: Self-references are resolved during dependency graph planning
|
|
1015
1141
|
- **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
|
|
1142
|
+
- **Fail-fast validation**: Errors are caught before execution begins (missing names, out-of-bounds indices)
|
|
1143
|
+
- **Clear error messages**: Lists available names/indices if reference doesn't exist
|
|
1018
1144
|
- **Backward compatible**: Existing anonymous inputs/outputs work unchanged
|
|
1019
1145
|
- **State tracking**: Works correctly with incremental execution and freshness checks
|
|
1146
|
+
- **Index order**: Positional indices follow YAML declaration order (0-based)
|
|
1020
1147
|
|
|
1021
1148
|
**Limitations:**
|
|
1022
1149
|
|
|
1023
|
-
- **Anonymous not referenceable**:
|
|
1150
|
+
- **Anonymous not referenceable by name**: Anonymous inputs/outputs cannot be referenced by name (use positional index instead: `{{ self.inputs.0 }}`)
|
|
1024
1151
|
- **Case sensitive**: `{{ self.inputs.Src }}` and `{{ self.inputs.src }}` are different
|
|
1025
1152
|
- **Argument defaults**: Self-references in argument defaults are not supported (arguments are evaluated before self-references)
|
|
1153
|
+
- **No negative indices**: Python-style negative indexing (`{{ self.inputs.-1 }}`) is not supported
|
|
1026
1154
|
|
|
1027
1155
|
**Use Cases:**
|
|
1028
1156
|
|
|
@@ -820,7 +820,7 @@ Hint: Define named outputs like: outputs: [{ missing: 'path/to/file' }]
|
|
|
820
820
|
|
|
821
821
|
### Self-References
|
|
822
822
|
|
|
823
|
-
Tasks can reference their own
|
|
823
|
+
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
824
|
|
|
825
825
|
**Named Inputs and Outputs:**
|
|
826
826
|
|
|
@@ -847,8 +847,10 @@ tasks:
|
|
|
847
847
|
|
|
848
848
|
- **Defining named inputs**: `inputs: [{ name: "path/to/file" }]`
|
|
849
849
|
- **Defining named outputs**: `outputs: [{ name: "path/to/file" }]`
|
|
850
|
-
- **
|
|
851
|
-
- **
|
|
850
|
+
- **Defining anonymous inputs**: `inputs: ["path/to/file"]`
|
|
851
|
+
- **Defining anonymous outputs**: `outputs: ["path/to/file"]`
|
|
852
|
+
- **Referencing by name**: `{{ self.inputs.input_name }}` or `{{ self.outputs.output_name }}`
|
|
853
|
+
- **Referencing by index**: `{{ self.inputs.0 }}` or `{{ self.outputs.1 }}` (0-based)
|
|
852
854
|
- **Mixed format**: Can combine named and anonymous inputs/outputs in the same task
|
|
853
855
|
|
|
854
856
|
**Why Use Self-References?**
|
|
@@ -957,6 +959,129 @@ tasks:
|
|
|
957
959
|
cmd: build-tool --config {{ self.inputs.config }} --output {{ self.outputs.binary }}
|
|
958
960
|
```
|
|
959
961
|
|
|
962
|
+
**Positional Index References:**
|
|
963
|
+
|
|
964
|
+
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:
|
|
965
|
+
|
|
966
|
+
- **Anonymous inputs/outputs**: Reference items that don't have names
|
|
967
|
+
- **Simple sequential access**: When order is more important than naming
|
|
968
|
+
- **Mixed with named access**: Use both styles in the same task
|
|
969
|
+
|
|
970
|
+
**Syntax:**
|
|
971
|
+
|
|
972
|
+
```yaml
|
|
973
|
+
tasks:
|
|
974
|
+
process:
|
|
975
|
+
inputs: ["file1.txt", "file2.txt", "file3.txt"]
|
|
976
|
+
outputs: ["output1.txt", "output2.txt"]
|
|
977
|
+
cmd: |
|
|
978
|
+
cat {{ self.inputs.0 }} {{ self.inputs.1 }} > {{ self.outputs.0 }}
|
|
979
|
+
cat {{ self.inputs.2 }} > {{ self.outputs.1 }}
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
Indices follow YAML declaration order, starting from 0 (zero-based indexing):
|
|
983
|
+
- First input/output = index 0
|
|
984
|
+
- Second input/output = index 1
|
|
985
|
+
- Third input/output = index 2, etc.
|
|
986
|
+
|
|
987
|
+
**Works with Both Named and Anonymous:**
|
|
988
|
+
|
|
989
|
+
```yaml
|
|
990
|
+
tasks:
|
|
991
|
+
build:
|
|
992
|
+
inputs:
|
|
993
|
+
- config: "build.yaml" # Index 0, also accessible as {{ self.inputs.config }}
|
|
994
|
+
- "src/**/*.c" # Index 1, ONLY accessible as {{ self.inputs.1 }}
|
|
995
|
+
- headers: "include/*.h" # Index 2, also accessible as {{ self.inputs.headers }}
|
|
996
|
+
outputs:
|
|
997
|
+
- "dist/app.js" # Index 0
|
|
998
|
+
- bundle: "dist/bundle.js" # Index 1, also accessible as {{ self.outputs.bundle }}
|
|
999
|
+
cmd: |
|
|
1000
|
+
# Mix positional and named references
|
|
1001
|
+
build-tool \
|
|
1002
|
+
--config {{ self.inputs.0 }} \
|
|
1003
|
+
--sources {{ self.inputs.1 }} \
|
|
1004
|
+
--headers {{ self.inputs.headers }} \
|
|
1005
|
+
--output {{ self.outputs.bundle }}
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
**Same Item, Multiple Ways:**
|
|
1009
|
+
|
|
1010
|
+
Named items can be accessed by both name and index:
|
|
1011
|
+
|
|
1012
|
+
```yaml
|
|
1013
|
+
tasks:
|
|
1014
|
+
copy:
|
|
1015
|
+
inputs:
|
|
1016
|
+
- source: data.txt
|
|
1017
|
+
cmd: |
|
|
1018
|
+
# These are equivalent:
|
|
1019
|
+
cat {{ self.inputs.source }} > copy1.txt
|
|
1020
|
+
cat {{ self.inputs.0 }} > copy2.txt
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
**With Variables and Arguments:**
|
|
1024
|
+
|
|
1025
|
+
Positional references work with variable-expanded paths:
|
|
1026
|
+
|
|
1027
|
+
```yaml
|
|
1028
|
+
variables:
|
|
1029
|
+
version: "1.0"
|
|
1030
|
+
|
|
1031
|
+
tasks:
|
|
1032
|
+
package:
|
|
1033
|
+
args: [platform]
|
|
1034
|
+
inputs:
|
|
1035
|
+
- "dist/app-{{ var.version }}.js"
|
|
1036
|
+
- "dist/lib-{{ arg.platform }}.so"
|
|
1037
|
+
outputs: ["release-{{ var.version }}-{{ arg.platform }}.tar.gz"]
|
|
1038
|
+
cmd: tar czf {{ self.outputs.0 }} {{ self.inputs.0 }} {{ self.inputs.1 }}
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
**Index Boundaries:**
|
|
1042
|
+
|
|
1043
|
+
Indices are validated before execution:
|
|
1044
|
+
|
|
1045
|
+
```yaml
|
|
1046
|
+
tasks:
|
|
1047
|
+
build:
|
|
1048
|
+
inputs: ["file1.txt", "file2.txt"]
|
|
1049
|
+
cmd: cat {{ self.inputs.5 }} # Error: index 5 out of bounds!
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
Error message:
|
|
1053
|
+
```
|
|
1054
|
+
Task 'build' references input index '5' but only has 2 inputs (indices 0-1)
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
**Empty Lists:**
|
|
1058
|
+
|
|
1059
|
+
Referencing an index when no inputs/outputs exist:
|
|
1060
|
+
|
|
1061
|
+
```yaml
|
|
1062
|
+
tasks:
|
|
1063
|
+
generate:
|
|
1064
|
+
cmd: echo "test" > {{ self.outputs.0 }} # Error: no outputs defined!
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
Error message:
|
|
1068
|
+
```
|
|
1069
|
+
Task 'generate' references output index '0' but has no outputs defined
|
|
1070
|
+
```
|
|
1071
|
+
|
|
1072
|
+
**When to Use Index References:**
|
|
1073
|
+
|
|
1074
|
+
- **Anonymous items**: Only way to reference inputs/outputs without names
|
|
1075
|
+
- **Order-based processing**: When the sequence matters more than naming
|
|
1076
|
+
- **Simple tasks**: Quick access without defining names
|
|
1077
|
+
- **Compatibility**: Accessing items in legacy YAML that uses anonymous format
|
|
1078
|
+
|
|
1079
|
+
**When to Use Named References:**
|
|
1080
|
+
|
|
1081
|
+
- **Clarity**: Names make commands more readable (`{{ self.inputs.config }}` vs `{{ self.inputs.2 }}`)
|
|
1082
|
+
- **Maintainability**: Adding/removing items doesn't break indices
|
|
1083
|
+
- **Complex tasks**: Many inputs/outputs are easier to manage with names
|
|
1084
|
+
|
|
960
1085
|
**Error Messages:**
|
|
961
1086
|
|
|
962
1087
|
If you reference a non-existent input or output:
|
|
@@ -996,18 +1121,21 @@ Hint: Define named inputs like: inputs: [{ src: 'file.txt' }]
|
|
|
996
1121
|
|
|
997
1122
|
**Key Behaviors:**
|
|
998
1123
|
|
|
1124
|
+
- **Two access methods**: Reference by name (`{{ self.inputs.name }}`) or by index (`{{ self.inputs.0 }}`)
|
|
999
1125
|
- **Template resolution**: Self-references are resolved during dependency graph planning
|
|
1000
1126
|
- **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
|
|
1127
|
+
- **Fail-fast validation**: Errors are caught before execution begins (missing names, out-of-bounds indices)
|
|
1128
|
+
- **Clear error messages**: Lists available names/indices if reference doesn't exist
|
|
1003
1129
|
- **Backward compatible**: Existing anonymous inputs/outputs work unchanged
|
|
1004
1130
|
- **State tracking**: Works correctly with incremental execution and freshness checks
|
|
1131
|
+
- **Index order**: Positional indices follow YAML declaration order (0-based)
|
|
1005
1132
|
|
|
1006
1133
|
**Limitations:**
|
|
1007
1134
|
|
|
1008
|
-
- **Anonymous not referenceable**:
|
|
1135
|
+
- **Anonymous not referenceable by name**: Anonymous inputs/outputs cannot be referenced by name (use positional index instead: `{{ self.inputs.0 }}`)
|
|
1009
1136
|
- **Case sensitive**: `{{ self.inputs.Src }}` and `{{ self.inputs.src }}` are different
|
|
1010
1137
|
- **Argument defaults**: Self-references in argument defaults are not supported (arguments are evaluated before self-references)
|
|
1138
|
+
- **No negative indices**: Python-style negative indexing (`{{ self.inputs.-1 }}`) is not supported
|
|
1011
1139
|
|
|
1012
1140
|
**Use Cases:**
|
|
1013
1141
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
variables:
|
|
2
|
+
log_level: { env: LOG_LEVEL, default: "info" }
|
|
3
|
+
|
|
4
|
+
tasks:
|
|
5
|
+
build:
|
|
6
|
+
desc: Building outputs (imagine this is a call to Cargo, or gcc, or something)
|
|
7
|
+
args:
|
|
8
|
+
- build_type:
|
|
9
|
+
choices: [ "debug", "release", "{{ var.log_level }}" ]
|
|
10
|
+
default: "{{ var.log_level }}"
|
|
11
|
+
- target:
|
|
12
|
+
type: str
|
|
13
|
+
choices:
|
|
14
|
+
- "x86"
|
|
15
|
+
- "x86_64"
|
|
16
|
+
- "arm"
|
|
17
|
+
- "ppc"
|
|
18
|
+
default: "x86_64"
|
|
19
|
+
outputs: [build/**]
|
|
20
|
+
cmd: |
|
|
21
|
+
echo building for {{ arg.target }}...
|
|
22
|
+
cd build
|
|
23
|
+
echo "code and stuff" > output-{{ arg.build_type }}-{{ arg.target }}.txt
|
|
24
|
+
|
|
25
|
+
package:
|
|
26
|
+
desc: Create archive
|
|
27
|
+
deps:
|
|
28
|
+
- build: [ "debug", "x86" ]
|
|
29
|
+
- build: [ "release" ]
|
|
30
|
+
- build: { build_type: "release", target: "ppc" }
|
|
31
|
+
outputs: [archive.tar.gz]
|
|
32
|
+
cmd: echo "making archive.tar.gz..."
|
|
33
|
+
|
|
34
|
+
transform:
|
|
35
|
+
desc: Transform data using self-references (demonstrates named inputs/outputs)
|
|
36
|
+
inputs:
|
|
37
|
+
- source: build/output-debug-x86_64.txt # Named input - can reference with {{ self.inputs.source }}
|
|
38
|
+
- config: config.yaml # Named input
|
|
39
|
+
outputs:
|
|
40
|
+
- result: processed/result.txt # Named output - can reference with {{ self.outputs.result }}
|
|
41
|
+
- log: processed/transform.log # Named output
|
|
42
|
+
cmd: |
|
|
43
|
+
mkdir -p processed
|
|
44
|
+
echo "Processing {{ self.inputs.source }} with config {{ self.inputs.config }}" > {{ self.outputs.log }}
|
|
45
|
+
cat {{ self.inputs.source }} | tr '[:lower:]' '[:upper:]' > {{ self.outputs.result }}
|
|
46
|
+
echo "Done" >> {{ self.outputs.log }}
|
|
47
|
+
|
|
48
|
+
deploy:
|
|
49
|
+
desc: Deploy using dependency outputs and self-references
|
|
50
|
+
deps: [transform]
|
|
51
|
+
inputs:
|
|
52
|
+
- manifest: deploy-manifest.yaml
|
|
53
|
+
outputs:
|
|
54
|
+
- deployed: deployed/app.tar.gz
|
|
55
|
+
cmd: |
|
|
56
|
+
mkdir -p deployed
|
|
57
|
+
echo "Deploying with manifest {{ self.inputs.manifest }}"
|
|
58
|
+
tar czf {{ self.outputs.deployed }} {{ dep.transform.outputs.result }} {{ self.inputs.manifest }}
|
|
59
|
+
echo "Deployed to {{ self.outputs.deployed }}"
|
|
60
|
+
|
|
61
|
+
clean:
|
|
62
|
+
desc: Clean generated files
|
|
63
|
+
inputs: [ archive.tar.gz ]
|
|
64
|
+
cmd: rm -f archive.tar.gz
|
|
@@ -62,11 +62,14 @@ check-jsonschema --schemafile schema/tasktree-schema.json tasktree.yaml
|
|
|
62
62
|
|
|
63
63
|
The schema validates:
|
|
64
64
|
|
|
65
|
-
- **Top-level structure**: Only `imports`, `environments`, and `tasks` are allowed at root
|
|
65
|
+
- **Top-level structure**: Only `imports`, `environments`, `variables`, and `tasks` are allowed at root
|
|
66
66
|
- **Required fields**: Tasks must have a `cmd` field
|
|
67
67
|
- **Field types**: Ensures strings, arrays, and objects are used correctly
|
|
68
68
|
- **Naming patterns**: Task names and namespaces must match `^[a-zA-Z][a-zA-Z0-9_-]*$`
|
|
69
|
-
- **
|
|
69
|
+
- **Named inputs/outputs**: Supports both anonymous (strings) and named (objects) format
|
|
70
|
+
- **Self-references**: Named inputs/outputs can be referenced with `{{ self.inputs.name }}` and `{{ self.outputs.name }}`
|
|
71
|
+
- **Dependency outputs**: Named outputs can be referenced with `{{ dep.task.outputs.name }}`
|
|
72
|
+
- **Environment requirements**: Environments must specify a `shell` (or `dockerfile` for Docker environments)
|
|
70
73
|
|
|
71
74
|
## Example
|
|
72
75
|
|
|
@@ -85,25 +88,40 @@ tasks:
|
|
|
85
88
|
build:
|
|
86
89
|
desc: Build the application
|
|
87
90
|
deps: [base.setup]
|
|
88
|
-
inputs:
|
|
91
|
+
inputs:
|
|
92
|
+
- sources: "src/**/*.rs" # Named input - can use {{ self.inputs.sources }}
|
|
89
93
|
outputs:
|
|
90
94
|
- binary: target/release/bin # Named output - can be referenced
|
|
91
95
|
- target/release/bin.map # Anonymous output
|
|
92
|
-
cmd: cargo build --release
|
|
96
|
+
cmd: cargo build --release --manifest-path {{ self.inputs.sources }}/../Cargo.toml
|
|
93
97
|
|
|
94
98
|
test:
|
|
95
99
|
desc: Run tests
|
|
96
100
|
deps: [build]
|
|
97
101
|
cmd: cargo test
|
|
98
102
|
|
|
103
|
+
package:
|
|
104
|
+
desc: Package the application
|
|
105
|
+
deps: [build]
|
|
106
|
+
inputs:
|
|
107
|
+
- manifest: package.yaml # Named input
|
|
108
|
+
outputs:
|
|
109
|
+
- archive: dist/app.tar.gz # Named output
|
|
110
|
+
cmd: |
|
|
111
|
+
mkdir -p dist
|
|
112
|
+
# Use self-references for own inputs/outputs
|
|
113
|
+
tar czf {{ self.outputs.archive }} \
|
|
114
|
+
{{ dep.build.outputs.binary }} \
|
|
115
|
+
{{ self.inputs.manifest }}
|
|
116
|
+
|
|
99
117
|
deploy:
|
|
100
118
|
desc: Deploy to environment
|
|
101
|
-
deps: [
|
|
119
|
+
deps: [package]
|
|
102
120
|
args: [environment, region=us-west-1]
|
|
103
121
|
cmd: |
|
|
104
122
|
echo "Deploying to {{ arg.environment }} in {{ arg.region }}"
|
|
105
123
|
# Reference named output from dependency
|
|
106
|
-
scp {{ dep.
|
|
124
|
+
scp {{ dep.package.outputs.archive }} server:/opt/
|
|
107
125
|
./deploy.sh {{ arg.environment }} {{ arg.region }}
|
|
108
126
|
```
|
|
109
127
|
|
|
@@ -244,21 +244,36 @@
|
|
|
244
244
|
]
|
|
245
245
|
},
|
|
246
246
|
"inputs": {
|
|
247
|
-
"description": "Input file patterns (glob supported). Task re-runs if inputs change.",
|
|
247
|
+
"description": "Input file patterns (glob supported). Supports anonymous inputs (strings) and named inputs (objects). Named inputs can be referenced in the same task using {{ self.inputs.name }}. Task re-runs if inputs change.",
|
|
248
248
|
"oneOf": [
|
|
249
249
|
{
|
|
250
250
|
"type": "array",
|
|
251
251
|
"items": {
|
|
252
|
-
"
|
|
252
|
+
"oneOf": [
|
|
253
|
+
{
|
|
254
|
+
"type": "string",
|
|
255
|
+
"description": "Anonymous input file pattern"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"type": "object",
|
|
259
|
+
"description": "Named input (format: { name: 'path/to/file' })",
|
|
260
|
+
"minProperties": 1,
|
|
261
|
+
"maxProperties": 1,
|
|
262
|
+
"additionalProperties": {
|
|
263
|
+
"type": "string"
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
]
|
|
253
267
|
}
|
|
254
268
|
},
|
|
255
269
|
{
|
|
256
|
-
"type": "string"
|
|
270
|
+
"type": "string",
|
|
271
|
+
"description": "Single anonymous input file pattern"
|
|
257
272
|
}
|
|
258
273
|
]
|
|
259
274
|
},
|
|
260
275
|
"outputs": {
|
|
261
|
-
"description": "Output file patterns (glob supported). Supports anonymous outputs (strings) and named outputs (objects). Named outputs can be referenced by dependent tasks using {{ dep.task.outputs.name }}. Task re-runs if outputs missing.",
|
|
276
|
+
"description": "Output file patterns (glob supported). Supports anonymous outputs (strings) and named outputs (objects). Named outputs can be referenced by dependent tasks using {{ dep.task.outputs.name }} or in the same task using {{ self.outputs.name }}. Task re-runs if outputs missing.",
|
|
262
277
|
"oneOf": [
|
|
263
278
|
{
|
|
264
279
|
"type": "array",
|
|
@@ -352,7 +367,7 @@
|
|
|
352
367
|
},
|
|
353
368
|
"cmd": {
|
|
354
369
|
"type": "string",
|
|
355
|
-
"description": "Shell command to execute.
|
|
370
|
+
"description": "Shell command to execute. Supports template substitution: {{ arg.name }} for arguments, {{ var.name }} for variables, {{ env.NAME }} for environment variables, {{ dep.task.outputs.name }} for dependency outputs, {{ self.inputs.name }} and {{ self.outputs.name }} for own inputs/outputs, {{ tt.* }} for built-in variables."
|
|
356
371
|
}
|
|
357
372
|
},
|
|
358
373
|
"required": ["cmd"],
|