Repository navigation
Description
What happened
specify bundle install cannot install any workflow from a catalog. Every workflow component fails with an error about --dev, even though --dev was never passed and the workflow resolves correctly from the catalog.
$ specify bundle install lean-full-lifecycle
Error: --dev source must be a workflow YAML file, supported archive, or
directory containing workflow.yml: lifecycle-greenfield-bootstrap
Error: Failed to install workflow 'lifecycle-greenfield-bootstrap'.
The same workflow installs fine through the CLI:
$ specify workflow add lifecycle-greenfield-bootstrap
✓ Workflow 'Greenfield Bootstrap' (lifecycle-greenfield-bootstrap) installed
Presets and extensions in the same bundle install without trouble; only workflows fail.
Cause
WorkflowPrimitive.install in src/specify_cli/bundler/services/primitives.py calls the Typer command as a plain Python function:
from ... import workflow_add
with _chdir(self._root):
_delegate_command(
"install", f"workflow '{component.id}'",
lambda: workflow_add(component.id),
)workflow_add is declared in src/specify_cli/workflows/_commands.py as:
def workflow_add(
source: str = typer.Argument(..., help="Workflow ID, URL, or local path"),
dev: bool = typer.Option(False, "--dev", help="Install from a local workflow YAML file or directory"),
from_url: str | None = typer.Option(None, "--from", help="Install from a custom URL"),
):Called directly, Typer never binds the parameters, so dev keeps its typer.OptionInfo default. That object is truthy, so the function takes the local-path branch and interprets the workflow id as a filesystem path.
Reproducible in a Python shell:
>>> import inspect
>>> from specify_cli.workflows._commands import workflow_add
>>> d = inspect.signature(workflow_add).parameters["dev"].default
>>> type(d).__name__, bool(d)
('OptionInfo', True)workflow_remove is unaffected: it declares only a typer.Argument, which the bundler supplies positionally.
Version
Reproduced on v1.0.1 (macOS, Python 3.13). The same code is present on main at the time of writing.
Suggested fix
Pass the options explicitly at both call sites in primitives.py:
lambda: workflow_add(component.id, dev=False, from_url=None),Extracting the body into a plain function that the Typer command wraps would prevent the whole class of bug, since any future option added to the command would otherwise reintroduce it silently.
Impact
Any bundle that provides workflows cannot be installed as a bundle. The workaround is to install workflows through specify workflow add <id> before specify bundle install, which lets Typer bind dev=False. They are still catalog-sourced, but bundle install then reports them as "already present" and does not attribute them to the bundle, so specify bundle remove leaves them installed.
Activity
docs: track roadmap execution on GitHub
fix(bundler): pass explicit workflow_add options from bundle install (g…
docs: track roadmap execution on GitHub