You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/03/09 09:10:29 UTC

[camel] branch main updated: CAMEL-18508: camel-jbang - User configuration

This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new d98319e04b5 CAMEL-18508: camel-jbang - User configuration
d98319e04b5 is described below

commit d98319e04b5145d0af0ce783d7bb5a371f55ee68
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Mar 9 10:09:57 2023 +0100

    CAMEL-18508: camel-jbang - User configuration
---
 .../modules/ROOT/pages/camel-jbang.adoc            |  73 ++++++++++++++
 dsl/camel-jbang/camel-jbang-core/pom.xml           |  14 +++
 .../apache/camel/dsl/jbang/core/commands/Bind.java |   2 +-
 .../dsl/jbang/core/commands/CamelJBangMain.java    |  14 ++-
 .../camel/dsl/jbang/core/commands/Complete.java    |   2 +-
 .../camel/dsl/jbang/core/commands/Export.java      |   2 +-
 .../apache/camel/dsl/jbang/core/commands/Init.java |   2 +-
 .../apache/camel/dsl/jbang/core/commands/Pipe.java |   2 +-
 .../apache/camel/dsl/jbang/core/commands/Run.java  |   4 +-
 .../{Complete.java => config/ConfigCommand.java}   |  26 ++---
 .../{Complete.java => config/ConfigGet.java}       |  35 +++----
 .../{Complete.java => config/ConfigList.java}      |  32 +++---
 .../dsl/jbang/core/commands/config/ConfigSet.java  |  54 +++++++++++
 .../{Complete.java => config/ConfigUnset.java}     |  32 +++---
 .../dsl/jbang/core/common/CommandLineHelper.java   | 107 +++++++++++++++++++++
 .../apache/camel/dsl/jbang/BackgroundTest.java}    |  53 +++++-----
 16 files changed, 353 insertions(+), 101 deletions(-)

diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
index 8da94028258..14b4f4b19ae 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
@@ -1807,6 +1807,79 @@ The follow options related to _exporting_, can be configured in `application.pro
 
 NOTE: These are options from the export command, so you can see mor details and default values using `camel export --help`.
 
+== Configuration
+
+Camel JBang can be used to store and use user configuration so that cli options won't have to be specified each time, for example, in order to run a different camel version, instead of executing
+
+[source,bash]
+----
+camel run * --camel-version=3.18.4
+----
+
+the option camel-version can be added to the user configuration like
+
+[source,bash]
+----
+camel config set camel-version=3.18.4
+----
+
+and then, the run command will pick the user configuration
+
+[source,bash]
+----
+camel run *
+----
+
+The user configuration file will be stored in ~/.camel-jbang-user.properties
+
+=== Set and unset configuration
+
+Every Camel JBang option can be added to the user configuration, for example to export a simple project
+
+[source,bash]
+----
+camel init foo.yaml
+camel config set gav=com.foo:acme:1.0-SNAPSHOT
+camel config set runtime=spring-boot
+camel config set deps=org.apache.camel.springboot:camel-timer-starter
+camel config set camel-spring-boot-version=3.20.1
+
+camel export
+----
+
+User configurations values can be unset via:
+
+[source,bash]
+----
+camel config unset camel-spring-boot-version=3.20.1
+----
+
+=== List and get configurations
+
+User configurations keys can be listed with:
+
+[source,bash]
+----
+camel config list
+----
+
+Given the configuration in the previous paragraph the ouptut will be:
+
+[source,bash]
+----
+gav
+runtime
+deps
+----
+
+In order to get a value given a key, get command can be used:
+
+[source,bash]
+----
+camel config get gav
+
+com.foo:acme:1.0-SNAPSHOT
+----
 
 == Troubleshooting
 
diff --git a/dsl/camel-jbang/camel-jbang-core/pom.xml b/dsl/camel-jbang/camel-jbang-core/pom.xml
index 225e3b48b51..5da9824fb91 100644
--- a/dsl/camel-jbang/camel-jbang-core/pom.xml
+++ b/dsl/camel-jbang/camel-jbang-core/pom.xml
@@ -130,6 +130,20 @@
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
         </dependency>
+
+        <!-- test -->
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>${assertj-version}</version>
+            <scope>test</scope>
+        </dependency>
+
     </dependencies>
 
 </project>
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Bind.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Bind.java
index 6ab96f549f2..4fbd181c6af 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Bind.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Bind.java
@@ -45,7 +45,7 @@ import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.asText;
 import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.nodeAt;
 
 @Command(name = "bind", description = "Bind source and sink Kamelets as a new Camel integration")
-class Bind extends CamelCommand {
+public class Bind extends CamelCommand {
 
     @CommandLine.Option(names = { "--source" }, description = "Source (from) such as a Kamelet or Camel endpoint uri",
                         required = true)
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
index 2dd2531aa54..e75bc172137 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
@@ -40,6 +40,11 @@ import org.apache.camel.dsl.jbang.core.commands.catalog.CatalogDoc;
 import org.apache.camel.dsl.jbang.core.commands.catalog.CatalogKamelet;
 import org.apache.camel.dsl.jbang.core.commands.catalog.CatalogLanguage;
 import org.apache.camel.dsl.jbang.core.commands.catalog.CatalogOther;
+import org.apache.camel.dsl.jbang.core.commands.config.ConfigCommand;
+import org.apache.camel.dsl.jbang.core.commands.config.ConfigGet;
+import org.apache.camel.dsl.jbang.core.commands.config.ConfigList;
+import org.apache.camel.dsl.jbang.core.commands.config.ConfigSet;
+import org.apache.camel.dsl.jbang.core.commands.config.ConfigUnset;
 import org.apache.camel.dsl.jbang.core.commands.process.CamelContextStatus;
 import org.apache.camel.dsl.jbang.core.commands.process.CamelContextTop;
 import org.apache.camel.dsl.jbang.core.commands.process.CamelCount;
@@ -62,6 +67,7 @@ import org.apache.camel.dsl.jbang.core.commands.process.ListProcess;
 import org.apache.camel.dsl.jbang.core.commands.process.ListService;
 import org.apache.camel.dsl.jbang.core.commands.process.ListVault;
 import org.apache.camel.dsl.jbang.core.commands.process.StopProcess;
+import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
 import picocli.CommandLine;
 import picocli.CommandLine.Command;
 
@@ -124,7 +130,12 @@ public class CamelJBangMain implements Callable<Integer> {
                 .addSubcommand("bind", new CommandLine(new Bind(main)))
                 .addSubcommand("pipe", new CommandLine(new Pipe(main)))
                 .addSubcommand("export", new CommandLine(new Export(main)))
-                .addSubcommand("completion", new CommandLine(new Complete(main)));
+                .addSubcommand("completion", new CommandLine(new Complete(main)))
+                .addSubcommand("config", new CommandLine(new ConfigCommand(main))
+                        .addSubcommand("list", new CommandLine(new ConfigList(main)))
+                        .addSubcommand("get", new CommandLine(new ConfigGet(main)))
+                        .addSubcommand("unset", new CommandLine(new ConfigUnset(main)))
+                        .addSubcommand("set", new CommandLine(new ConfigSet(main))));
 
         commandLine.getCommandSpec().versionProvider(() -> {
             CamelCatalog catalog = new DefaultCamelCatalog();
@@ -132,6 +143,7 @@ public class CamelJBangMain implements Callable<Integer> {
             return new String[] { v };
         });
 
+        CommandLineHelper.augmentWithUserConfiguration(commandLine, args);
         int exitCode = commandLine.execute(args);
         System.exit(exitCode);
     }
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java
index e958ff1efa1..f23ae4c83e3 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java
@@ -22,7 +22,7 @@ import picocli.AutoComplete;
 import picocli.CommandLine;
 
 @CommandLine.Command(name = "complete", description = "Generate completion script for bash/zsh")
-class Complete extends CamelCommand {
+public class Complete extends CamelCommand {
 
     public Complete(CamelJBangMain main) {
         super(main);
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Export.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Export.java
index 22d599ff96a..246c3e59647 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Export.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Export.java
@@ -27,7 +27,7 @@ import picocli.CommandLine.Command;
 
 @Command(name = "export",
          description = "Export Camel integration to Spring Boot or Quarkus based project")
-class Export extends ExportBaseCommand {
+public class Export extends ExportBaseCommand {
 
     public Export(CamelJBangMain main) {
         super(main);
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Init.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Init.java
index f5749493983..9eef178bc30 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Init.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Init.java
@@ -42,7 +42,7 @@ import static org.apache.camel.dsl.jbang.core.common.GitHubHelper.asGithubSingle
 import static org.apache.camel.dsl.jbang.core.common.GitHubHelper.fetchGithubUrls;
 
 @Command(name = "init", description = "Creates a new Camel integration")
-class Init extends CamelCommand {
+public class Init extends CamelCommand {
 
     @Parameters(description = "Name of integration file (or a github link)", arity = "1",
                 paramLabel = "<file>", parameterConsumer = FileConsumer.class)
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Pipe.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Pipe.java
index d62a46841bd..cf893843d99 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Pipe.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Pipe.java
@@ -23,7 +23,7 @@ import org.apache.camel.dsl.jbang.core.common.LoggingLevelCompletionCandidates;
 import picocli.CommandLine;
 
 @CommandLine.Command(name = "pipe", description = "Run Camel integration in pipe and filters mode for terminal scripting")
-class Pipe extends CamelCommand {
+public class Pipe extends CamelCommand {
 
     @CommandLine.Parameters(description = "Name of file", arity = "1",
                             paramLabel = "<file>", parameterConsumer = FileConsumer.class)
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
index e76ed202182..f29b01d22c3 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
@@ -76,7 +76,7 @@ import static org.apache.camel.dsl.jbang.core.common.GitHubHelper.asGithubSingle
 import static org.apache.camel.dsl.jbang.core.common.GitHubHelper.fetchGithubUrls;
 
 @Command(name = "run", description = "Run as local Camel integration")
-class Run extends CamelCommand {
+public class Run extends CamelCommand {
 
     public static final String WORK_DIR = ".camel-jbang";
     public static final String RUN_SETTINGS_FILE = "camel-jbang-run.properties";
@@ -640,7 +640,7 @@ class Run extends CamelCommand {
             cmd = StringHelper.after(cmd, "main.CamelJBang ");
         }
         if (cmd == null) {
-            System.err.println("No Camel integration files to run");
+            spec.commandLine().getOut().println("No Camel integration files to run");
             return 1;
         }
         cmd = cmd.replaceFirst("--background=true", "");
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigCommand.java
similarity index 59%
copy from dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java
copy to dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigCommand.java
index e958ff1efa1..7a76619aeb1 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigCommand.java
@@ -14,33 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.dsl.jbang.core.commands;
+package org.apache.camel.dsl.jbang.core.commands.config;
 
-import java.io.PrintStream;
-
-import picocli.AutoComplete;
+import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import picocli.CommandLine;
 
-@CommandLine.Command(name = "complete", description = "Generate completion script for bash/zsh")
-class Complete extends CamelCommand {
+@CommandLine.Command(name = "config", description = "User configuration")
+public class ConfigCommand extends CamelCommand {
 
-    public Complete(CamelJBangMain main) {
+    public ConfigCommand(CamelJBangMain main) {
         super(main);
     }
 
     @Override
     public Integer call() throws Exception {
-        String script = AutoComplete.bash(
-                spec.parent().name(),
-                spec.parent().commandLine());
-
-        // not PrintWriter.println: scripts with Windows line separators fail in strange
-        // ways!
-        PrintStream out = System.out;
-        out.print(script);
-        out.print('\n');
-        out.flush();
+        // defaults to list
+        new CommandLine(new ConfigList(getMain())).execute();
         return 0;
     }
-
 }
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigGet.java
similarity index 53%
copy from dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java
copy to dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigGet.java
index e958ff1efa1..5dc07d391a2 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigGet.java
@@ -14,33 +14,34 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.dsl.jbang.core.commands;
+package org.apache.camel.dsl.jbang.core.commands.config;
 
-import java.io.PrintStream;
-
-import picocli.AutoComplete;
+import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
 import picocli.CommandLine;
 
-@CommandLine.Command(name = "complete", description = "Generate completion script for bash/zsh")
-class Complete extends CamelCommand {
+@CommandLine.Command(name = "get",
+                     description = "Display user configuration value")
+public class ConfigGet extends CamelCommand {
+
+    @CommandLine.Parameters(description = "Configuration key", arity = "1")
+    private String key;
 
-    public Complete(CamelJBangMain main) {
+    public ConfigGet(CamelJBangMain main) {
         super(main);
     }
 
     @Override
     public Integer call() throws Exception {
-        String script = AutoComplete.bash(
-                spec.parent().name(),
-                spec.parent().commandLine());
+        CommandLineHelper.loadProperties(properties -> {
+            if (properties.containsKey(key)) {
+                System.out.println(properties.getProperty(key));
+            } else {
+                System.out.println(key + " key not found");
+            }
+        });
 
-        // not PrintWriter.println: scripts with Windows line separators fail in strange
-        // ways!
-        PrintStream out = System.out;
-        out.print(script);
-        out.print('\n');
-        out.flush();
         return 0;
     }
-
 }
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigList.java
similarity index 57%
copy from dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java
copy to dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigList.java
index e958ff1efa1..717b532192e 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigList.java
@@ -14,33 +14,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.dsl.jbang.core.commands;
+package org.apache.camel.dsl.jbang.core.commands.config;
 
-import java.io.PrintStream;
-
-import picocli.AutoComplete;
+import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
 import picocli.CommandLine;
 
-@CommandLine.Command(name = "complete", description = "Generate completion script for bash/zsh")
-class Complete extends CamelCommand {
+@CommandLine.Command(name = "list", description = "Displays user configuration")
+public class ConfigList extends CamelCommand {
 
-    public Complete(CamelJBangMain main) {
+    public ConfigList(CamelJBangMain main) {
         super(main);
     }
 
     @Override
     public Integer call() throws Exception {
-        String script = AutoComplete.bash(
-                spec.parent().name(),
-                spec.parent().commandLine());
-
-        // not PrintWriter.println: scripts with Windows line separators fail in strange
-        // ways!
-        PrintStream out = System.out;
-        out.print(script);
-        out.print('\n');
-        out.flush();
+        CommandLineHelper
+                .loadProperties(p -> {
+                    for (String k : p.stringPropertyNames()) {
+                        String v = p.getProperty(k);
+                        System.out.printf("%s = %s%n", k, v);
+                    }
+                });
         return 0;
     }
-
 }
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigSet.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigSet.java
new file mode 100644
index 00000000000..d7893c80154
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigSet.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.dsl.jbang.core.commands.config;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
+import org.apache.camel.util.StringHelper;
+import picocli.CommandLine;
+
+@CommandLine.Command(name = "set",
+                     description = "Set user configuration value")
+public class ConfigSet extends CamelCommand {
+
+    @CommandLine.Parameters(description = "Configuration parameter (ex. key=value)", arity = "1")
+    private String configuration;
+
+    public ConfigSet(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer call() throws Exception {
+        CommandLineHelper.createPropertyFile();
+
+        if (configuration.split("=").length == 1) {
+            System.out.println("Configuration parameter not in key=value format");
+            return 1;
+        }
+
+        CommandLineHelper.loadProperties(properties -> {
+            String key = StringHelper.before(configuration, "=").trim();
+            String value = StringHelper.after(configuration, "=").trim();
+            properties.put(key, value);
+            CommandLineHelper.storeProperties(properties);
+        });
+
+        return 0;
+    }
+}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigUnset.java
similarity index 57%
copy from dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java
copy to dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigUnset.java
index e958ff1efa1..de229b41429 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Complete.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/config/ConfigUnset.java
@@ -14,33 +14,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.dsl.jbang.core.commands;
+package org.apache.camel.dsl.jbang.core.commands.config;
 
-import java.io.PrintStream;
-
-import picocli.AutoComplete;
+import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
 import picocli.CommandLine;
 
-@CommandLine.Command(name = "complete", description = "Generate completion script for bash/zsh")
-class Complete extends CamelCommand {
+@CommandLine.Command(name = "unset",
+                     description = "Remove user configuration value")
+public class ConfigUnset extends CamelCommand {
+
+    @CommandLine.Parameters(description = "Configuration key", arity = "1")
+    private String key;
 
-    public Complete(CamelJBangMain main) {
+    public ConfigUnset(CamelJBangMain main) {
         super(main);
     }
 
     @Override
     public Integer call() throws Exception {
-        String script = AutoComplete.bash(
-                spec.parent().name(),
-                spec.parent().commandLine());
+        CommandLineHelper.loadProperties(properties -> {
+            properties.remove(key);
+            CommandLineHelper.storeProperties(properties);
+        });
 
-        // not PrintWriter.println: scripts with Windows line separators fail in strange
-        // ways!
-        PrintStream out = System.out;
-        out.print(script);
-        out.print('\n');
-        out.flush();
         return 0;
     }
-
 }
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
new file mode 100644
index 00000000000..4bb8dc73ca8
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CommandLineHelper.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.dsl.jbang.core.common;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.function.Consumer;
+
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.OrderedProperties;
+import picocli.CommandLine;
+
+/**
+ * Helper for CLI command line.
+ */
+public final class CommandLineHelper {
+
+    public static final String USER_CONFIG = ".camel-jbang-user.properties";
+
+    private CommandLineHelper() {
+        super();
+    }
+
+    public static void augmentWithUserConfiguration(CommandLine commandLine, String... args) {
+        File file = new File(System.getProperty("user.home"), USER_CONFIG);
+        if (file.isFile() && file.exists()) {
+            commandLine.setDefaultValueProvider(new CamelUserConfigDefaultValueProvider(file));
+        }
+    }
+
+    public static void createPropertyFile() throws IOException {
+        File file = new File(System.getProperty("user.home"), CommandLineHelper.USER_CONFIG);
+        if (!file.exists()) {
+            file.createNewFile();
+        }
+    }
+
+    public static void loadProperties(Consumer<Properties> consumer) {
+        File file = new File(System.getProperty("user.home"), CommandLineHelper.USER_CONFIG);
+        if (file.isFile() && file.exists()) {
+            FileInputStream fis = null;
+            try {
+                fis = new FileInputStream(file);
+                Properties prop = new OrderedProperties();
+                prop.load(fis);
+                consumer.accept(prop);
+            } catch (Exception e) {
+                throw new RuntimeException("Cannot load user configuration: " + file);
+            } finally {
+                IOHelper.close(fis);
+            }
+        }
+    }
+
+    public static void storeProperties(Properties properties) {
+        File file = new File(System.getProperty("user.home"), CommandLineHelper.USER_CONFIG);
+        if (file.isFile() && file.exists()) {
+            try (FileOutputStream fos = new FileOutputStream(file)) {
+                properties.store(fos, null);
+            } catch (IOException ex) {
+                throw new RuntimeException(ex);
+            }
+        } else {
+            System.out.println(CommandLineHelper.USER_CONFIG + " does not exists");
+        }
+    }
+
+    private static class CamelUserConfigDefaultValueProvider extends CommandLine.PropertiesDefaultProvider {
+
+        public CamelUserConfigDefaultValueProvider(File file) {
+            super(file);
+        }
+
+        @Override
+        public String defaultValue(CommandLine.Model.ArgSpec arg) throws Exception {
+            String value = super.defaultValue(arg);
+            if (value != null) {
+                if (arg instanceof CommandLine.Model.OptionSpec) {
+                    // TODO: capture these default values that are in use
+                    // and find a way to log them only once (and have an option to turn this off)
+                    CommandLine.Model.OptionSpec os = (CommandLine.Model.OptionSpec) arg;
+                    String k = os.longestName();
+                    System.out.println(k + "=" + value);
+                }
+            }
+            return value;
+        }
+    }
+
+}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/BackgroundTest.java
similarity index 84%
copy from dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
copy to dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/BackgroundTest.java
index 2dd2531aa54..95171d87a8a 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/BackgroundTest.java
@@ -14,12 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.dsl.jbang.core.commands;
+package org.apache.camel.dsl.jbang;
 
-import java.util.concurrent.Callable;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 
-import org.apache.camel.catalog.CamelCatalog;
-import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.dsl.jbang.core.commands.Bind;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.commands.CodeGenerator;
+import org.apache.camel.dsl.jbang.core.commands.CodeRestGenerator;
+import org.apache.camel.dsl.jbang.core.commands.Complete;
+import org.apache.camel.dsl.jbang.core.commands.DependencyCommand;
+import org.apache.camel.dsl.jbang.core.commands.DependencyCopy;
+import org.apache.camel.dsl.jbang.core.commands.DependencyList;
+import org.apache.camel.dsl.jbang.core.commands.Export;
+import org.apache.camel.dsl.jbang.core.commands.Init;
+import org.apache.camel.dsl.jbang.core.commands.Pipe;
+import org.apache.camel.dsl.jbang.core.commands.Run;
 import org.apache.camel.dsl.jbang.core.commands.action.CamelAction;
 import org.apache.camel.dsl.jbang.core.commands.action.CamelGCAction;
 import org.apache.camel.dsl.jbang.core.commands.action.CamelLogAction;
@@ -62,16 +73,18 @@ import org.apache.camel.dsl.jbang.core.commands.process.ListProcess;
 import org.apache.camel.dsl.jbang.core.commands.process.ListService;
 import org.apache.camel.dsl.jbang.core.commands.process.ListVault;
 import org.apache.camel.dsl.jbang.core.commands.process.StopProcess;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
 import picocli.CommandLine;
-import picocli.CommandLine.Command;
 
-@Command(name = "camel", description = "Apache Camel CLI", mixinStandardHelpOptions = true)
-public class CamelJBangMain implements Callable<Integer> {
-    private static CommandLine commandLine;
+@Disabled
+public class BackgroundTest {
 
-    public static void run(String... args) {
+    @Test
+    public void test() {
         CamelJBangMain main = new CamelJBangMain();
-        commandLine = new CommandLine(main)
+        CommandLine commandLine = new CommandLine(main)
                 .addSubcommand("init", new CommandLine(new Init(main)))
                 .addSubcommand("run", new CommandLine(new Run(main)))
                 .addSubcommand("log", new CommandLine(new CamelLogAction(main)))
@@ -126,20 +139,14 @@ public class CamelJBangMain implements Callable<Integer> {
                 .addSubcommand("export", new CommandLine(new Export(main)))
                 .addSubcommand("completion", new CommandLine(new Complete(main)));
 
-        commandLine.getCommandSpec().versionProvider(() -> {
-            CamelCatalog catalog = new DefaultCamelCatalog();
-            String v = catalog.getCatalogVersion();
-            return new String[] { v };
-        });
+        StringWriter sw = new StringWriter();
+        commandLine.setOut(new PrintWriter(sw));
 
-        int exitCode = commandLine.execute(args);
-        System.exit(exitCode);
-    }
+        int exitCode = commandLine.execute("init", "test.yaml", "--directory=target/data");
+        Assertions.assertThat(exitCode).isEqualTo(0);
 
-    @Override
-    public Integer call() throws Exception {
-        commandLine.execute("--help");
-        return 0;
+        exitCode = commandLine.execute("run", "target/data/test.yaml", "--background");
+        Assertions.assertThat(exitCode).isEqualTo(0);
+        Assertions.assertThat(sw.toString()).doesNotContain("No Camel integration files to run");
     }
-
 }