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=[]

Reply via email to