This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch CAMEL-23810-cli-option-cleanup in repository https://gitbox.apache.org/repos/asf/camel.git
commit db9fef0873dd07106314d4ce0aa6a3a2f3bcfbba Author: Claus Ibsen <[email protected]> AuthorDate: Sun Nov 1 14:20:31 2026 +0100 CAMEL-26455: camel-jbang - camel config set dev=true does not set the --dev option of camel run A default from the user configuration was only looked up by the longest name of an option, so dev=true did not set --reload,--dev. Every long name is now looked up, the command's own key (camel.run.dev) first. Two option definitions cleaned up before the port to aesh (CAMEL-23810): dependency update sets the files of the export from its own target files, instead of relying on picocli giving them to both, and --output-directory of dependency copy is no longer required next to its default. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../ROOT/pages/camel-jbang-configuration.adoc | 9 +++++ .../META-INF/camel-jbang-commands-metadata.json | 2 +- .../dsl/jbang/core/commands/DependencyCopy.java | 2 +- .../dsl/jbang/core/commands/DependencyUpdate.java | 9 +++-- .../dsl/jbang/core/common/CommandLineHelper.java | 43 ++++++++++++++++++++++ .../jbang/core/commands/DependencyUpdateTest.java | 25 +++++++++++++ .../core/commands/UserConfigDefaultsTest.java | 42 +++++++++++++++++++++ .../src/test/resources/golden/cli-parse.txt | 4 +- 8 files changed, 128 insertions(+), 8 deletions(-) diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc b/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc index 142cd4d99f47..68aee5cfb6dd 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc @@ -104,6 +104,15 @@ camel config list camel config get gav ---- +A key is the name of a command option without `--`, and the value is its default for every command that has the +option. An option with more than one name, such as `--reload` and `--dev` of `camel run`, can be set by any of them. +To set the default for one command only, start the key with `camel.` and the command: + +[source,bash] +---- +camel config set camel.run.dev=true +---- + === Placeholder substitution Configuration values can be referenced as placeholders with `#optionName`: diff --git a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json index 05eb2ab9ac66..a9c0aabcb36f 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json +++ b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json @@ -7,7 +7,7 @@ { "name": "completion", "fullName": "completion", "description": "Generate completion script for bash\/zsh", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Complete", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, { "name": "config", "fullName": "config", "description": "Get and set user configuration values", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.config.ConfigCommand", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "get", "fullName": "config get", "description": "Display user configuration value", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.config. [...] { "name": "debug", "fullName": "debug", "description": "Debug local Camel integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Debug", "options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names": "--background", "description": "Run in the background", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--background-wait", "description": "How [...] - { "name": "dependency", "fullName": "dependency", "description": "Displays all Camel dependencies required to run", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.DependencyCommand", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "copy", "fullName": "dependency copy", "description": "Copies all Camel dependencies required to run to a specific directory", "sourc [...] + { "name": "dependency", "fullName": "dependency", "description": "Displays all Camel dependencies required to run", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.DependencyCommand", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "copy", "fullName": "dependency copy", "description": "Copies all Camel dependencies required to run to a specific directory", "sourc [...] { "name": "dev", "fullName": "dev", "description": "Run in dev mode with live reload", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Dev", "options": [ { "names": "--background", "description": "Run in the background", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "--background-wait", "description": "How long to wait for the integration in the background to start, before returning, in millis or as a duration such as 2m (true waits 60 secon [...] { "name": "dirty", "fullName": "dirty", "description": "Check if there are dirty files from previous Camel runs that did not terminate gracefully", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.Dirty", "options": [ { "names": "--clean", "description": "Clean dirty files which are no longer in use", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", " [...] { "name": "doc", "fullName": "doc", "description": "Shows documentation for kamelet, component, EIP, and other Camel resources", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.catalog.CatalogDoc", "options": [ { "names": "--camel-version", "description": "To use a different Camel version than the default version", "javaType": "java.lang.String", "type": "string" }, { "names": "--download", "description": "Whether to allow automatic downloading JAR dependencies (over the int [...] diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyCopy.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyCopy.java index d1ec34795362..e2b6beef71fc 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyCopy.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyCopy.java @@ -45,7 +45,7 @@ public class DependencyCopy extends DependencyList { private MavenDownloader downloader; @CommandLine.Option(names = { "--output-directory" }, description = "Directory where dependencies should be copied", - defaultValue = "lib", required = true) + defaultValue = "lib") protected String outputDirectory; private boolean outputDirectoryCreated = false; diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyUpdate.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyUpdate.java index effadb16e0d8..87bda04d17c4 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyUpdate.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyUpdate.java @@ -85,6 +85,10 @@ public class DependencyUpdate extends DependencyList { return CliExitCode.USAGE; } + // every file is a source file of the export pipeline: the Java files for their //DEPS and the route + // definition files (YAML, XML) for the dependency resolution (a pom.xml is skipped there). + // The command line can also give them to the files of the export, so they are set only from here. + this.files.clear(); for (Path file : targetFiles) { if (!Files.exists(file)) { printer().printErr("Source file does not exist: " + file); @@ -94,11 +98,8 @@ public class DependencyUpdate extends DependencyList { String ext = FileUtil.onlyExt(name, true); if ("pom.xml".equals(name) || "java".equals(ext)) { updateTargets.add(file); - } else { - // route definition files (YAML, XML) are used as source files - // for the export pipeline dependency resolution - this.files.add(file.toString()); } + this.files.add(file.toString()); } if (scanRoutes) { diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CommandLineHelper.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CommandLineHelper.java index edb006cc7470..8186242c0828 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CommandLineHelper.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CommandLineHelper.java @@ -24,6 +24,8 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; import java.util.Properties; import java.util.function.Consumer; @@ -235,12 +237,16 @@ public final class CommandLineHelper { private static class CamelUserConfigDefaultValueProvider extends CommandLine.PropertiesDefaultProvider { + private final Properties properties; + public CamelUserConfigDefaultValueProvider(Path file) { super(file.toFile()); + this.properties = null; } public CamelUserConfigDefaultValueProvider(Properties properties) { super(properties); + this.properties = properties; } @Override @@ -250,8 +256,45 @@ public final class CommandLineHelper { if (parent != null && parent.name().equals("plugin")) { return null; } + if (properties != null && argSpec instanceof CommandLine.Model.OptionSpec option) { + return optionDefaultValue(option); + } return super.defaultValue(argSpec); } + + // picocli only looks up the longest name of an option, so dev=true would not set --reload,--dev: + // look up every long name, first for the command (camel.run.dev) and then for all commands (dev) + private String optionDefaultValue(CommandLine.Model.OptionSpec option) { + List<String> names = new ArrayList<>(); + names.add(option.longestName()); + for (String name : option.names()) { + if (name.startsWith("--") && !names.contains(name)) { + names.add(name); + } + } + String command = option.command().qualifiedName("."); + for (String name : names) { + String value = properties.getProperty(command + "." + stripPrefix(name)); + if (value != null) { + return value; + } + } + for (String name : names) { + String value = properties.getProperty(stripPrefix(name)); + if (value != null) { + return value; + } + } + return null; + } + + private static String stripPrefix(String name) { + int i = 0; + while (i < name.length() && !Character.isJavaIdentifierPart(name.charAt(i))) { + i++; + } + return name.substring(i); + } } } diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/DependencyUpdateTest.java b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/DependencyUpdateTest.java index 1c5a528fc1a5..f6e9fffd722d 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/DependencyUpdateTest.java +++ b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/DependencyUpdateTest.java @@ -385,6 +385,31 @@ class DependencyUpdateTest extends CamelCommandBaseTestSupport { assertThat(content2).contains("//DEPS org.apache.camel:camel-bom:"); } + @Test + void shouldUpdateWhenOnlyTheTargetFilesAreGiven() throws Exception { + Path java = createFile("MyRoute.java", """ + ///usr/bin/env jbang + import org.apache.camel.builder.RouteBuilder; + public class MyRoute extends RouteBuilder { + public void configure() { + from("timer:tick").to("arangodb:myDb"); + } + } + """); + + DependencyUpdate command = new DependencyUpdate(new CamelJBangMain().withPrinter(printer)); + CommandLine.populateCommand(command, + "--dir=" + workingDir, + java.toAbsolutePath().toString()); + // picocli also gives the files to the <files> of the export, a command line parser may not + command.files.clear(); + + int exit = command.doCall(); + Assertions.assertEquals(0, exit, printer.getLines().toString()); + + assertThat(Files.readString(java)).contains("//DEPS org.apache.camel:camel-arangodb"); + } + // ==================== Helpers ==================== private void addArangodbToCamelFile() throws Exception { diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/UserConfigDefaultsTest.java b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/UserConfigDefaultsTest.java index ee87738b010c..649770e29ccd 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/UserConfigDefaultsTest.java +++ b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/UserConfigDefaultsTest.java @@ -83,12 +83,32 @@ class UserConfigDefaultsTest extends BaseConfigTestSupport { assertThat(run("plugin", "echo")).isEqualTo("sort=pid repos=null"); } + @Test + void everyLongNameOfAnOption() throws Exception { + UserConfigHelper.createUserConfig("dev=true"); + assertThat(run("dev")).isEqualTo("dev=true"); + + UserConfigHelper.createUserConfig("reload=true"); + assertThat(run("dev")).isEqualTo("dev=true"); + + UserConfigHelper.createUserConfig("camel.dev.dev=true"); + assertThat(run("dev")).isEqualTo("dev=true"); + } + + @Test + void configurationOfOneCommandWinsForAnyName() throws Exception { + UserConfigHelper.createUserConfig("reload=true\ncamel.dev.dev=false"); + + assertThat(run("dev")).isEqualTo("dev=false"); + } + private static String run(String... args) { StringPrinter out = new StringPrinter(); CamelJBangMain main = new CamelJBangMain().withPrinter(out); CommandLine commandLine = new CommandLine(main) .addSubcommand("echo", new CommandLine(new EchoCommand(main))) .addSubcommand("other", new CommandLine(new EchoCommand(main))) + .addSubcommand("dev", new CommandLine(new DevCommand(main))) .addSubcommand("plugin", new CommandLine(new PluginCommand(main)) .addSubcommand("echo", new CommandLine(new EchoCommand(main)))); CommandLineHelper.augmentWithUserConfiguration(commandLine); @@ -121,4 +141,26 @@ class UserConfigDefaultsTest extends BaseConfigTestSupport { return 0; } } + + // an option with two long names, as --reload,--dev of camel run + static class DevCommand extends CamelCommand { + + @CommandLine.Option(names = { "--reload", "--dev" }) + boolean dev; + + DevCommand(CamelJBangMain main) { + super(main); + } + + @Override + protected boolean disarrangeLogging() { + return false; + } + + @Override + public Integer doCall() { + printer().println("dev=" + dev); + return 0; + } + } } diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/cli-parse.txt b/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/cli-parse.txt index df67a10a13e6..c6379d430612 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/cli-parse.txt +++ b/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/cli-parse.txt @@ -6270,7 +6270,7 @@ debugOptions.openTelemetryAgentExport="tui" debugOptions.trace=false dependencies=[] - dev=false + dev=true empty=false example=null excludes=[] @@ -6331,7 +6331,7 @@ stub=null verbose=false > run --dev=false - (defaults) + dev=false > export applicationProperties=null buildProperties=[]
