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 5af9b31e6911 CAMEL-26440: camel-jbang - A command of a plugin creates
all the commands of the CLI and of the other plugins (#28774)
5af9b31e6911 is described below
commit 5af9b31e69110b07f73c9bed1d7f9a99e541a348
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Oct 31 22:00:20 2026 +0100
CAMEL-26440: camel-jbang - A command of a plugin creates all the commands
of the CLI and of the other plugins (#28774)
Signed-off-by: Claus Ibsen <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 3 +-
.../dsl/jbang/core/commands/CamelJBangMain.java | 62 +++++++++++-----
.../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, 240 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 33e287c98cab..881333655347 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
@@ -881,7 +881,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..fbda96bdf959 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,30 @@ 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 CLI
created none of its own commands for it (the
+ * command is not a command of the CLI, but of a plugin, see {@link
#isPluginCommand(String)}), so the command line
+ * 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 +547,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".