tasktree 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tasktree/cli.py +9 -5
- tasktree/executor.py +20 -7
- {tasktree-0.0.1.dist-info → tasktree-0.0.2.dist-info}/METADATA +10 -22
- {tasktree-0.0.1.dist-info → tasktree-0.0.2.dist-info}/RECORD +6 -6
- {tasktree-0.0.1.dist-info → tasktree-0.0.2.dist-info}/WHEEL +0 -0
- {tasktree-0.0.1.dist-info → tasktree-0.0.2.dist-info}/entry_points.txt +0 -0
tasktree/cli.py
CHANGED
|
@@ -236,13 +236,16 @@ def main(
|
|
|
236
236
|
reset: Optional[bool] = typer.Option(
|
|
237
237
|
None, "--reset", help="Remove state file (reset task cache)"
|
|
238
238
|
),
|
|
239
|
+
force: Optional[bool] = typer.Option(
|
|
240
|
+
None, "--force", "-f", help="Force re-run all tasks (ignore freshness)"
|
|
241
|
+
),
|
|
239
242
|
task_args: Optional[List[str]] = typer.Argument(
|
|
240
243
|
None, help="Task name and arguments"
|
|
241
244
|
),
|
|
242
245
|
):
|
|
243
|
-
"""Task Tree - A task automation tool with
|
|
246
|
+
"""Task Tree - A task automation tool with incremental execution.
|
|
244
247
|
|
|
245
|
-
Run tasks defined in tasktree.yaml with
|
|
248
|
+
Run tasks defined in tasktree.yaml with dependency tracking
|
|
246
249
|
and incremental execution.
|
|
247
250
|
|
|
248
251
|
Examples:
|
|
@@ -284,7 +287,7 @@ def main(
|
|
|
284
287
|
|
|
285
288
|
# Handle task execution
|
|
286
289
|
if task_args:
|
|
287
|
-
_execute_dynamic_task(task_args)
|
|
290
|
+
_execute_dynamic_task(task_args, force=force or False)
|
|
288
291
|
else:
|
|
289
292
|
# No arguments - show available tasks
|
|
290
293
|
recipe = _get_recipe()
|
|
@@ -332,11 +335,12 @@ def _get_recipe() -> Recipe | None:
|
|
|
332
335
|
raise typer.Exit(1)
|
|
333
336
|
|
|
334
337
|
|
|
335
|
-
def _execute_dynamic_task(args: list[str]) -> None:
|
|
338
|
+
def _execute_dynamic_task(args: list[str], force: bool = False) -> None:
|
|
336
339
|
"""Execute a task specified by name with arguments.
|
|
337
340
|
|
|
338
341
|
Args:
|
|
339
342
|
args: Command line arguments (task name and task arguments)
|
|
343
|
+
force: If True, ignore freshness and re-run all tasks
|
|
340
344
|
"""
|
|
341
345
|
if not args:
|
|
342
346
|
return
|
|
@@ -373,7 +377,7 @@ def _execute_dynamic_task(args: list[str]) -> None:
|
|
|
373
377
|
# Execute task
|
|
374
378
|
executor = Executor(recipe, state)
|
|
375
379
|
try:
|
|
376
|
-
executor.execute_task(task_name, args_dict, dry_run=False)
|
|
380
|
+
executor.execute_task(task_name, args_dict, dry_run=False, force=force)
|
|
377
381
|
console.print(f"[green]✓ Task '{task_name}' completed successfully[/green]")
|
|
378
382
|
except Exception as e:
|
|
379
383
|
console.print(f"[red]✗ Task '{task_name}' failed: {e}[/red]")
|
tasktree/executor.py
CHANGED
|
@@ -51,25 +51,36 @@ class Executor:
|
|
|
51
51
|
task: Task,
|
|
52
52
|
args_dict: dict[str, Any],
|
|
53
53
|
dep_statuses: dict[str, TaskStatus],
|
|
54
|
+
force: bool = False,
|
|
54
55
|
) -> TaskStatus:
|
|
55
56
|
"""Check if a task needs to run.
|
|
56
57
|
|
|
57
58
|
A task executes if ANY of these conditions are met:
|
|
58
|
-
1.
|
|
59
|
-
2.
|
|
60
|
-
3. Any
|
|
61
|
-
4.
|
|
62
|
-
5.
|
|
63
|
-
6.
|
|
59
|
+
1. Force flag is set (--force)
|
|
60
|
+
2. Task definition hash differs from cached state
|
|
61
|
+
3. Any explicit inputs have newer mtime than last_run
|
|
62
|
+
4. Any implicit inputs (from deps) have changed
|
|
63
|
+
5. No cached state exists for this task+args combination
|
|
64
|
+
6. Task has no inputs AND no outputs (always runs)
|
|
65
|
+
7. Different arguments than any cached execution
|
|
64
66
|
|
|
65
67
|
Args:
|
|
66
68
|
task: Task to check
|
|
67
69
|
args_dict: Arguments for this task execution
|
|
68
70
|
dep_statuses: Status of dependencies
|
|
71
|
+
force: If True, ignore freshness and force execution
|
|
69
72
|
|
|
70
73
|
Returns:
|
|
71
74
|
TaskStatus indicating whether task will run and why
|
|
72
75
|
"""
|
|
76
|
+
# If force flag is set, always run
|
|
77
|
+
if force:
|
|
78
|
+
return TaskStatus(
|
|
79
|
+
task_name=task.name,
|
|
80
|
+
will_run=True,
|
|
81
|
+
reason="forced",
|
|
82
|
+
)
|
|
83
|
+
|
|
73
84
|
# Compute hashes
|
|
74
85
|
task_hash = hash_task(task.cmd, task.outputs, task.working_dir, task.args)
|
|
75
86
|
args_hash = hash_args(args_dict) if args_dict else None
|
|
@@ -136,6 +147,7 @@ class Executor:
|
|
|
136
147
|
task_name: str,
|
|
137
148
|
args_dict: dict[str, Any] | None = None,
|
|
138
149
|
dry_run: bool = False,
|
|
150
|
+
force: bool = False,
|
|
139
151
|
) -> dict[str, TaskStatus]:
|
|
140
152
|
"""Execute a task and its dependencies.
|
|
141
153
|
|
|
@@ -143,6 +155,7 @@ class Executor:
|
|
|
143
155
|
task_name: Name of task to execute
|
|
144
156
|
args_dict: Arguments to pass to the task
|
|
145
157
|
dry_run: If True, only check what would run without executing
|
|
158
|
+
force: If True, ignore freshness and re-run all tasks
|
|
146
159
|
|
|
147
160
|
Returns:
|
|
148
161
|
Dictionary of task names to their execution status
|
|
@@ -167,7 +180,7 @@ class Executor:
|
|
|
167
180
|
# Determine task-specific args (only for target task)
|
|
168
181
|
task_args = args_dict if name == task_name else {}
|
|
169
182
|
|
|
170
|
-
status = self.check_task_status(task, task_args, dep_statuses)
|
|
183
|
+
status = self.check_task_status(task, task_args, dep_statuses, force=force)
|
|
171
184
|
statuses[name] = status
|
|
172
185
|
|
|
173
186
|
if dry_run:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tasktree
|
|
3
|
-
Version: 0.0.
|
|
4
|
-
Summary: A task automation tool with
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: A task automation tool with incremental execution
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Requires-Dist: click>=8.1.0
|
|
7
7
|
Requires-Dist: colorama>=0.4.6
|
|
@@ -14,9 +14,9 @@ Description-Content-Type: text/markdown
|
|
|
14
14
|
|
|
15
15
|
# Task Tree (tt)
|
|
16
16
|
|
|
17
|
-
[](https://github.com/kevinchannon/task-tree/actions/workflows/test.yml)
|
|
18
18
|
|
|
19
|
-
A task automation tool that combines simple command execution with
|
|
19
|
+
A task automation tool that combines simple command execution with dependency tracking and incremental execution.
|
|
20
20
|
|
|
21
21
|
## Installation
|
|
22
22
|
|
|
@@ -31,13 +31,13 @@ pipx install tasktree
|
|
|
31
31
|
For the latest unreleased version from GitHub:
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
pipx install git+https://github.com/kevinchannon/
|
|
34
|
+
pipx install git+https://github.com/kevinchannon/task-tree.git
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
Or to install from a local clone:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
git clone https://github.com/kevinchannon/
|
|
40
|
+
git clone https://github.com/kevinchannon/task-tree.git
|
|
41
41
|
cd tasktree
|
|
42
42
|
pipx install .
|
|
43
43
|
```
|
|
@@ -297,7 +297,7 @@ State file uses JSON format for simplicity and standard library compatibility.
|
|
|
297
297
|
|
|
298
298
|
```bash
|
|
299
299
|
# Clone repository
|
|
300
|
-
git clone https://github.com/kevinchannon/
|
|
300
|
+
git clone https://github.com/kevinchannon/task-tree.git
|
|
301
301
|
cd tasktree
|
|
302
302
|
|
|
303
303
|
# Install uv (if not already installed)
|
|
@@ -363,8 +363,8 @@ git push origin v1.0.0
|
|
|
363
363
|
- Publish to PyPI
|
|
364
364
|
|
|
365
365
|
4. Verify the release:
|
|
366
|
-
- GitHub: https://github.com/kevinchannon/
|
|
367
|
-
- PyPI: https://pypi.org/
|
|
366
|
+
- GitHub: https://github.com/kevinchannon/task-tree/releases
|
|
367
|
+
- PyPI: https://pypi.org/kevinchannon/tasktree/
|
|
368
368
|
- Test: `pipx install --force tasktree`
|
|
369
369
|
|
|
370
370
|
### Version Numbering
|
|
@@ -372,16 +372,4 @@ git push origin v1.0.0
|
|
|
372
372
|
Follow semantic versioning:
|
|
373
373
|
- `v1.0.0` - Major release (breaking changes)
|
|
374
374
|
- `v1.1.0` - Minor release (new features, backward compatible)
|
|
375
|
-
- `v1.1.1` - Patch release (bug fixes)
|
|
376
|
-
|
|
377
|
-
### PyPI Trusted Publishing Setup
|
|
378
|
-
|
|
379
|
-
Before the first release, configure trusted publishing on PyPI:
|
|
380
|
-
|
|
381
|
-
1. Go to https://pypi.org/manage/account/publishing/
|
|
382
|
-
2. Add a new publisher:
|
|
383
|
-
- **PyPI Project Name**: `tasktree`
|
|
384
|
-
- **Owner**: `kevinchannon`
|
|
385
|
-
- **Repository name**: `tasktree`
|
|
386
|
-
- **Workflow name**: `release.yml`
|
|
387
|
-
- **Environment name**: (leave blank)
|
|
375
|
+
- `v1.1.1` - Patch release (bug fixes)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
tasktree/__init__.py,sha256=MVmdvKb3JdqLlo0x2_TPGMfgFC0HsDnP79HAzGnFnjI,1081
|
|
2
|
-
tasktree/cli.py,sha256=
|
|
3
|
-
tasktree/executor.py,sha256=
|
|
2
|
+
tasktree/cli.py,sha256=VZGB8FJ8glZLn_z2bjSLLbmGRfFZ5Zh4nc1cYDLjGck,15904
|
|
3
|
+
tasktree/executor.py,sha256=ZM-qEsgvjfRz77GEZzxF29L3I2d8Uz5W1ev7Mox9Vnc,12287
|
|
4
4
|
tasktree/graph.py,sha256=9ngfg93y7EkOIN_lUQa0u-JhnwiMN1UdQQvIFw8RYCE,4181
|
|
5
5
|
tasktree/hasher.py,sha256=jvXBvIfFH9g6AOBHHd012v8nG5JOgVnHH35mIEtVkwc,2133
|
|
6
6
|
tasktree/parser.py,sha256=F2LbB84NinvBQT8zLONxn036R9Rued2y4XB3WGZ8PLk,9150
|
|
7
7
|
tasktree/state.py,sha256=rxKtS3SbsPtAuraHbN807RGWfoYYkQ3pe8CxUstwo2k,3535
|
|
8
8
|
tasktree/tasks.py,sha256=2QdQZtJAX2rSGbyXKG1z9VF_siz1DUzdvzCgPkykxtU,173
|
|
9
9
|
tasktree/types.py,sha256=wrBzO-Z2ebCTRjWyOWNvuCjqAq-74Zyb9E4FQ4beF38,3751
|
|
10
|
-
tasktree-0.0.
|
|
11
|
-
tasktree-0.0.
|
|
12
|
-
tasktree-0.0.
|
|
13
|
-
tasktree-0.0.
|
|
10
|
+
tasktree-0.0.2.dist-info/METADATA,sha256=yUoIhImEiwFND5rcCwzVdHosfW6BMvwbvdQgqF8EjnE,9145
|
|
11
|
+
tasktree-0.0.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
12
|
+
tasktree-0.0.2.dist-info/entry_points.txt,sha256=lQINlvRYnimvteBbnhH84A9clTg8NnpEjCWqWkqg8KE,40
|
|
13
|
+
tasktree-0.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|