From c18bcc7254f68d7d9eb806f2d31fe5d6a828c8cd Mon Sep 17 00:00:00 2001 From: Simon Halvorsen Date: Wed, 2 Sep 2026 13:56:05 +0200 Subject: [PATCH 1/2] Fixed a bug where files copied in from outside the project were not getting the correct path-variable Ticket: None Signed-off-by: Simon Halvorsen --- cfbs/build.py | 5 ++++- tests/test_build.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/cfbs/build.py b/cfbs/build.py index 86af077c..1f5fbc6a 100644 --- a/cfbs/build.py +++ b/cfbs/build.py @@ -301,11 +301,14 @@ def _localize(rel_path): if already_shipped is not None: return already_shipped + rel_path = os.path.normpath(rel_path) + in_module_dir = rel_path.split(os.sep)[0] == module_dir_name + dest = os.path.join( destination, "services", "cfbs", - "modules" if rel_path.startswith(module_dir_name) else "", + "modules" if in_module_dir else "", rel_path, ) cp(rel_path, dest) diff --git a/tests/test_build.py b/tests/test_build.py index 7400388c..56389929 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -42,7 +42,7 @@ def test_localize_file_inputs_copies_list_of_files(tmp_path, monkeypatch): } ] - _localize_file_inputs("run-scripts", input_data, "out/masterfiles", []) + _localize_file_inputs("run-scripts-module", input_data, "out/masterfiles", []) assert input_data[0]["response"] == [ "$(sys.inputdir)/services/cfbs/one.sh", @@ -54,6 +54,33 @@ def test_localize_file_inputs_copies_list_of_files(tmp_path, monkeypatch): ) +def test_localize_file_inputs_strips_local_module_prefix_with_dot_slash( + tmp_path, monkeypatch +): + monkeypatch.chdir(tmp_path) + os.makedirs("out/masterfiles") + os.makedirs("run-a-script") + with open("run-a-script/deploy.sh", "w") as f: + f.write("echo hi\n") + + input_data = [ + { + "type": "file", + "variable": "script", + "response": "./run-a-script/deploy.sh", + } + ] + + _localize_file_inputs("./run-a-script", input_data, "out/masterfiles", []) + + assert input_data[0]["response"] == ( + "$(sys.inputdir)/services/cfbs/modules/run-a-script/deploy.sh" + ) + assert os.path.isfile( + "out/masterfiles/services/cfbs/modules/run-a-script/deploy.sh" + ) + + def test_localize_file_inputs_strips_local_module_prefix(tmp_path, monkeypatch): monkeypatch.chdir(tmp_path) os.makedirs("out/masterfiles") From db8756fc57cad192790e75cf86d79b4df4133b0f Mon Sep 17 00:00:00 2001 From: Simon Halvorsen Date: Wed, 2 Sep 2026 14:25:03 +0200 Subject: [PATCH 2/2] Added example of multiline-string to JSON.md & updated file example Ticket: None Signed-off-by: Simon Halvorsen --- JSON.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/JSON.md b/JSON.md index bcaf7cb4..acd98a10 100644 --- a/JSON.md +++ b/JSON.md @@ -665,6 +665,47 @@ $ cat ./out/masterfiles/def.json } ``` +### Multi-line string input example + +For content that may span several lines, use `"string-multiline"` instead of `"string"`. +Swapping the `content` field from the previous example: + +```json + { + "type": "string-multiline", + "variable": "content", + "label": "Content", + "question": "What content should this file have?", + "default": "Hello CFEngine!\nBye CFEngine!" + } +``` + +When prompted interactively, keep entering lines and finish with double empty line, or Ctrl+D (Ctrl+Z followed by Enter on Windows): + +``` +$ cfbs input create-single-file-with-content +Adding input for module 'create-single-file-with-content': +What file should this module create? /tmp/create-single-file-with-content.txt +What content should this file have? +(Enter one or more lines of text, then finish with double newline or Ctrl+D (Ctrl+Z followed by Enter on Windows)) +Hello CFEngine! +Bye CFEngine! + +``` + +The entered lines are joined with `\n` into a single string response, same as for `"string"`: + +```json + { + "type": "string-multiline", + "variable": "content", + "label": "Content", + "question": "What content should this file have?", + "default": "Hello CFEngine!\nBye CFEngine!", + "response": "Hello CFEngine!\nBye CFEngine!" + } +``` + ### Create multiple files example Sometimes we would like a module to support taking an arbritary number of inputs. @@ -904,6 +945,33 @@ $ cat ./run-scripts/input.json Without `"while"`, `"response"` is a single path instead of a list. A file already inside the project is referred to as-is; a file from outside (as above) is copied into the module's own directory, next to `input.json`. +#### Where these files end up in the built policy set + +During `cfbs build`, every `"file"` response is copied into the policy set (so it's actually part of what gets deployed) and rewritten to the resulting `$(sys.inputdir)/...` path. + +- If the file is already inside a local module directory that has its own `directory ./ ` build step (see the next example), that step already ships the whole directory during the same build - so the file isn't copied again, its response just points at that step's destination. +- Otherwise, the file is copied into `services/cfbs/`, preserving its path *relative to the project root* rather than just its filename, so that files with the same name coming from different sources (e.g. two different modules' file inputs both named `deploy.sh`) don't overwrite each other. + - If the file lives inside the referencing module's own directory - as is normal for files placed there by `cfbs input`, like the two scripts above, or for a `"file"` input whose `"default"` points at a file the module ships itself - it's additionally namespaced under `services/cfbs/modules//...`, keeping different modules' same-named files apart from each other. + +Continuing the example above, both scripts live inside `./run-scripts/`, next to `input.json`, so `cfbs build` copies them under `modules/run-scripts/`: + +``` +$ cfbs build +--snip-- +$ cat ./out/masterfiles/def.json +{ + "variables": { + "my_namespace:my_bundle.scripts": { + "value": [ + "$(sys.inputdir)/services/cfbs/modules/run-scripts/deploy.sh", + "$(sys.inputdir)/services/cfbs/modules/run-scripts/rollback.sh" + ], + "comment": "Added by 'cfbs input'" + } + } +} +``` + Sometimes you may want to place all your files in a separate directory for easier management. ``` $ tree -L 2