Skip to content

Commit 3431feb

Browse files
committed
fix: resolve workflow_dir to absolute path and add installed-by-ID test (#3469)
Applied fixes from bot review comments: - Comment #3563382853: resolve source_path before taking parent to ensure absolute paths - Comment #3563382864: add test for installed-by-ID workflow_dir semantics Assisted-By: 🤖 Claude Code
1 parent ae0c02f commit 3431feb

2 files changed

Lines changed: 70 additions & 6 deletions

File tree

src/specify_cli/workflows/engine.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ def execute(
701701
resolved_inputs = self._resolve_inputs(definition, inputs or {})
702702
state.inputs = resolved_inputs
703703
workflow_dir = (
704-
str(definition.source_path.parent)
704+
str(definition.source_path.resolve().parent)
705705
if definition.source_path is not None
706706
else None
707707
)

tests/test_workflows.py

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4231,7 +4231,7 @@ def test_from_yaml_sets_workflow_dir(self, project_dir):
42314231
state = engine.execute(definition)
42324232

42334233
stdout = state.step_results["check-dir"]["output"]["stdout"]
4234-
assert stdout.strip() == f"DIR={wf_dir}"
4234+
assert stdout.strip() == f"DIR={wf_dir.resolve()}"
42354235

42364236
def test_from_string_has_empty_workflow_dir(self, project_dir):
42374237
"""String-loaded workflows have empty workflow_dir."""
@@ -4279,7 +4279,7 @@ def test_shell_step_receives_speckit_workflow_dir_env_var(self, project_dir):
42794279
state = engine.execute(definition)
42804280

42814281
stdout = state.step_results["print-env"]["output"]["stdout"]
4282-
assert stdout.strip() == str(wf_dir)
4282+
assert stdout.strip() == str(wf_dir.resolve())
42834283

42844284
def test_shell_step_no_env_var_when_workflow_dir_unset(self, project_dir, monkeypatch):
42854285
"""Shell steps do not set SPECKIT_WORKFLOW_DIR for string-loaded workflows."""
@@ -4336,7 +4336,7 @@ def test_resume_preserves_original_workflow_dir(self, project_dir):
43364336
# Execute -- gate pauses the workflow
43374337
state = engine.execute(definition)
43384338
assert state.status == RunStatus.PAUSED
4339-
assert state.workflow_dir == str(wf_dir)
4339+
assert state.workflow_dir == str(wf_dir.resolve())
43404340

43414341
# Simulate gate approval by patching the gate step
43424342
from unittest.mock import patch
@@ -4350,7 +4350,7 @@ def test_resume_preserves_original_workflow_dir(self, project_dir):
43504350

43514351
assert state.status == RunStatus.COMPLETED
43524352
stdout = state.step_results["after-gate"]["output"]["stdout"]
4353-
assert stdout.strip() == f"DIR={wf_dir}"
4353+
assert stdout.strip() == f"DIR={wf_dir.resolve()}"
43544354

43554355
def test_workflow_dir_persisted_in_state(self, project_dir):
43564356
"""workflow_dir is persisted in state.json and survives load/save."""
@@ -4376,7 +4376,71 @@ def test_workflow_dir_persisted_in_state(self, project_dir):
43764376

43774377
# Reload state from disk and verify workflow_dir survived
43784378
loaded = RunState.load(state.run_id, project_dir)
4379-
assert loaded.workflow_dir == str(wf_dir)
4379+
assert loaded.workflow_dir == str(wf_dir.resolve())
4380+
4381+
def test_installed_workflow_has_workflow_dir(self, project_dir):
4382+
"""Installed-by-ID workflows get workflow_dir pointing to the
4383+
installation directory (.specify/workflows/<id>/).
4384+
"""
4385+
from specify_cli.workflows.engine import WorkflowEngine
4386+
from specify_cli.workflows.base import RunStatus
4387+
4388+
wf_id = "installed-wfdir"
4389+
install_dir = project_dir / ".specify" / "workflows" / wf_id
4390+
install_dir.mkdir(parents=True)
4391+
(install_dir / "workflow.yml").write_text("""
4392+
schema_version: "1.0"
4393+
workflow:
4394+
id: "installed-wfdir"
4395+
name: "Installed WfDir"
4396+
version: "1.0.0"
4397+
steps:
4398+
- id: check-dir
4399+
type: shell
4400+
run: "echo DIR={{ context.workflow_dir }}"
4401+
""")
4402+
engine = WorkflowEngine(project_dir)
4403+
definition = engine.load_workflow(wf_id)
4404+
state = engine.execute(definition)
4405+
4406+
assert state.status == RunStatus.COMPLETED
4407+
stdout = state.step_results["check-dir"]["output"]["stdout"]
4408+
assert stdout.strip() == f"DIR={install_dir.resolve()}"
4409+
4410+
def test_workflow_dir_is_resolved_to_absolute(self, project_dir):
4411+
"""workflow_dir is resolved to an absolute path even when the
4412+
source path is relative.
4413+
"""
4414+
from specify_cli.workflows.engine import WorkflowDefinition, WorkflowEngine
4415+
import os
4416+
4417+
wf_dir = project_dir / "rel-test"
4418+
wf_dir.mkdir()
4419+
wf_file = wf_dir / "workflow.yml"
4420+
wf_file.write_text("""
4421+
schema_version: "1.0"
4422+
workflow:
4423+
id: "rel-path"
4424+
name: "Relative Path"
4425+
version: "1.0.0"
4426+
steps:
4427+
- id: check
4428+
type: shell
4429+
run: "echo ok"
4430+
""")
4431+
# Load via a relative path
4432+
saved_cwd = os.getcwd()
4433+
try:
4434+
os.chdir(project_dir)
4435+
rel_path = Path("rel-test/workflow.yml")
4436+
definition = WorkflowDefinition.from_yaml(rel_path)
4437+
engine = WorkflowEngine(project_dir)
4438+
state = engine.execute(definition)
4439+
finally:
4440+
os.chdir(saved_cwd)
4441+
4442+
assert Path(state.workflow_dir).is_absolute()
4443+
assert state.workflow_dir == str(wf_dir.resolve())
43804444

43814445

43824446
# ===== continue_on_error Tests =====

0 commit comments

Comments
 (0)