flowsh-cli 0.7.2__tar.gz → 0.8.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flowsh-cli
3
- Version: 0.7.2
3
+ Version: 0.8.1
4
4
  Summary: Generate Bash harness scripts from workflow YAML files.
5
5
  License-Expression: MIT
6
6
  Classifier: Programming Language :: Python :: 3.11
@@ -26,7 +26,7 @@ Use it when you want a small, reproducible way to encode a workflow once and rer
26
26
  uvx flowsh-cli path/to/workflows.yml
27
27
  ```
28
28
 
29
- That reads the workflow YAML at the path you provide and writes harnesses under `.harness/`.
29
+ That reads the workflow YAML at the path you provide and writes harness scripts to the current working directory.
30
30
 
31
31
  In this repository, the example workflow file lives at `.made/workflows.yml`.
32
32
 
@@ -82,7 +82,7 @@ workflows:
82
82
  run: echo "$ITEM"
83
83
  ```
84
84
 
85
- Harness paths are derived from workflow ids. `wf_example` becomes `.harness/example.sh`.
85
+ Harness paths are derived from workflow ids. `wf_example` becomes `example.sh` in the current working directory.
86
86
 
87
87
  ## Step Types
88
88
 
@@ -10,7 +10,7 @@ Use it when you want a small, reproducible way to encode a workflow once and rer
10
10
  uvx flowsh-cli path/to/workflows.yml
11
11
  ```
12
12
 
13
- That reads the workflow YAML at the path you provide and writes harnesses under `.harness/`.
13
+ That reads the workflow YAML at the path you provide and writes harness scripts to the current working directory.
14
14
 
15
15
  In this repository, the example workflow file lives at `.made/workflows.yml`.
16
16
 
@@ -66,7 +66,7 @@ workflows:
66
66
  run: echo "$ITEM"
67
67
  ```
68
68
 
69
- Harness paths are derived from workflow ids. `wf_example` becomes `.harness/example.sh`.
69
+ Harness paths are derived from workflow ids. `wf_example` becomes `example.sh` in the current working directory.
70
70
 
71
71
  ## Step Types
72
72
 
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "flowsh-cli"
7
- version = "0.7.2"
7
+ version = "0.8.1"
8
8
  description = "Generate Bash harness scripts from workflow YAML files."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -42,3 +42,6 @@ target-version = "py311"
42
42
 
43
43
  [tool.ruff.lint]
44
44
  select = ["E", "F", "I", "UP", "B", "SIM"]
45
+
46
+ [tool.ruff.lint.per-file-ignores]
47
+ "tests/test_workflow_to_harness.py" = ["E501"]
@@ -3,7 +3,7 @@
3
3
  from flowsh_cli.models import Workflow, WorkflowParseError, parse_workflows
4
4
  from flowsh_cli.render import harness_path, render_harness
5
5
 
6
- __version__ = "0.7.2"
6
+ __version__ = "0.8.1"
7
7
 
8
8
  __all__ = [
9
9
  "Workflow",
@@ -20,7 +20,6 @@ app = typer.Typer(
20
20
  context_settings={"terminal_width": 120, "max_content_width": 120},
21
21
  help="Generate reproducible OpenCode Bash harness scripts from MADE workflow YAML.",
22
22
  pretty_exceptions_enable=False,
23
- rich_markup_mode=None,
24
23
  )
25
24
 
26
25
 
@@ -91,6 +90,14 @@ def generate(
91
90
  is_eager=True,
92
91
  ),
93
92
  ] = None,
93
+ output: Annotated[
94
+ Path | None,
95
+ typer.Option(
96
+ "--output",
97
+ metavar="PATH",
98
+ help="Write generated script to PATH. Only valid when generating a single workflow.",
99
+ ),
100
+ ] = None,
94
101
  ) -> None:
95
102
  """Generate Bash harnesses from workflow YAML."""
96
103
 
@@ -102,7 +109,14 @@ def generate(
102
109
  try:
103
110
  workflows = parse_workflows(workflow_yaml)
104
111
  selected = select_workflows(workflows, workflow)
105
- write_harnesses(selected, dry_run=dry_run, force=force)
112
+ if output is not None and len(selected) != 1:
113
+ print(
114
+ "ERROR: --output requires exactly one workflow "
115
+ "(use --workflow or ensure the file contains only one workflow)",
116
+ file=sys.stderr,
117
+ )
118
+ raise typer.Exit(1)
119
+ write_harnesses(selected, dry_run=dry_run, force=force, output_path=output)
106
120
  except WorkflowParseError as error:
107
121
  print(f"ERROR: {error}", file=sys.stderr)
108
122
  raise typer.Exit(1) from error
@@ -159,8 +173,13 @@ def print_example(value: str | None) -> None:
159
173
  raise typer.Exit
160
174
 
161
175
 
162
- def write_harnesses(workflows: list[Workflow], *, dry_run: bool, force: bool) -> None:
163
- output_paths = [(workflow, harness_path(workflow)) for workflow in workflows]
176
+ def write_harnesses(
177
+ workflows: list[Workflow], *, dry_run: bool, force: bool, output_path: Path | None = None
178
+ ) -> None:
179
+ output_paths = [
180
+ (workflow, output_path if output_path is not None else harness_path(workflow))
181
+ for workflow in workflows
182
+ ]
164
183
 
165
184
  if dry_run:
166
185
  for workflow, output_path in output_paths:
@@ -6,9 +6,12 @@ EXAMPLES: dict[str, tuple[str, str, str]] = {
6
6
  "Vars + sequential bash steps",
7
7
  "vars, bash",
8
8
  """\
9
+ description: Simple workflow examples for demonstration purposes
10
+
9
11
  workflows:
10
12
  - id: wf_simple
11
13
  name: Simple example — vars and bash
14
+ description: Captures today's date and prints a greeting via sequential bash steps.
12
15
 
13
16
  steps:
14
17
  - type: vars
@@ -199,7 +199,11 @@ class WorkflowParam(StrictModel):
199
199
  class Workflow(StrictModel):
200
200
  id: str
201
201
  name: str
202
+ description: str | None = None
202
203
  params: list[WorkflowParam] = []
204
+ enabled: bool = True
205
+ schedule: str | None = None
206
+ shellScriptPath: str | None = None
203
207
  steps: list[Step]
204
208
 
205
209
  @field_validator("id")
@@ -227,6 +231,7 @@ class Workflow(StrictModel):
227
231
 
228
232
 
229
233
  class WorkflowFile(StrictModel):
234
+ description: str | None = None
230
235
  workflows: list[Workflow]
231
236
 
232
237
  @field_validator("workflows")
@@ -16,7 +16,7 @@ from flowsh_cli.models import (
16
16
 
17
17
 
18
18
  def harness_path(workflow: Workflow) -> Path:
19
- return Path(".harness") / f"{workflow.id.removeprefix('wf_')}.sh"
19
+ return Path(f"{workflow.id.removeprefix('wf_')}.sh")
20
20
 
21
21
 
22
22
  def render_harness(workflow: Workflow) -> str:
@@ -326,8 +326,9 @@ def _render_step_body(step: Step) -> list[str]:
326
326
  ]
327
327
  )
328
328
  if step.expandPrompt:
329
- braced = re.findall(r"\$\{([A-Z_][A-Z0-9_]*)\}", step.prompt)
330
- bare = re.findall(r"\$([A-Z_][A-Z0-9_]*)(?!\w)", step.prompt)
329
+ _prompt_no_code = re.sub(r"```.*?```", "", step.prompt, flags=re.DOTALL)
330
+ braced = re.findall(r"\$\{([A-Z_][A-Z0-9_]*)\}", _prompt_no_code)
331
+ bare = re.findall(r"\$([A-Z_][A-Z0-9_]*)(?!\w)", _prompt_no_code)
331
332
  seen: dict[str, None] = {}
332
333
  for var in braced + bare:
333
334
  seen[var] = None