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

davsclaus pushed a commit to branch CAMEL-26440-lazy-plugins
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 2dc9d87a51b35e3e45b1580f09d62514743ef33b
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Oct 31 19:41:08 2026 +0100

    CAMEL-26440: camel-jbang - A command of a plugin creates all the commands 
of the CLI and of the other plugins
    
    A command of a plugin creates no command of the CLI, and only that plugin. 
The
    help of a command creates only that command; the banner of the plugins that 
are
    not installed is printed by camel and camel --help only. The shell panel of
    camel tui makes its own command line with all commands.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |  3 +-
 .../dsl/jbang/core/commands/CamelJBangMain.java    | 61 +++++++++++-----
 .../camel/dsl/jbang/core/common/PluginHelper.java  | 26 +++++++
 .../commands/CamelJBangMainOnlyCommandTest.java    | 85 ++++++++++++++++++----
 .../dsl/jbang/core/common/PluginHelperTest.java    | 25 +++++++
 .../dsl/jbang/core/commands/tui/ShellPanel.java    |  9 ++-
 .../dsl/jbang/launcher/CamelLauncherMain.java      | 37 +++++++---
 .../dsl/jbang/launcher/CamelLauncherTest.java      | 41 +++++++++++
 8 files changed, 239 insertions(+), 48 deletions(-)

diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 0ff788fe9f49..2c76744d50f6 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -872,7 +872,8 @@ A script that checks the exit code of a command must take 
these changes into acc
 
 Errors, warnings and tips are printed on stderr instead of stdout, so stdout 
only has the output of the command, such
 as JSON. If a script reads the error text of a command from stdout, read 
stderr instead. The list of plugins that are
-not installed, after `camel --help`, is printed on stderr, and only in an 
interactive terminal.
+not installed, after `camel` and `camel --help` (no longer after the help of a 
command, such as `camel run --help`),
+is printed on stderr, and only in an interactive terminal.
 
 === camel-pqc - parameterSpec accepts MAYO and SNOVA, and the FIPS advisory no 
longer flags two aliases
 
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 655ba828fe4e..be75a82fac10 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
@@ -120,9 +120,14 @@ public class CamelJBangMain implements Callable<Integer> {
         // classes) costs more than most commands take to run
         String only = onlyCommand(args);
         commandLine = createCommandLine(only);
+        boolean pluginCommand = false;
         if (only != null && !commandLine.getSubcommands().containsKey(only)) {
-            // not a command of the CLI (such as the command of a plugin, or a 
typo, for which picocli suggests others)
-            commandLine = createCommandLine(null);
+            // the command of a plugin needs none of the commands of the CLI, 
anything else (such as a typo, for which
+            // picocli suggests other commands) needs all of them
+            pluginCommand = isPluginCommand(only);
+            if (!pluginCommand) {
+                commandLine = createCommandLine(null);
+            }
         }
 
         postAddCommands(commandLine, args);
@@ -138,6 +143,11 @@ public class CamelJBangMain implements Callable<Integer> {
                 return;
             }
         }
+        if (pluginCommand && !commandLine.getSubcommands().containsKey(only)) {
+            // no plugin added the command: all the commands of the CLI, for 
the commands that picocli suggests
+            commandLine = createCommandLine(null);
+            postAddCommands(commandLine, args);
+        }
         withErrorHandling(commandLine);
 
         commandLine.getCommandSpec().versionProvider(() -> {
@@ -164,9 +174,10 @@ public class CamelJBangMain implements Callable<Integer> {
     }
 
     /**
-     * The command of the CLI that a command line runs, when the CLI needs to 
create only that command: not for the
-     * help, a command line that starts with an option (such as {@code 
--version}), or the commands that use the other
-     * commands ({@link #FULL_COMMANDS}) or the plugins ({@link 
PluginHelper#isPluginConsumingCommand(String)}).
+     * The command that a command line runs, when the CLI needs to create only 
that command (of the CLI, or of a
+     * plugin): not for {@code camel} and a command line that starts with an 
option (such as {@code --help}), which list
+     * all commands, or the commands that use the other commands ({@link 
#FULL_COMMANDS}) or the plugins
+     * ({@link PluginHelper#isPluginConsumingCommand(String)}).
      *
      * @param  args the command line
      * @return      the name of the command, or {@code null} to create all 
commands
@@ -180,12 +191,6 @@ public class CamelJBangMain implements Callable<Integer> {
                 || PluginHelper.isPluginConsumingCommand(target)) {
             return null;
         }
-        for (String arg : args) {
-            if ("--help".equals(arg) || "-h".equals(arg)) {
-                // the help lists the plugins that are not installed, from all 
commands
-                return null;
-            }
-        }
         return target;
     }
 
@@ -202,6 +207,29 @@ public class CamelJBangMain implements Callable<Integer> {
         return only != null && commandLine.getSubcommands().size() == 1 && 
commandLine.getSubcommands().containsKey(only);
     }
 
+    /**
+     * The command of the plugin that the command line runs, when the command 
line has none of the commands of the CLI
+     * (see {@link #isPluginCommand(String)}), so it needs only that plugin.
+     *
+     * @param  commandLine the command line
+     * @param  args        the arguments it runs
+     * @return             the name of the command, or {@code null} when the 
command line needs all plugins
+     */
+    public static String onlyPluginCommand(CommandLine commandLine, String... 
args) {
+        String only = onlyCommand(args);
+        return only != null && commandLine.getSubcommands().isEmpty() ? only : 
null;
+    }
+
+    /**
+     * Whether the command is the command of a plugin, which needs none of the 
commands of the CLI.
+     *
+     * @param  command the name of the command
+     * @return         {@code true} for the command of a plugin that the CLI 
can add
+     */
+    protected boolean isPluginCommand(String command) {
+        return discoverPlugins && PluginHelper.isPluginCommand(command);
+    }
+
     private static boolean shouldCreate(String only, String name) {
         return only == null || only.equals(name);
     }
@@ -518,16 +546,9 @@ public class CamelJBangMain implements Callable<Integer> {
         return 0;
     }
 
+    // camel and camel --help, which list all commands
     private static boolean isHelpRequest(String[] args) {
-        if (args == null || args.length == 0) {
-            return true;
-        }
-        for (String arg : args) {
-            if ("--help".equals(arg) || "-h".equals(arg)) {
-                return true;
-            }
-        }
-        return false;
+        return args == null || args.length == 0 || "--help".equals(args[0]) || 
"-h".equals(args[0]);
     }
 
     void printAvailablePlugins() {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/PluginHelper.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/PluginHelper.java
index cf05692f8090..a7abe8630526 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/PluginHelper.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/PluginHelper.java
@@ -25,6 +25,7 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardOpenOption;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -125,6 +126,31 @@ public final class PluginHelper {
         return PLUGIN_CONSUMING_BUILTINS.contains(command);
     }
 
+    /**
+     * Whether the command is the command of a plugin: of a plugin of Camel 
(which is installed on first use), of a
+     * plugin in the plugin configuration, or of a plugin embedded in the 
classpath.
+     *
+     * @param  command the name of the command
+     * @return         {@code true} when a plugin adds the command
+     */
+    public static boolean isPluginCommand(String command) {
+        if (Arrays.stream(PluginType.values()).anyMatch(p -> 
p.getCommand().equals(command))) {
+            return true;
+        }
+        JsonObject config = getPluginConfig();
+        if (config != null) {
+            JsonObject plugins = config.getMap("plugins");
+            for (String pluginKey : plugins.keySet()) {
+                JsonObject properties = plugins.getMap(pluginKey);
+                String name = properties.getOrDefault("name", 
pluginKey).toString();
+                if (command.equals(properties.getOrDefault("command", 
name).toString())) {
+                    return true;
+                }
+            }
+        }
+        return 
PluginHelper.class.getClassLoader().getResource(PLUGIN_SERVICE_DIR + 
"camel-jbang-plugin-" + command) != null;
+    }
+
     /**
      * Loads the plugin Json configuration from the user home and goes through 
all configured plugins adding the plugin
      * commands to the current command line. Tries to resolve each plugin from 
the classpath with the factory finder
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMainOnlyCommandTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMainOnlyCommandTest.java
index 39939ab2b169..4d1170f511b9 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMainOnlyCommandTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMainOnlyCommandTest.java
@@ -18,6 +18,7 @@ package org.apache.camel.dsl.jbang.core.commands;
 
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.Callable;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -51,7 +52,27 @@ class CamelJBangMainOnlyCommandTest extends 
CamelCommandBaseTestSupport {
     public void theHelpCreatesAllCommands() {
         assertAllCommands(execute("--help"));
         assertAllCommands(execute());
-        assertAllCommands(execute("config", "--help"));
+    }
+
+    @Test
+    public void theHelpOfACommandCreatesOnlyThatCommand() {
+        CommandLine commandLine = execute("config", "--help");
+
+        assertEquals(Set.of("config"), commandLine.getSubcommands().keySet());
+        assertTrue(CamelJBangMain.hasOnlyCommand(commandLine, "config", 
"--help"));
+    }
+
+    @Test
+    public void aCommandOfAPluginCreatesNoCommandOfTheCli() {
+        CommandLine commandLine = execute(new PluginMain(true), "fake", 
"--help");
+
+        assertEquals(Set.of("fake"), commandLine.getSubcommands().keySet());
+    }
+
+    @Test
+    public void aCommandOfAPluginThatAddsNoCommandCreatesAllCommands() {
+        // for the commands that picocli suggests
+        assertAllCommands(execute(new PluginMain(false), "fake"));
     }
 
     @Test
@@ -70,6 +91,8 @@ class CamelJBangMainOnlyCommandTest extends 
CamelCommandBaseTestSupport {
         }
         assertNull(CamelJBangMain.onlyCommand("--verbose", "version"));
         assertEquals("version", CamelJBangMain.onlyCommand("version", "get"));
+        assertEquals("version", CamelJBangMain.onlyCommand("version", 
"--help"));
+        assertNull(CamelJBangMain.onlyCommand("--help"));
     }
 
     @Test
@@ -81,21 +104,57 @@ class CamelJBangMainOnlyCommandTest extends 
CamelCommandBaseTestSupport {
     }
 
     private CommandLine execute(String... args) {
-        CommandLine[] executed = new CommandLine[1];
-        CamelJBangMain main = new CamelJBangMain() {
-            @Override
-            public void postExecute(CommandLine commandLine, String[] args, 
int exitCode) {
-                executed[0] = commandLine;
-            }
+        return execute(new TestMain(), args);
+    }
 
-            @Override
-            public void quit(int exitCode) {
-                // noop
-            }
-        }.withPrinter(printer);
+    private CommandLine execute(TestMain main, String... args) {
+        main.withPrinter(printer);
         main.setDiscoverPlugins(false);
         main.execute(args);
-        return executed[0];
+        return main.executed;
+    }
+
+    private static class TestMain extends CamelJBangMain {
+        CommandLine executed;
+
+        @Override
+        public void postExecute(CommandLine commandLine, String[] args, int 
exitCode) {
+            executed = commandLine;
+        }
+
+        @Override
+        public void quit(int exitCode) {
+            // noop
+        }
+    }
+
+    // a main with the plugin of the command fake
+    private static class PluginMain extends TestMain {
+        private final boolean addsCommand;
+
+        PluginMain(boolean addsCommand) {
+            this.addsCommand = addsCommand;
+        }
+
+        @Override
+        protected boolean isPluginCommand(String command) {
+            return "fake".equals(command);
+        }
+
+        @Override
+        public void postAddCommands(CommandLine commandLine, String[] args) {
+            if (addsCommand && "fake".equals(onlyPluginCommand(commandLine, 
args))) {
+                commandLine.addSubcommand("fake", new CommandLine(new 
FakeCommand()));
+            }
+        }
+    }
+
+    @CommandLine.Command(name = "fake", description = "The command of a 
plugin")
+    static class FakeCommand implements Callable<Integer> {
+        @Override
+        public Integer call() {
+            return 0;
+        }
     }
 
     private static void assertAllCommands(CommandLine commandLine) {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/PluginHelperTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/PluginHelperTest.java
index e4e49fb17071..0b4682d0be36 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/PluginHelperTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/PluginHelperTest.java
@@ -61,6 +61,31 @@ public class PluginHelperTest {
         assertDoesNotThrow(PluginHelper::hasEmbeddedPlugins);
     }
 
+    @Test
+    public void aPluginCommandIsOfACamelPluginOrOfTheConfiguration() throws 
Exception {
+        Path userConfig = 
CommandLineHelper.getHomeDir().resolve(PluginHelper.PLUGIN_CONFIG);
+        Files.writeString(userConfig, """
+                {
+                  "plugins": {
+                    "forage": {
+                      "name": "forage",
+                      "command": "forage",
+                      "description": "Forage plugin",
+                      "firstVersion": "4.18.0"
+                    }
+                  }
+                }
+                """, StandardOpenOption.CREATE);
+
+        // installed on first use
+        assertTrue(PluginHelper.isPluginCommand("tui"));
+        assertTrue(PluginHelper.isPluginCommand("kubernetes"));
+        // in the plugin configuration
+        assertTrue(PluginHelper.isPluginCommand("forage"));
+        assertFalse(PluginHelper.isPluginCommand("version"));
+        assertFalse(PluginHelper.isPluginCommand("confg"));
+    }
+
     @Test
     public void testFallbackToJsonConfig() throws Exception {
         // Create a user plugin config file in home directory
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ShellPanel.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ShellPanel.java
index 5fdf488a61a5..4c7c20c9eb0c 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ShellPanel.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ShellPanel.java
@@ -477,7 +477,8 @@ class ShellPanel {
                     screenTerminal, StandardCharsets.UTF_8, feedbackOutput);
 
             LineDisciplineTerminal terminal = virtualTerminal;
-            shellThread = new Thread(() -> runShell(terminal, commandLine), 
"tui-shell");
+            CamelJBangMain main = (CamelJBangMain) commandLine.getCommand();
+            shellThread = new Thread(() -> runShell(terminal, main), 
"tui-shell");
             shellThread.setDaemon(true);
             shellThread.start();
         } catch (Exception e) {
@@ -487,12 +488,12 @@ class ShellPanel {
         }
     }
 
-    private void runShell(LineDisciplineTerminal terminal, CommandLine 
commandLine) {
+    private void runShell(LineDisciplineTerminal terminal, CamelJBangMain 
main) {
         try {
-            PicocliCommandRegistry registry = new 
CamelShellCommandRegistry(commandLine);
+            // all the commands of the CLI and the plugins, as the CLI created 
only the command it ran (camel tui)
+            PicocliCommandRegistry registry = new 
CamelShellCommandRegistry(main.newCommandLine());
             // Redirect command output (printer()) through the virtual terminal
             // so it renders in the shell panel instead of the TUI's real 
terminal
-            CamelJBangMain main = (CamelJBangMain) commandLine.getCommand();
             Printer originalPrinter = main.getOut();
             Printer terminalPrinter = new Printer() {
                 @Override
diff --git 
a/dsl/camel-jbang/camel-launcher/src/main/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherMain.java
 
b/dsl/camel-jbang/camel-launcher/src/main/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherMain.java
index 2ab0a379fc67..187a5d6b8c15 100644
--- 
a/dsl/camel-jbang/camel-launcher/src/main/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherMain.java
+++ 
b/dsl/camel-jbang/camel-launcher/src/main/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherMain.java
@@ -17,6 +17,8 @@
 package org.apache.camel.dsl.jbang.launcher;
 
 import java.util.List;
+import java.util.Set;
+import java.util.function.Supplier;
 
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.commands.generate.GeneratePlugin;
@@ -34,17 +36,36 @@ import picocli.CommandLine;
  */
 public class CamelLauncherMain extends CamelJBangMain {
 
+    // the plugins embedded in the launcher, with the commands they add
+    private record EmbeddedPlugin(Supplier<Plugin> factory, Set<String> 
commands) {
+    }
+
+    private static final List<EmbeddedPlugin> PLUGINS = List.of(
+            new EmbeddedPlugin(GeneratePlugin::new, Set.of("generate")),
+            new EmbeddedPlugin(KubernetesPlugin::new, Set.of("kubernetes", 
"k8s")),
+            new EmbeddedPlugin(SelfUpdatePlugin::new, Set.of("self-update")),
+            new EmbeddedPlugin(TuiPlugin::new, Set.of("tui")),
+            new EmbeddedPlugin(ValidatePlugin::new, Set.of("validate")),
+            new EmbeddedPlugin(TestPlugin::new, Set.of("test")));
+
     @Override
     public void preExecute(CommandLine commandLine, String[] args) {
         UpdateChecker.maybeNotify(args);
     }
 
+    @Override
+    protected boolean isPluginCommand(String command) {
+        return PLUGINS.stream().anyMatch(p -> p.commands().contains(command)) 
|| super.isPluginCommand(command);
+    }
+
     @Override
     public void postAddCommands(CommandLine commandLine, String[] args) {
         if (hasOnlyCommand(commandLine, args)) {
             // a command of the CLI, which needs none of the commands of the 
plugins
             return;
         }
+        // a command of a plugin needs only that plugin
+        String only = onlyPluginCommand(commandLine, args);
         // Install embedded plugins. Each plugin must be given the classloader 
that loaded it (this
         // class's classloader, i.e. the fat-jar classloader) so that plugins 
relying on ServiceLoader
         // discovery can see the dependencies bundled in the launcher. The TUI 
plugin installs this
@@ -52,16 +73,12 @@ public class CamelLauncherMain extends CamelJBangMain {
         // without it the discovery falls back to the system classloader, 
which cannot see the nested
         // BOOT-INF/lib jars of the Spring Boot fat-jar, and the TUI fails 
with "No BackendProvider found".
         ClassLoader classLoader = getClass().getClassLoader();
-        List<Plugin> plugins = List.of(
-                new GeneratePlugin(),
-                new KubernetesPlugin(),
-                new SelfUpdatePlugin(),
-                new TuiPlugin(),
-                new ValidatePlugin(),
-                new TestPlugin());
-        for (Plugin plugin : plugins) {
-            plugin.setClassLoader(classLoader);
-            plugin.customize(commandLine, this);
+        for (EmbeddedPlugin embedded : PLUGINS) {
+            if (only == null || embedded.commands().contains(only)) {
+                Plugin plugin = embedded.factory().get();
+                plugin.setClassLoader(classLoader);
+                plugin.customize(commandLine, this);
+            }
         }
     }
 
diff --git 
a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherTest.java
 
b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherTest.java
index c8eebea27c80..2c979026a64d 100644
--- 
a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherTest.java
+++ 
b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherTest.java
@@ -19,6 +19,9 @@ package org.apache.camel.dsl.jbang.launcher;
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.camel.dsl.jbang.run.common.Printer;
@@ -27,6 +30,7 @@ import org.junit.jupiter.api.Test;
 import picocli.CommandLine;
 
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -91,6 +95,43 @@ public class CamelLauncherTest {
         assertTrue(output.contains("Camel CLI version:"), "Output should 
contain version information");
     }
 
+    @Test
+    public void aCommandOfAnEmbeddedPluginCreatesOnlyThatPlugin() {
+        // creating all the commands of the CLI and the plugins costs more 
than half a second
+        assertEquals(Set.of("generate"), commandsOf("generate"));
+        assertEquals(Set.of("kubernetes", "k8s"), commandsOf("k8s", "--help"));
+        assertEquals(Set.of("version"), commandsOf("version"));
+    }
+
+    @Test
+    public void theHelpCreatesTheCommandsOfAllEmbeddedPlugins() {
+        assertTrue(commandsOf("--help").containsAll(Set.of("version", 
"generate", "kubernetes", "self-update", "tui")));
+    }
+
+    private static Set<String> commandsOf(String... args) {
+        List<CommandLine> executed = new ArrayList<>();
+        CamelLauncherMain main = new CamelLauncherMain() {
+            @Override
+            public void preExecute(CommandLine commandLine, String[] args) {
+                // no update check
+            }
+
+            @Override
+            public void postExecute(CommandLine commandLine, String[] args, 
int exitCode) {
+                executed.add(commandLine);
+            }
+
+            @Override
+            public void quit(int exitCode) {
+                // Do nothing to prevent System.exit
+            }
+        };
+        main.setDiscoverPlugins(false);
+        main.setOut(new PrintStreamPrinter(new PrintStream(new 
ByteArrayOutputStream())));
+        main.execute(args);
+        return executed.get(0).getSubcommands().keySet();
+    }
+
     @Test
     public void testEmbeddedTuiPluginReceivesClassLoader() throws Exception {
         // Regression for the launcher TUI failure "No BackendProvider found 
on classpath".

Reply via email to