tasktree 0.0.9__py3-none-any.whl → 0.0.11__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 +5 -3
- tasktree/docker.py +17 -9
- tasktree/hasher.py +8 -1
- tasktree/parser.py +40 -30
- {tasktree-0.0.9.dist-info → tasktree-0.0.11.dist-info}/METADATA +4 -2
- tasktree-0.0.11.dist-info/RECORD +15 -0
- tasktree-0.0.9.dist-info/RECORD +0 -15
- {tasktree-0.0.9.dist-info → tasktree-0.0.11.dist-info}/WHEEL +0 -0
- {tasktree-0.0.9.dist-info → tasktree-0.0.11.dist-info}/entry_points.txt +0 -0
tasktree/cli.py
CHANGED
|
@@ -192,10 +192,12 @@ tasks:
|
|
|
192
192
|
# deploy:
|
|
193
193
|
# desc: Deploy to environment
|
|
194
194
|
# deps: [build]
|
|
195
|
-
# args:
|
|
195
|
+
# args:
|
|
196
|
+
# - environment
|
|
197
|
+
# - region: { default: eu-west-1 }
|
|
196
198
|
# cmd: |
|
|
197
|
-
# echo "Deploying to {{environment}} in {{region}}"
|
|
198
|
-
# ./deploy.sh {{environment}} {{region}}
|
|
199
|
+
# echo "Deploying to {{ arg.environment }} in {{ arg.region }}"
|
|
200
|
+
# ./deploy.sh {{ arg.environment }} {{ arg.region }}
|
|
199
201
|
|
|
200
202
|
# Uncomment and modify the examples above to define your tasks
|
|
201
203
|
"""
|
tasktree/docker.py
CHANGED
|
@@ -87,16 +87,24 @@ class DockerManager:
|
|
|
87
87
|
|
|
88
88
|
# Build the image
|
|
89
89
|
try:
|
|
90
|
+
docker_build_cmd = [
|
|
91
|
+
"docker",
|
|
92
|
+
"build",
|
|
93
|
+
"-t",
|
|
94
|
+
image_tag,
|
|
95
|
+
"-f",
|
|
96
|
+
str(dockerfile_path),
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
# Add build args if environment has them (docker environments use dict for args)
|
|
100
|
+
if isinstance(env.args, dict):
|
|
101
|
+
for arg_name, arg_value in env.args.items():
|
|
102
|
+
docker_build_cmd.extend(["--build-arg", f"{arg_name}={arg_value}"])
|
|
103
|
+
|
|
104
|
+
docker_build_cmd.append(str(context_path))
|
|
105
|
+
|
|
90
106
|
subprocess.run(
|
|
91
|
-
|
|
92
|
-
"docker",
|
|
93
|
-
"build",
|
|
94
|
-
"-t",
|
|
95
|
-
image_tag,
|
|
96
|
-
"-f",
|
|
97
|
-
str(dockerfile_path),
|
|
98
|
-
str(context_path),
|
|
99
|
-
],
|
|
107
|
+
docker_build_cmd,
|
|
100
108
|
check=True,
|
|
101
109
|
capture_output=False, # Show build output to user
|
|
102
110
|
)
|
tasktree/hasher.py
CHANGED
|
@@ -104,9 +104,16 @@ def hash_environment_definition(env) -> str:
|
|
|
104
104
|
# Import inside function to avoid circular dependency
|
|
105
105
|
from tasktree.parser import Environment
|
|
106
106
|
|
|
107
|
+
# Handle args - can be list (shell args) or dict (docker build args)
|
|
108
|
+
args_value = env.args
|
|
109
|
+
if isinstance(env.args, dict):
|
|
110
|
+
args_value = dict(sorted(env.args.items())) # Sort dict for determinism
|
|
111
|
+
elif isinstance(env.args, list):
|
|
112
|
+
args_value = sorted(env.args) # Sort list for determinism
|
|
113
|
+
|
|
107
114
|
data = {
|
|
108
115
|
"shell": env.shell,
|
|
109
|
-
"args":
|
|
116
|
+
"args": args_value,
|
|
110
117
|
"preamble": env.preamble,
|
|
111
118
|
"dockerfile": env.dockerfile,
|
|
112
119
|
"context": env.context,
|
tasktree/parser.py
CHANGED
|
@@ -31,7 +31,7 @@ class Environment:
|
|
|
31
31
|
|
|
32
32
|
name: str
|
|
33
33
|
shell: str = "" # Path to shell (required for shell envs, optional for Docker)
|
|
34
|
-
args: list[str] = field(default_factory=list)
|
|
34
|
+
args: list[str] | dict[str, str] = field(default_factory=list) # Shell args (list) or Docker build args (dict)
|
|
35
35
|
preamble: str = ""
|
|
36
36
|
# Docker-specific fields (presence of dockerfile indicates Docker environment)
|
|
37
37
|
dockerfile: str = "" # Path to Dockerfile
|
|
@@ -44,7 +44,7 @@ class Environment:
|
|
|
44
44
|
run_as_root: bool = False # If True, skip user mapping (run as root in container)
|
|
45
45
|
|
|
46
46
|
def __post_init__(self):
|
|
47
|
-
"""Ensure args is
|
|
47
|
+
"""Ensure args is in the correct format."""
|
|
48
48
|
if isinstance(self.args, str):
|
|
49
49
|
self.args = [self.args]
|
|
50
50
|
|
|
@@ -60,7 +60,7 @@ class Task:
|
|
|
60
60
|
inputs: list[str] = field(default_factory=list)
|
|
61
61
|
outputs: list[str] = field(default_factory=list)
|
|
62
62
|
working_dir: str = ""
|
|
63
|
-
args: list[str] = field(default_factory=list)
|
|
63
|
+
args: list[str | dict[str, Any]] = field(default_factory=list) # Can be strings or dicts (each dict has single key: arg name)
|
|
64
64
|
source_file: str = "" # Track which file defined this task
|
|
65
65
|
env: str = "" # Environment name to use for execution
|
|
66
66
|
|
|
@@ -75,6 +75,19 @@ class Task:
|
|
|
75
75
|
if isinstance(self.args, str):
|
|
76
76
|
self.args = [self.args]
|
|
77
77
|
|
|
78
|
+
# Validate args is not a dict (common YAML mistake)
|
|
79
|
+
if isinstance(self.args, dict):
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"Task '{self.name}' has invalid 'args' syntax.\n\n"
|
|
82
|
+
f"Found dictionary syntax (without dashes):\n"
|
|
83
|
+
f" args:\n"
|
|
84
|
+
f" {list(self.args.keys())[0] if self.args else 'key'}: ...\n\n"
|
|
85
|
+
f"Correct syntax uses list format (with dashes):\n"
|
|
86
|
+
f" args:\n"
|
|
87
|
+
f" - {list(self.args.keys())[0] if self.args else 'key'}: ...\n\n"
|
|
88
|
+
f"Arguments must be defined as a list, not a dictionary."
|
|
89
|
+
)
|
|
90
|
+
|
|
78
91
|
|
|
79
92
|
@dataclass
|
|
80
93
|
class DependencyInvocation:
|
|
@@ -1246,18 +1259,16 @@ def parse_arg_spec(arg_spec: str | dict) -> ArgSpec:
|
|
|
1246
1259
|
|
|
1247
1260
|
Supports both string format and dictionary format:
|
|
1248
1261
|
|
|
1249
|
-
String format:
|
|
1262
|
+
String format (simple names only):
|
|
1250
1263
|
- Simple name: "argname"
|
|
1251
1264
|
- Exported (becomes env var): "$argname"
|
|
1252
|
-
- With default: "argname=value" or "$argname=value"
|
|
1253
|
-
- Legacy type syntax: "argname:type=value" (for backwards compat)
|
|
1254
1265
|
|
|
1255
1266
|
Dictionary format:
|
|
1256
1267
|
- argname: { default: "value" }
|
|
1257
1268
|
- argname: { type: int, default: 42 }
|
|
1258
1269
|
- argname: { type: int, min: 1, max: 100 }
|
|
1259
1270
|
- argname: { type: str, choices: ["dev", "staging", "prod"] }
|
|
1260
|
-
- $argname: { default: "value" } # Exported
|
|
1271
|
+
- $argname: { default: "value" } # Exported (type not allowed)
|
|
1261
1272
|
|
|
1262
1273
|
Args:
|
|
1263
1274
|
arg_spec: Argument specification (string or dict with single key)
|
|
@@ -1315,34 +1326,24 @@ def parse_arg_spec(arg_spec: str | dict) -> ArgSpec:
|
|
|
1315
1326
|
if is_exported:
|
|
1316
1327
|
arg_spec = arg_spec[1:] # Remove $ prefix
|
|
1317
1328
|
|
|
1318
|
-
#
|
|
1319
|
-
if "=" in arg_spec:
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
name, arg_type = name_type.split(":", 1)
|
|
1329
|
+
# String format only supports simple names (no = or :)
|
|
1330
|
+
if "=" in arg_spec or ":" in arg_spec:
|
|
1331
|
+
raise ValueError(
|
|
1332
|
+
f"Invalid argument syntax: {'$' if is_exported else ''}{arg_spec}\n\n"
|
|
1333
|
+
f"String format only supports simple argument names.\n"
|
|
1334
|
+
f"Use YAML dict format for type annotations, defaults, or constraints:\n"
|
|
1335
|
+
f" args:\n"
|
|
1336
|
+
f" - {'$' if is_exported else ''}{arg_spec.split('=')[0].split(':')[0]}: {{ default: value }}"
|
|
1337
|
+
)
|
|
1328
1338
|
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
raise ValueError(
|
|
1332
|
-
f"Type annotations not allowed on exported arguments\n"
|
|
1333
|
-
f"In argument: ${name}:{arg_type}\n\n"
|
|
1334
|
-
f"Exported arguments are always strings. Remove the type annotation:\n"
|
|
1335
|
-
f" args: [${name}]"
|
|
1336
|
-
)
|
|
1337
|
-
else:
|
|
1338
|
-
name = name_type
|
|
1339
|
-
arg_type = "str"
|
|
1339
|
+
name = arg_spec
|
|
1340
|
+
arg_type = "str"
|
|
1340
1341
|
|
|
1341
|
-
# String format doesn't support min/max/choices
|
|
1342
|
+
# String format doesn't support min/max/choices/defaults
|
|
1342
1343
|
return ArgSpec(
|
|
1343
1344
|
name=name,
|
|
1344
1345
|
arg_type=arg_type,
|
|
1345
|
-
default=
|
|
1346
|
+
default=None,
|
|
1346
1347
|
is_exported=is_exported,
|
|
1347
1348
|
min_val=None,
|
|
1348
1349
|
max_val=None,
|
|
@@ -1390,6 +1391,15 @@ def _parse_arg_dict(arg_name: str, config: dict, is_exported: bool) -> ArgSpec:
|
|
|
1390
1391
|
f"Exported arguments are always strings. Remove the 'type' field"
|
|
1391
1392
|
)
|
|
1392
1393
|
|
|
1394
|
+
# Exported arguments must have string defaults (if any default is provided)
|
|
1395
|
+
if is_exported and default is not None and not isinstance(default, str):
|
|
1396
|
+
raise ValueError(
|
|
1397
|
+
f"Exported argument '${arg_name}' must have a string default value.\n"
|
|
1398
|
+
f"Got: {default!r} (type: {type(default).__name__})\n"
|
|
1399
|
+
f"Exported arguments become environment variables, which are always strings.\n"
|
|
1400
|
+
f"Use a quoted string: ${arg_name}: {{ default: \"{default}\" }}"
|
|
1401
|
+
)
|
|
1402
|
+
|
|
1393
1403
|
# Validate choices
|
|
1394
1404
|
if choices is not None:
|
|
1395
1405
|
# Validate choices is a list
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tasktree
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.11
|
|
4
4
|
Summary: A task automation tool with incremental execution
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Requires-Dist: click>=8.1.0
|
|
@@ -604,7 +604,9 @@ Dependencies can invoke tasks with specific arguments, enabling flexible and reu
|
|
|
604
604
|
tasks:
|
|
605
605
|
# Task with parameters
|
|
606
606
|
process:
|
|
607
|
-
args:
|
|
607
|
+
args:
|
|
608
|
+
- mode
|
|
609
|
+
- verbose: { default: false }
|
|
608
610
|
cmd: echo "mode={{arg.mode}} verbose={{arg.verbose}}"
|
|
609
611
|
|
|
610
612
|
# Simple dependency (uses defaults)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
tasktree/__init__.py,sha256=MVmdvKb3JdqLlo0x2_TPGMfgFC0HsDnP79HAzGnFnjI,1081
|
|
2
|
+
tasktree/cli.py,sha256=uL4RGap1U7-_4mcdEGbsELR4cvm1aUaqbvnX8XJFNKc,17652
|
|
3
|
+
tasktree/docker.py,sha256=qvja8G63uAcC73YMVY739egda1_CcBtoqzm0qIJU_Q8,14443
|
|
4
|
+
tasktree/executor.py,sha256=Q7Bks5B88i-IyZDpxGSps9MM3uflz0U3yn4Rtq_uHMM,42266
|
|
5
|
+
tasktree/graph.py,sha256=oXLxX0Ix4zSkVBg8_3x9K7WxSFpg136sp4MF-d2mDEQ,9682
|
|
6
|
+
tasktree/hasher.py,sha256=0GrnCfwAXnwq_kpnHFFb12B5_2VFNXx6Ng7hTdcCyXo,4415
|
|
7
|
+
tasktree/parser.py,sha256=rHJuYMM4AUjM1E-Jh3SpUpBRKGkciYOvgfgERoXylSE,67364
|
|
8
|
+
tasktree/state.py,sha256=Cktl4D8iDZVd55aO2LqVyPrc-BnljkesxxkcMcdcfOY,3541
|
|
9
|
+
tasktree/substitution.py,sha256=M_qcP0NKJATrKcNShSqHJatneuth1RVwTk1ci8-ZuxQ,6473
|
|
10
|
+
tasktree/tasks.py,sha256=2QdQZtJAX2rSGbyXKG1z9VF_siz1DUzdvzCgPkykxtU,173
|
|
11
|
+
tasktree/types.py,sha256=R_YAyO5bMLB6XZnkMRT7VAtlkA_Xx6xu0aIpzQjrBXs,4357
|
|
12
|
+
tasktree-0.0.11.dist-info/METADATA,sha256=a41OmLVRm4BbIpZT4e7v-l3g3gmw3DD1Eg0OXnrVsYA,37151
|
|
13
|
+
tasktree-0.0.11.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
14
|
+
tasktree-0.0.11.dist-info/entry_points.txt,sha256=lQINlvRYnimvteBbnhH84A9clTg8NnpEjCWqWkqg8KE,40
|
|
15
|
+
tasktree-0.0.11.dist-info/RECORD,,
|
tasktree-0.0.9.dist-info/RECORD
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
tasktree/__init__.py,sha256=MVmdvKb3JdqLlo0x2_TPGMfgFC0HsDnP79HAzGnFnjI,1081
|
|
2
|
-
tasktree/cli.py,sha256=H5T8wOxLBGx-ZTQEnkoJrX3srgD5b_7BLf1IWl18M2M,17597
|
|
3
|
-
tasktree/docker.py,sha256=R69NcZw4MyaxEXyJAwniYCm877iaI10jRhxlLmkA6Fs,14119
|
|
4
|
-
tasktree/executor.py,sha256=Q7Bks5B88i-IyZDpxGSps9MM3uflz0U3yn4Rtq_uHMM,42266
|
|
5
|
-
tasktree/graph.py,sha256=oXLxX0Ix4zSkVBg8_3x9K7WxSFpg136sp4MF-d2mDEQ,9682
|
|
6
|
-
tasktree/hasher.py,sha256=C8oN-K6dtL3vLHSPhKR7uu5c1d4vplGSAMZUq5M4scw,4125
|
|
7
|
-
tasktree/parser.py,sha256=DjdfsKErdBggqS8Tw_mwuMvMSavIJqq2BCdsh1O82CY,66333
|
|
8
|
-
tasktree/state.py,sha256=Cktl4D8iDZVd55aO2LqVyPrc-BnljkesxxkcMcdcfOY,3541
|
|
9
|
-
tasktree/substitution.py,sha256=M_qcP0NKJATrKcNShSqHJatneuth1RVwTk1ci8-ZuxQ,6473
|
|
10
|
-
tasktree/tasks.py,sha256=2QdQZtJAX2rSGbyXKG1z9VF_siz1DUzdvzCgPkykxtU,173
|
|
11
|
-
tasktree/types.py,sha256=R_YAyO5bMLB6XZnkMRT7VAtlkA_Xx6xu0aIpzQjrBXs,4357
|
|
12
|
-
tasktree-0.0.9.dist-info/METADATA,sha256=VBgQ1ZF2hacw1CajhxcjkrGTyygnf-uWxiZm7H92AyE,37123
|
|
13
|
-
tasktree-0.0.9.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
14
|
-
tasktree-0.0.9.dist-info/entry_points.txt,sha256=lQINlvRYnimvteBbnhH84A9clTg8NnpEjCWqWkqg8KE,40
|
|
15
|
-
tasktree-0.0.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|