From eeb078093797631b66e6e772a624d570ea83605b Mon Sep 17 00:00:00 2001 From: Simon Halvorsen Date: Wed, 29 Jul 2026 17:13:17 +0200 Subject: [PATCH] ENT-14368: Added a new input-type: multilinestring Ticket: ENT-14368 Signed-off-by: Simon Halvorsen --- cfbs/cfbs_config.py | 13 ++++++- cfbs/prompts.py | 34 +++++++++++++++++++ cfbs/validate.py | 4 +-- tests/shell/059_input_multilinestring.sh | 17 ++++++++++ .../example-cfbs.json | 33 ++++++++++++++++++ .../expected-augment.json | 12 +++++++ tests/shell/all.sh | 1 + 7 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 tests/shell/059_input_multilinestring.sh create mode 100644 tests/shell/059_input_multilinestring/example-cfbs.json create mode 100644 tests/shell/059_input_multilinestring/expected-augment.json diff --git a/cfbs/cfbs_config.py b/cfbs/cfbs_config.py index 1b250e22..02a0a361 100644 --- a/cfbs/cfbs_config.py +++ b/cfbs/cfbs_config.py @@ -44,7 +44,7 @@ is_module_local, is_module_absolute, ) -from cfbs.prompts import prompt_user, prompt_user_yesno +from cfbs.prompts import prompt_user, prompt_user_yesno, prompt_user_multiline from cfbs.validate import validate_single_module @@ -565,6 +565,15 @@ def _input_string(input_data): ) return response + def _input_multiline_string(input_data): + _check_keys(["question"], input_data) + response = prompt_user_multiline( + self.non_interactive, + input_data["question"], + default=input_data.get("default"), + ) + return response + def _input_elements(subtype): result = OrderedDict() for element in subtype: @@ -615,6 +624,8 @@ def _input_list(input_data): if definition["type"] == "string": definition["response"] = _input_string(definition) + elif definition["type"] == "string-multiline": + definition["response"] = _input_multiline_string(definition) elif definition["type"] == "list": definition["response"] = _input_list(definition) else: diff --git a/cfbs/prompts.py b/cfbs/prompts.py index d89be0e1..5212016a 100644 --- a/cfbs/prompts.py +++ b/cfbs/prompts.py @@ -43,6 +43,40 @@ def prompt_user(non_interactive: bool, prompt: str, choices=None, default=None): return answer +def prompt_user_multiline(non_interactive: bool, prompt: str, default=None): + if non_interactive: + if default is None: + raise ValueError( + "Missing default value for prompt '%s' in non-interactive mode" % prompt + ) + return default + + print(prompt) + print( + "(Enter one or more lines of text, then finish with double newline or Ctrl+D" + " (Ctrl+Z followed by Enter on Windows))" + ) + + lines = [] + while True: + try: + inp = input() + if len(lines) > 0 and inp == "" == lines[-1]: + break + lines.append(inp) + except EOFError: + break + except KeyboardInterrupt: + print("\nOperation cancelled by user") + exit(1) + + answer = "\n".join(lines) + if answer == "" and default is not None: + answer = default + + return answer + + def prompt_user_yesno(non_interactive: bool, prompt: str, default="yes"): """Returns `True` if the answer is yes, and `False` otherwise.""" diff --git a/cfbs/validate.py b/cfbs/validate.py index dca67ed0..19aa0671 100644 --- a/cfbs/validate.py +++ b/cfbs/validate.py @@ -682,10 +682,10 @@ def _validate_module_input(name, module): % field, ) - if input_element["type"] not in ("string", "list"): + if input_element["type"] not in ("string", "list", "string-multiline"): raise CFBSValidationError( name, - 'The input "type" must be "string" or "list", not "%s"' + 'The input "type" must be "string", "string-multiline", or "list", not "%s"' % input_element["type"], ) if not re.fullmatch(r"[a-z_]+", input_element["variable"]): diff --git a/tests/shell/059_input_multilinestring.sh b/tests/shell/059_input_multilinestring.sh new file mode 100644 index 00000000..e02442ec --- /dev/null +++ b/tests/shell/059_input_multilinestring.sh @@ -0,0 +1,17 @@ +set -e +set -x +cd tests/ +mkdir -p ./tmp/ +cd ./tmp/ +touch cfbs.json && rm cfbs.json +rm -rf .git +rm -rf create-single-file-with-content +cp ../shell/059_input_multilinestring/example-cfbs.json cfbs.json + +cfbs --non-interactive input create-single-file-with-content +grep '"type": "string-multiline"' create-single-file-with-content/input.json +grep '"default": "Hello CFEngine!\\nBye CFEngine!"' create-single-file-with-content/input.json +grep '"response": "Hello CFEngine!\\nBye CFEngine!"' create-single-file-with-content/input.json + +cfbs render-input create-single-file-with-content create-single-file-with-content/input.json actual.output +diff actual.output ../shell/059_input_multilinestring/expected-augment.json diff --git a/tests/shell/059_input_multilinestring/example-cfbs.json b/tests/shell/059_input_multilinestring/example-cfbs.json new file mode 100644 index 00000000..8a64ad1b --- /dev/null +++ b/tests/shell/059_input_multilinestring/example-cfbs.json @@ -0,0 +1,33 @@ +{ + "name": "Example", + "type": "policy-set", + "description": "Example description", + "git": false, + "build": [ + { + "name": "create-single-file-with-content", + "description": "Create a single file with content.", + "steps": ["input ./input.json def.json"], + "input": [ + { + "type": "string", + "variable": "filename", + "namespace": "cfbs", + "bundle": "create_single_file_with_content", + "label": "Filename", + "question": "What file should this module create?", + "default": "/tmp/create-single-file-with-content.txt" + }, + { + "type": "string-multiline", + "variable": "content", + "namespace": "cfbs", + "bundle": "create_single_file_with_content", + "label": "Content", + "question": "What content should this file have?", + "default": "Hello CFEngine!\nBye CFEngine!" + } + ] + } + ] +} diff --git a/tests/shell/059_input_multilinestring/expected-augment.json b/tests/shell/059_input_multilinestring/expected-augment.json new file mode 100644 index 00000000..a7634664 --- /dev/null +++ b/tests/shell/059_input_multilinestring/expected-augment.json @@ -0,0 +1,12 @@ +{ + "variables": { + "cfbs:create_single_file_with_content.filename": { + "value": "/tmp/create-single-file-with-content.txt", + "comment": "Added by 'cfbs input'" + }, + "cfbs:create_single_file_with_content.content": { + "value": "Hello CFEngine!\nBye CFEngine!", + "comment": "Added by 'cfbs input'" + } + } +} diff --git a/tests/shell/all.sh b/tests/shell/all.sh index 0fe315cd..22d21c2f 100644 --- a/tests/shell/all.sh +++ b/tests/shell/all.sh @@ -102,6 +102,7 @@ run_test tests/shell/055_render_input_two_variables.sh run_test tests/shell/056_render_input_list.sh run_test tests/shell/057_render_input_no_response.sh run_test tests/shell/058_render_input_fail.sh +run_test tests/shell/059_input_multilinestring.sh # Summary _suite_end=$(date +%s)