diff --git a/connectors/citrus-jbang-connector/src/main/java/org/citrusframework/jbang/JBangSettings.java b/connectors/citrus-jbang-connector/src/main/java/org/citrusframework/jbang/JBangSettings.java index 1ea6afb43c..be1b1619d6 100644 --- a/connectors/citrus-jbang-connector/src/main/java/org/citrusframework/jbang/JBangSettings.java +++ b/connectors/citrus-jbang-connector/src/main/java/org/citrusframework/jbang/JBangSettings.java @@ -20,7 +20,10 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Optional; public final class JBangSettings { @@ -59,6 +62,9 @@ public final class JBangSettings { private static final String APP_ENV = JBANG_ENV_PREFIX + "APP"; private static final String APP_DEFAULT = "citrus@citrusframework/citrus"; + private static final String ARGS_PROPERTY = JBANG_PROPERTY_PREFIX + "args"; + private static final String ARGS_ENV = JBANG_ENV_PREFIX + "ARGS"; + private static final String JBANG_EXECUTABLE_PROPERTY = JBANG_PROPERTY_PREFIX + "executable"; private static final String JBANG_EXECUTABLE_ENV = JBANG_ENV_PREFIX + "EXECUTABLE"; private static final String JBANG_EXECUTABLE_DEFAULT = ""; @@ -158,4 +164,64 @@ public static String getJBangExecutable() { .or(() -> Optional.ofNullable(System.getProperty(JBANG_EXECUTABLE_PROPERTY, System.getenv(JBANG_EXECUTABLE_ENV)))) .orElse(JBANG_EXECUTABLE_DEFAULT); } + + /** + * JBang command arguments resolved from system property or environment variable. + * Arguments are specified as comma-delimited key=value pairs. Repeated argument names + * are merged into a single argument with comma-separated values. + */ + public static List getArgs() { + return parseArgs(System.getProperty(ARGS_PROPERTY, System.getenv(ARGS_ENV))); + } + + /** + * Parses comma-delimited key=value argument pairs into a list of JBang command arguments. + * Adds the "--" prefix to argument names when not already present. + * Merges repeated argument names into a single "--arg=value1,value2" argument. + * Empty values produce flag-only arguments (e.g., "fresh=" becomes "--fresh"). + */ + static List parseArgs(String argsValue) { + if (argsValue == null || argsValue.isBlank()) { + return Collections.emptyList(); + } + + LinkedHashMap> argMap = new LinkedHashMap<>(); + for (String part : argsValue.split(",")) { + String trimmed = part.trim(); + if (trimmed.isEmpty()) { + continue; + } + + int eqIndex = trimmed.indexOf('='); + String name; + String value; + if (eqIndex >= 0) { + name = trimmed.substring(0, eqIndex).trim(); + value = trimmed.substring(eqIndex + 1).trim(); + } else { + name = trimmed; + value = ""; + } + + if (!name.startsWith("--")) { + name = "--" + name; + } + + argMap.computeIfAbsent(name, k -> new ArrayList<>()); + if (!value.isEmpty()) { + argMap.get(name).add(value); + } + } + + List result = new ArrayList<>(); + for (Map.Entry> entry : argMap.entrySet()) { + if (entry.getValue().isEmpty()) { + result.add(entry.getKey()); + } else { + result.add(entry.getKey() + "=" + String.join(",", entry.getValue())); + } + } + + return result; + } } diff --git a/connectors/citrus-jbang-connector/src/main/java/org/citrusframework/jbang/JBangSupport.java b/connectors/citrus-jbang-connector/src/main/java/org/citrusframework/jbang/JBangSupport.java index 286da4403d..15748294f0 100644 --- a/connectors/citrus-jbang-connector/src/main/java/org/citrusframework/jbang/JBangSupport.java +++ b/connectors/citrus-jbang-connector/src/main/java/org/citrusframework/jbang/JBangSupport.java @@ -246,7 +246,8 @@ public ProcessAndOutput runAsync(String command, File output, List args) private List constructAllArgs(String command, List args) { List allArgs = new ArrayList<>(); - // JBang app name + allArgs.addAll(JBangSettings.getArgs()); + if (app != null) { allArgs.add(app); } diff --git a/connectors/citrus-jbang-connector/src/test/java/org/citrusframework/jbang/JBangSettingsTest.java b/connectors/citrus-jbang-connector/src/test/java/org/citrusframework/jbang/JBangSettingsTest.java new file mode 100644 index 0000000000..fa530d5bad --- /dev/null +++ b/connectors/citrus-jbang-connector/src/test/java/org/citrusframework/jbang/JBangSettingsTest.java @@ -0,0 +1,94 @@ +/* + * Copyright the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.citrusframework.jbang; + +import java.util.List; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class JBangSettingsTest { + + @Test + public void shouldReturnEmptyListForNullInput() { + Assert.assertTrue(JBangSettings.parseArgs(null).isEmpty()); + } + + @Test + public void shouldReturnEmptyListForEmptyInput() { + Assert.assertTrue(JBangSettings.parseArgs("").isEmpty()); + } + + @Test + public void shouldReturnEmptyListForBlankInput() { + Assert.assertTrue(JBangSettings.parseArgs(" ").isEmpty()); + } + + @Test + public void shouldParseSingleArgWithValue() { + List result = JBangSettings.parseArgs("deps=org.foo:bar:1.0"); + Assert.assertEquals(result, List.of("--deps=org.foo:bar:1.0")); + } + + @Test + public void shouldParseMultipleArgs() { + List result = JBangSettings.parseArgs("deps=org.foo:bar:1.0,fresh="); + Assert.assertEquals(result, List.of("--deps=org.foo:bar:1.0", "--fresh")); + } + + @Test + public void shouldMergeRepeatedArgNames() { + List result = JBangSettings.parseArgs("deps=org.foo:bar:1.0,deps=org.baz:qux:2.0"); + Assert.assertEquals(result, List.of("--deps=org.foo:bar:1.0,org.baz:qux:2.0")); + } + + @Test + public void shouldHandleEmptyValueAsFlag() { + List result = JBangSettings.parseArgs("fresh="); + Assert.assertEquals(result, List.of("--fresh")); + } + + @Test + public void shouldHandleArgWithoutEqualsAsFlag() { + List result = JBangSettings.parseArgs("fresh"); + Assert.assertEquals(result, List.of("--fresh")); + } + + @Test + public void shouldNotAddDoubleDashPrefix() { + List result = JBangSettings.parseArgs("--deps=org.foo:bar:1.0"); + Assert.assertEquals(result, List.of("--deps=org.foo:bar:1.0")); + } + + @Test + public void shouldHandleFullExampleFromIssue() { + List result = JBangSettings.parseArgs("deps=org.foo:bar:1.0,deps=org.baz:qux:2.0,fresh="); + Assert.assertEquals(result, List.of("--deps=org.foo:bar:1.0,org.baz:qux:2.0", "--fresh")); + } + + @Test + public void shouldPreserveArgOrder() { + List result = JBangSettings.parseArgs("fresh=,deps=org.foo:bar:1.0,verbose="); + Assert.assertEquals(result, List.of("--fresh", "--deps=org.foo:bar:1.0", "--verbose")); + } + + @Test + public void shouldTrimWhitespace() { + List result = JBangSettings.parseArgs(" deps = org.foo:bar:1.0 , fresh = "); + Assert.assertEquals(result, List.of("--deps=org.foo:bar:1.0", "--fresh")); + } +}