flowsh-cli 0.4.2__tar.gz → 0.5.0__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.
- {flowsh_cli-0.4.2 → flowsh_cli-0.5.0}/PKG-INFO +1 -1
- {flowsh_cli-0.4.2 → flowsh_cli-0.5.0}/pyproject.toml +1 -1
- {flowsh_cli-0.4.2 → flowsh_cli-0.5.0}/src/flowsh_cli/__init__.py +1 -1
- {flowsh_cli-0.4.2 → flowsh_cli-0.5.0}/src/flowsh_cli/models.py +14 -0
- {flowsh_cli-0.4.2 → flowsh_cli-0.5.0}/src/flowsh_cli/render.py +65 -8
- {flowsh_cli-0.4.2 → flowsh_cli-0.5.0}/README.md +0 -0
- {flowsh_cli-0.4.2 → flowsh_cli-0.5.0}/src/flowsh_cli/__main__.py +0 -0
- {flowsh_cli-0.4.2 → flowsh_cli-0.5.0}/src/flowsh_cli/cli.py +0 -0
|
@@ -157,9 +157,23 @@ class VarsStep(BaseStep):
|
|
|
157
157
|
Step = Annotated[VarsStep | BashStep | AgentStep, Field(discriminator="type")]
|
|
158
158
|
|
|
159
159
|
|
|
160
|
+
class WorkflowParam(StrictModel):
|
|
161
|
+
name: str
|
|
162
|
+
description: str | None = None
|
|
163
|
+
required: bool = False
|
|
164
|
+
|
|
165
|
+
@field_validator("name")
|
|
166
|
+
@classmethod
|
|
167
|
+
def validate_name(cls, value: str) -> str:
|
|
168
|
+
if not re.fullmatch(r"[A-Z_][A-Z0-9_]*", value):
|
|
169
|
+
raise ValueError("must match ^[A-Z_][A-Z0-9_]*$")
|
|
170
|
+
return value
|
|
171
|
+
|
|
172
|
+
|
|
160
173
|
class Workflow(StrictModel):
|
|
161
174
|
id: str
|
|
162
175
|
name: str
|
|
176
|
+
params: list[WorkflowParam] = []
|
|
163
177
|
steps: list[Step]
|
|
164
178
|
|
|
165
179
|
@field_validator("id")
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import re
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
-
from flowsh_cli.models import AgentStep, BashStep, Step, VarsStep, Workflow
|
|
6
|
+
from flowsh_cli.models import AgentStep, BashStep, Step, VarsStep, Workflow, WorkflowParam
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def harness_path(workflow: Workflow) -> Path:
|
|
@@ -26,13 +26,7 @@ def render_harness(workflow: Workflow) -> str:
|
|
|
26
26
|
'LOG_BASENAME="flowsh-${WORKFLOW_SLUG}-${LOG_TIMESTAMP}-$$.log"',
|
|
27
27
|
"",
|
|
28
28
|
section("Argument handling"),
|
|
29
|
-
|
|
30
|
-
'if [[ $# -eq 1 && "$1" == "--dry-run" ]]; then',
|
|
31
|
-
" DRY_RUN=true",
|
|
32
|
-
"elif [[ $# -gt 0 ]]; then",
|
|
33
|
-
' printf "Usage: %s [--dry-run]\\n" "$0" >&2',
|
|
34
|
-
" exit 2",
|
|
35
|
-
"fi",
|
|
29
|
+
*_render_arg_block(workflow.params),
|
|
36
30
|
"",
|
|
37
31
|
section("refuse_symlink_path() - keep generated logs inside plain relative paths"),
|
|
38
32
|
"refuse_symlink_path() {",
|
|
@@ -353,6 +347,69 @@ def bash_quote(value: str) -> str:
|
|
|
353
347
|
return "'" + value.replace("'", "'\\''") + "'"
|
|
354
348
|
|
|
355
349
|
|
|
350
|
+
def _render_arg_block(params: list[WorkflowParam]) -> list[str]:
|
|
351
|
+
if not params:
|
|
352
|
+
return [
|
|
353
|
+
"DRY_RUN=false",
|
|
354
|
+
'if [[ $# -eq 1 && "$1" == "--dry-run" ]]; then',
|
|
355
|
+
" DRY_RUN=true",
|
|
356
|
+
"elif [[ $# -gt 0 ]]; then",
|
|
357
|
+
' printf "Usage: %s [--dry-run]\\n" "$0" >&2',
|
|
358
|
+
" exit 2",
|
|
359
|
+
"fi",
|
|
360
|
+
]
|
|
361
|
+
|
|
362
|
+
usage_parts: list[str] = ["[--dry-run]"]
|
|
363
|
+
for p in params:
|
|
364
|
+
usage_parts.append(f"<{p.name}>" if p.required else f"[{p.name}]")
|
|
365
|
+
usage_str = " ".join(usage_parts)
|
|
366
|
+
|
|
367
|
+
lines: list[str] = [
|
|
368
|
+
"DRY_RUN=false",
|
|
369
|
+
"POSITIONAL_ARGS=()",
|
|
370
|
+
"",
|
|
371
|
+
"while [[ $# -gt 0 ]]; do",
|
|
372
|
+
' case "$1" in',
|
|
373
|
+
" --dry-run)",
|
|
374
|
+
" DRY_RUN=true",
|
|
375
|
+
" shift",
|
|
376
|
+
" ;;",
|
|
377
|
+
" --*)",
|
|
378
|
+
' printf "Unknown option: %s\\n" "$1" >&2',
|
|
379
|
+
" exit 2",
|
|
380
|
+
" ;;",
|
|
381
|
+
" *)",
|
|
382
|
+
' POSITIONAL_ARGS+=("$1")',
|
|
383
|
+
" shift",
|
|
384
|
+
" ;;",
|
|
385
|
+
" esac",
|
|
386
|
+
"done",
|
|
387
|
+
"",
|
|
388
|
+
]
|
|
389
|
+
|
|
390
|
+
for idx, param in enumerate(params):
|
|
391
|
+
lines += [
|
|
392
|
+
f"if [[ ${{#POSITIONAL_ARGS[@]}} -gt {idx} ]]; then",
|
|
393
|
+
f' {param.name}="${{POSITIONAL_ARGS[{idx}]}}"',
|
|
394
|
+
f" export {param.name}",
|
|
395
|
+
"fi",
|
|
396
|
+
]
|
|
397
|
+
|
|
398
|
+
lines.append("")
|
|
399
|
+
|
|
400
|
+
required = [p for p in params if p.required]
|
|
401
|
+
if required:
|
|
402
|
+
for param in required:
|
|
403
|
+
lines += [
|
|
404
|
+
f'if [[ -z "${{{param.name}:-}}" ]]; then',
|
|
405
|
+
f' printf "Usage: %s {usage_str}\\n" "$0" >&2',
|
|
406
|
+
" exit 2",
|
|
407
|
+
"fi",
|
|
408
|
+
]
|
|
409
|
+
|
|
410
|
+
return lines
|
|
411
|
+
|
|
412
|
+
|
|
356
413
|
def section(title: str) -> str:
|
|
357
414
|
safe_title = truncate_one_line(title, limit=120)
|
|
358
415
|
return "\n".join(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|