Skip to content

Commit 4b86cd2

Browse files
olehermanseclaude
andcommitted
cfengine format: Fixed issue with multilining function calls inside macros
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Ole Herman Schumacher Elgesem <ole@northern.tech>
1 parent 93ccbf0 commit 4b86cd2

3 files changed

Lines changed: 159 additions & 5 deletions

File tree

src/cfengine_cli/format.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,13 +236,20 @@ def split_generic_list(
236236
"""Split list elements into one-per-line strings, each pre-indented."""
237237
has_macros = _contains_macro(middle)
238238
elements: list[str] = []
239+
# Indices of the lines that end a top-level value element. For a wrapped
240+
# element (split across several lines) this is only the final line, not the
241+
# inner continuation lines, so commas are never added inside a wrapped value.
242+
value_end_indices: list[int] = []
239243
for element in middle:
240244
if elements and element.type == ",":
241245
elements[-1] = elements[-1] + ","
242246
continue
243247
if element.type == "macro":
244248
elements.append(text(element))
245249
continue
250+
if element.type == "comment":
251+
elements.append(" " * indent + text(element))
252+
continue
246253
line = " " * indent + stringify_single_line_node(element)
247254
# Strict < reserves 1 char for the comma appended after this check
248255
if len(line) < line_length:
@@ -251,13 +258,13 @@ def split_generic_list(
251258
lines = split_generic_value(element, indent, line_length)
252259
elements.append(" " * indent + lines[0])
253260
elements.extend(lines[1:])
261+
value_end_indices.append(len(elements) - 1)
254262

255-
# Adjust trailing commas: with macros, fix every non-macro element
256-
# (one per branch); without, fix only the last non-comment element.
263+
# Adjust trailing commas: with macros, fix the last line of every value
264+
# element (one per branch); without, fix only the last non-comment element.
257265
if has_macros:
258-
for i, e in enumerate(elements):
259-
if not e.lstrip().startswith(("@", "#")):
260-
elements[i] = _set_trailing_comma(e, trailing_comma)
266+
for i in value_end_indices:
267+
elements[i] = _set_trailing_comma(elements[i], trailing_comma)
261268
else:
262269
for i in reversed(range(len(elements))):
263270
if not elements[i].lstrip().startswith("#"):

tests/format/011_macros.expected.cf

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,94 @@ bundle agent test(a)
200200
"$(a)";
201201
}
202202
@endif
203+
bundle agent test_macro_call_param
204+
{
205+
classes:
206+
"a" -> { "CFE-3927" }
207+
and => {
208+
# The variable must be defined
209+
isvariable("default:def.control_agent_environment_vars"),
210+
# The length of the variable must be greater than 0 (can't be an empty list)
211+
isgreaterthan(length("default:def.control_agent_environment_vars"), 0),
212+
# Each element of the list must be of the form KEY=VALUE
213+
every(".+=.+", "default:def.control_agent_environment_vars"),
214+
# In 3.18 and greater we can validate the type of variable in use
215+
@if minimum_version(3.18.0)
216+
regcmp(
217+
"(policy slist|data array)",
218+
type("default:def.control_agent_environment_vars", "true")
219+
),
220+
@endif
221+
};
222+
223+
"b" -> { "CFE-3927" }
224+
and => {
225+
# The variable must be defined
226+
isvariable("default:def.control_agent_environment_vars"),
227+
isgreaterthan(length("default:def.control_agent_environment_vars"), 0),
228+
@if minimum_version(3.18.0)
229+
regcmp(
230+
"(policy slist|data array)",
231+
type("default:def.control_agent_environment_vars", "true")
232+
),
233+
@endif
234+
every(".+=.+", "default:def.control_agent_environment_vars"),
235+
};
236+
237+
"c" -> { "CFE-3927" }
238+
and => {
239+
# The variable must be defined
240+
isvariable("default:def.control_agent_environment_vars"),
241+
isgreaterthan(length("default:def.control_agent_environment_vars"), 0),
242+
@if minimum_version(3.18.0)
243+
regcmp(
244+
"(policy slist|data array)",
245+
type("default:def.control_agent_environment_vars", "true")
246+
),
247+
every(".+=.+", "default:def.control_agent_environment_vars"),
248+
@endif
249+
};
250+
251+
"d" -> { "CFE-3927" }
252+
and => {
253+
@if minimum_version(3.18.0)
254+
every(".+=.+", "default:def.control_agent_environment_vars"),
255+
@endif
256+
isvariable("default:def.control_agent_environment_vars"),
257+
isgreaterthan(length("default:def.control_agent_environment_vars"), 0),
258+
regcmp(
259+
"(policy slist|data array)",
260+
type("default:def.control_agent_environment_vars", "true")
261+
),
262+
};
263+
264+
"e" -> { "CFE-3927" }
265+
and => {
266+
@if minimum_version(3.18.0)
267+
"a",
268+
@endif
269+
"b",
270+
"c",
271+
"d",
272+
isgreaterthan(length("default:def.control_agent_environment_vars"), 0),
273+
regcmp(
274+
"(policy slist|data array)",
275+
type("default:def.control_agent_environment_vars", "true")
276+
),
277+
};
278+
279+
"f" -> { "CFE-3927" }
280+
and => {
281+
isgreaterthan(length("default:def.control_agent_environment_vars"), 0),
282+
regcmp(
283+
"(policy slist|data array)",
284+
type("default:def.control_agent_environment_vars", "true")
285+
),
286+
"a",
287+
"b",
288+
"c",
289+
@if minimum_version(3.18.0)
290+
"d",
291+
@endif
292+
};
293+
}

tests/format/011_macros.input.cf

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,59 @@ reports:
193193
"$(a)";
194194
}
195195
@endif
196+
197+
bundle agent test_macro_call_param
198+
{
199+
classes:
200+
"a" -> { "CFE-3927" }
201+
and => {
202+
# The variable must be defined
203+
isvariable("default:def.control_agent_environment_vars"),
204+
# The length of the variable must be greater than 0 (can't be an empty list)
205+
isgreaterthan(length("default:def.control_agent_environment_vars"), 0),
206+
# Each element of the list must be of the form KEY=VALUE
207+
every(".+=.+", "default:def.control_agent_environment_vars"),
208+
# In 3.18 and greater we can validate the type of variable in use
209+
@if minimum_version(3.18.0)
210+
regcmp("(policy slist|data array)",type("default:def.control_agent_environment_vars", "true")),
211+
@endif
212+
};
213+
"b" -> { "CFE-3927" }
214+
and => {
215+
# The variable must be defined
216+
isvariable("default:def.control_agent_environment_vars"),isgreaterthan(length("default:def.control_agent_environment_vars"), 0),
217+
@if minimum_version(3.18.0)
218+
regcmp( "(policy slist|data array)", type("default:def.control_agent_environment_vars", "true") ),
219+
@endif
220+
every(".+=.+", "default:def.control_agent_environment_vars"),
221+
};
222+
"c" -> { "CFE-3927" }
223+
and => {
224+
# The variable must be defined
225+
isvariable("default:def.control_agent_environment_vars"), isgreaterthan(length("default:def.control_agent_environment_vars"), 0),
226+
@if minimum_version(3.18.0)
227+
regcmp("(policy slist|data array)",type("default:def.control_agent_environment_vars", "true")),every(".+=.+", "default:def.control_agent_environment_vars"),
228+
@endif
229+
};
230+
"d" -> { "CFE-3927" }
231+
and => {
232+
@if minimum_version(3.18.0)
233+
every(".+=.+", "default:def.control_agent_environment_vars"),
234+
@endif
235+
isvariable("default:def.control_agent_environment_vars"),isgreaterthan(length("default:def.control_agent_environment_vars"), 0),regcmp("(policy slist|data array)",type("default:def.control_agent_environment_vars", "true")),
236+
};
237+
"e" -> { "CFE-3927" }
238+
and => {
239+
@if minimum_version(3.18.0)
240+
"a",
241+
@endif
242+
"b","c","d",isgreaterthan(length("default:def.control_agent_environment_vars"),0),regcmp("(policy slist|data array)",type("default:def.control_agent_environment_vars", "true")),
243+
};
244+
"f" -> { "CFE-3927" }
245+
and => {
246+
isgreaterthan(length("default:def.control_agent_environment_vars"), 0),regcmp("(policy slist|data array)",type("default:def.control_agent_environment_vars", "true")),"a","b","c",
247+
@if minimum_version(3.18.0)
248+
"d",
249+
@endif
250+
};
251+
}

0 commit comments

Comments
 (0)