Skip to content

Commit f5a89df

Browse files
committed
feat(cli): add --extra-yaml to cci flow run and cci flow info
Click option accepts multiple paths and honors CUMULUSCI_EXTRA_YAML as a fallback. The resolved YAML string is passed to CliRuntime.reload_project_config before flow resolution.
1 parent 29c75aa commit f5a89df

2 files changed

Lines changed: 114 additions & 4 deletions

File tree

cumulusci/cli/flow.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import click
77

8+
from cumulusci.cli.extra_yaml import resolve_extra_yaml
89
from cumulusci.core.exceptions import FlowNotFoundError
910
from cumulusci.core.utils import format_duration
1011
from cumulusci.utils import document_flow, flow_ref_title_and_intro
@@ -106,8 +107,21 @@ def flow_list(runtime, plain, print_json):
106107

107108
@flow.command(name="info", help="Displays information for a flow")
108109
@click.argument("flow_name")
110+
@click.option(
111+
"--extra-yaml",
112+
"extra_yaml",
113+
multiple=True,
114+
type=click.Path(),
115+
help=(
116+
"Path to an additional YAML file to merge into the project config "
117+
"for this command only. Can be specified multiple times; later files "
118+
"override earlier ones. Also honors CUMULUSCI_EXTRA_YAML env var "
119+
"(colon-separated paths) as a fallback."
120+
),
121+
)
109122
@pass_runtime(require_keychain=True)
110-
def flow_info(runtime, flow_name):
123+
def flow_info(runtime, flow_name, extra_yaml):
124+
runtime.reload_project_config(additional_yaml=resolve_extra_yaml(extra_yaml))
111125
try:
112126
coordinator = runtime.get_flow(flow_name)
113127
output = coordinator.get_summary(verbose=True)
@@ -141,8 +155,21 @@ def flow_info(runtime, flow_name):
141155
is_flag=True,
142156
help="Disables all prompts. Set for non-interactive mode use such as calling from scripts or CI systems",
143157
)
158+
@click.option(
159+
"--extra-yaml",
160+
"extra_yaml",
161+
multiple=True,
162+
type=click.Path(),
163+
help=(
164+
"Path to an additional YAML file to merge into the project config "
165+
"for this command only. Can be specified multiple times; later files "
166+
"override earlier ones. Also honors CUMULUSCI_EXTRA_YAML env var "
167+
"(colon-separated paths) as a fallback."
168+
),
169+
)
144170
@pass_runtime(require_keychain=True)
145-
def flow_run(runtime, flow_name, org, delete_org, debug, o, no_prompt):
171+
def flow_run(runtime, flow_name, org, delete_org, debug, o, no_prompt, extra_yaml):
172+
runtime.reload_project_config(additional_yaml=resolve_extra_yaml(extra_yaml))
146173

147174
# Get necessary configs
148175
org, org_config = runtime.get_org(org)

cumulusci/cli/tests/test_flow.py

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def test_flow_info(echo):
6464
load_keychain=False,
6565
)
6666

67-
run_click_command(flow.flow_info, runtime=runtime, flow_name="test")
67+
run_click_command(flow.flow_info, runtime=runtime, flow_name="test", extra_yaml=())
6868

6969
echo.assert_called_with(
7070
"\nFlow Steps\n1) task: test_task [from current folder]\n options:\n option_name: option_value"
@@ -75,7 +75,36 @@ def test_flow_info__not_found():
7575
runtime = mock.Mock()
7676
runtime.get_flow.side_effect = FlowNotFoundError
7777
with pytest.raises(click.UsageError):
78-
run_click_command(flow.flow_info, runtime=runtime, flow_name="test")
78+
run_click_command(
79+
flow.flow_info, runtime=runtime, flow_name="test", extra_yaml=()
80+
)
81+
82+
83+
def test_flow_info__extra_yaml_applied(tmp_path):
84+
extra = tmp_path / "extra.yml"
85+
extra.write_text(
86+
"flows:\n"
87+
" injected_flow:\n"
88+
" description: injected flow\n"
89+
" steps:\n"
90+
" 1:\n"
91+
" task: util_sleep\n"
92+
)
93+
runtime = mock.Mock()
94+
runtime.get_flow.return_value.get_summary.return_value = "summary text"
95+
96+
run_click_command(
97+
flow.flow_info,
98+
runtime=runtime,
99+
flow_name="injected_flow",
100+
extra_yaml=(str(extra),),
101+
)
102+
103+
runtime.reload_project_config.assert_called_once()
104+
assert (
105+
"injected flow"
106+
in runtime.reload_project_config.call_args.kwargs["additional_yaml"]
107+
)
79108

80109

81110
@mock.patch("cumulusci.cli.flow.group_items")
@@ -157,6 +186,7 @@ def test_flow_run():
157186
debug=False,
158187
o=[("test_task__color", "blue")],
159188
no_prompt=True,
189+
extra_yaml=(),
160190
)
161191

162192
runtime.get_flow.assert_called_once_with(
@@ -165,6 +195,55 @@ def test_flow_run():
165195
org_config.delete_org.assert_called_once()
166196

167197

198+
def test_flow_run__extra_yaml_applied(tmp_path):
199+
extra = tmp_path / "extra.yml"
200+
extra.write_text(
201+
"tasks:\n"
202+
" injected_task:\n"
203+
" description: injected via --extra-yaml\n"
204+
" class_path: cumulusci.tasks.util.Sleep\n"
205+
)
206+
runtime = mock.Mock()
207+
runtime.get_org.return_value = ("dev", mock.Mock(scratch=False))
208+
runtime.get_flow.return_value.run.return_value = None
209+
210+
run_click_command(
211+
flow.flow_run,
212+
runtime=runtime,
213+
flow_name="test_flow",
214+
org="dev",
215+
delete_org=False,
216+
debug=False,
217+
o=(),
218+
no_prompt=True,
219+
extra_yaml=(str(extra),),
220+
)
221+
222+
runtime.reload_project_config.assert_called_once()
223+
call_kwargs = runtime.reload_project_config.call_args.kwargs
224+
assert "injected via --extra-yaml" in call_kwargs["additional_yaml"]
225+
226+
227+
def test_flow_run__no_extra_yaml_does_not_reload():
228+
runtime = mock.Mock()
229+
runtime.get_org.return_value = ("dev", mock.Mock(scratch=False))
230+
runtime.get_flow.return_value.run.return_value = None
231+
232+
run_click_command(
233+
flow.flow_run,
234+
runtime=runtime,
235+
flow_name="test_flow",
236+
org="dev",
237+
delete_org=False,
238+
debug=False,
239+
o=(),
240+
no_prompt=True,
241+
extra_yaml=(),
242+
)
243+
244+
runtime.reload_project_config.assert_called_once_with(additional_yaml=None)
245+
246+
168247
def test_flow_run__delete_org_when_error_occurs_in_flow():
169248
org_config = mock.Mock(scratch=True, config={})
170249
runtime = CliRuntime(
@@ -194,6 +273,7 @@ def test_flow_run__delete_org_when_error_occurs_in_flow():
194273
debug=False,
195274
o=[("test_task__color", "blue")],
196275
no_prompt=True,
276+
extra_yaml=(),
197277
)
198278

199279
runtime.get_flow.assert_called_once_with(
@@ -217,6 +297,7 @@ def test_flow_run__option_error():
217297
debug=False,
218298
o=[("test_task", "blue")],
219299
no_prompt=True,
300+
extra_yaml=(),
220301
)
221302

222303

@@ -235,6 +316,7 @@ def test_flow_run__delete_non_scratch():
235316
debug=False,
236317
o=None,
237318
no_prompt=True,
319+
extra_yaml=(),
238320
)
239321

240322

@@ -267,6 +349,7 @@ def test_flow_run__org_delete_error(echo):
267349
"debug": False,
268350
"no_prompt": True,
269351
"o": (("test_task__color", "blue"),),
352+
"extra_yaml": (),
270353
}
271354

272355
run_click_command(flow.flow_run, **kwargs)

0 commit comments

Comments
 (0)