Skip to content

Commit 265e131

Browse files
jawwad-aliclaude
andcommitted
fix(bundler): pass explicit option values when delegating to workflow_add
`specify bundle install` can never install a workflow component. It fails 100% of the time with a nonsensical error about `--dev`. The bundler delegates to the Typer command callables in-process: lambda: workflow_add(component.id) `workflow_add` declares two `typer.Option` parameters. Called from Python rather than through Typer, those keep their `OptionInfo` sentinels as the value — and the sentinel is truthy and is not None: dev default -> OptionInfo truthy=True from_url default -> OptionInfo is None=False So `if dev:` takes the local-path branch for every catalog install: Error: --dev source must be a workflow YAML file, supported archive, or directory containing workflow.yml: code-review BundlerError: Failed to install workflow 'code-review'. `workflow_add` is the only one of the four delegated commands that declares options; workflow_remove / workflow_step_add / workflow_step_remove take a bare `typer.Argument` and are safe as written. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent c173bf1 commit 265e131

2 files changed

Lines changed: 43 additions & 0 deletions

File tree

‎src/specify_cli/bundler/services/primitives.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,15 @@ def install(self, component: ComponentRef) -> None:
347347
with _chdir(self._root):
348348
_delegate_command(
349349
"install", f"workflow '{component.id}'",
350+
# Pass the options explicitly. Called in-process rather than
351+
# through Typer, an omitted ``typer.Option`` parameter keeps its
352+
# ``OptionInfo`` sentinel as the value -- which is TRUTHY and is
353+
# not ``None`` -- so ``workflow_add``'s ``if dev:`` took the
354+
# local-path branch for every catalog install and failed with
355+
# "--dev source must be a workflow YAML file ...". It is the only
356+
# one of the four delegated commands that declares options;
357+
# workflow_remove / workflow_step_add / workflow_step_remove take
358+
# a bare ``typer.Argument`` and are safe as written.
350359
lambda: workflow_add(component.id, dev=False, from_url=None),
351360
)
352361

‎tests/unit/test_bundler_primitives.py‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,40 @@ def test_offline_workflow_allows_bundled(tmp_path: Path, monkeypatch):
9191
assert calls == [("bundled-wf", False, None)]
9292

9393

94+
def test_workflow_install_passes_explicit_typer_options(tmp_path: Path, monkeypatch):
95+
"""The bundler calls ``workflow_add`` in-process, so it must pass the
96+
``typer.Option`` values explicitly.
97+
98+
Outside Typer, an omitted option parameter keeps its ``OptionInfo``
99+
sentinel as the value. That sentinel is truthy and is not ``None``, so
100+
``workflow_add``'s ``if dev:`` took the local-path branch for *every*
101+
catalog install and failed with "--dev source must be a workflow YAML
102+
file, supported archive, or directory containing workflow.yml".
103+
"""
104+
import specify_cli
105+
import specify_cli._assets as assets
106+
107+
monkeypatch.setattr(
108+
assets, "_locate_bundled_workflow", lambda wid: tmp_path / "wf"
109+
)
110+
seen: list[dict] = []
111+
112+
def _capture(wid, *args, **kwargs):
113+
seen.append({"id": wid, "args": args, "kwargs": kwargs})
114+
115+
monkeypatch.setattr(specify_cli, "workflow_add", _capture)
116+
117+
manager = primitive_manager("workflows", tmp_path, allow_network=False)
118+
manager.install(_component("workflows", "bundled-wf"))
119+
120+
assert len(seen) == 1, seen
121+
call = seen[0]
122+
assert call["id"] == "bundled-wf"
123+
# Both options must arrive as real values, never as Typer sentinels.
124+
assert call["kwargs"].get("dev") is False, call["kwargs"]
125+
assert call["kwargs"].get("from_url") is None, call["kwargs"]
126+
127+
94128
def test_assert_pinned_version_matches_passes():
95129
from specify_cli.bundler.services.primitives import _assert_pinned_version
96130

0 commit comments

Comments
 (0)