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 35d5e989ae3d CAMEL-25830: camel-jbang - camel run of a whole directory
without --name is named after the directory (#28122)
35d5e989ae3d is described below
commit 35d5e989ae3d27f71cbe0ba9eed5534e4d0240c9
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Oct 19 21:58:15 2026 +0200
CAMEL-25830: camel-jbang - camel run of a whole directory without --name is
named after the directory (#28122)
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 8 +++++
.../pages/jbang-commands/camel-jbang-debug.adoc | 2 +-
.../ROOT/pages/jbang-commands/camel-jbang-dev.adoc | 2 +-
.../ROOT/pages/jbang-commands/camel-jbang-run.adoc | 2 +-
.../META-INF/camel-jbang-commands-metadata.json | 6 ++--
.../apache/camel/dsl/jbang/core/commands/Run.java | 37 +++++++++++++++++++++-
.../camel/dsl/jbang/core/commands/RunTest.java | 18 +++++++++++
7 files changed, 68 insertions(+), 7 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 3299d0902c77..e941b09b63f7 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
@@ -1673,6 +1673,14 @@ failures of the route go. A highlighted path that steps
into an `onException` cl
The `<camel-topology-diagram>` web component no longer draws the error paths
of the route topology as calls between
routes. With its new `errors` attribute it draws the routes reached only on
error in a frame below the other routes.
+=== camel-jbang - the default name of a run of a whole directory
+
+`camel run --source-dir`, and `camel run` of a directory, without `--name`
name the integration after the directory.
+A folder of a project layout (`src`, `main`, `resources`, `java`, `camel` or
`routes`) gives way to the first parent
+with another name, and `.` is the working directory. Before, the name was the
one of the first file, in the order that
+the file system lists them, which could be any file, such as a Java class that
a route uses. `--name`, and
+`camel.main.name` in application.properties, still win.
+
=== camel-jbang - route-diagram, route-structure and route-topology read Java
sources without compiling them
Given Java source files, `camel cmd route-diagram`, `camel cmd
route-structure` and `camel cmd route-topology` (and the
diff --git
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
index e6118b087330..75127cb9001f 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
@@ -67,7 +67,7 @@ camel debug [options]
| `--mcp-tags` | Comma-separated ai-tool tags to expose on the ai-tool MCP
server (maps to camel.server.mcp-tags). Use with --mcp-stdio or when
camel.server.mcp-enabled is configured for HTTP transport. Not used by --mcp
(dev/diagnostics MCP). | | String
| `--metrics` _(deprecated)_ | Deprecated: use --observe instead. Metrics
(Micrometer and Prometheus) at /q/metrics on local HTTP server (port 8080 by
default) | false | boolean
| `--modeline` | Whether to support JBang style //DEPS to specify additional
dependencies | true | boolean
-| `--name` | The name of the Camel application | CamelJBang | String
+| `--name` | The name of the Camel application. By default the name of the
directory when running a whole directory (--source-dir, or a directory), else
the name of the first file | CamelJBang | String
| `--observe` | Enable observability services (health, metrics, dev console,
and lightweight Camel-only tracing in the TUI Spans tab) | false | boolean
| `--open-api` | Adds an OpenAPI spec from the given file (json or yaml file)
| | String
| `--open-telemetry-agent` | Enable OpenTelemetry Java Agent for
auto-instrumentation of third-party libraries (HTTP clients, JDBC, Kafka
clients, gRPC, etc.). Traces are shown in the TUI Spans tab with Camel spans in
cyan and 3rd-party agent spans in magenta. | false | boolean
diff --git
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc
index 705c8bae7259..021bf53e0e87 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-dev.adoc
@@ -63,7 +63,7 @@ camel dev [options]
| `--mcp-tags` | Comma-separated ai-tool tags to expose on the ai-tool MCP
server (maps to camel.server.mcp-tags). Use with --mcp-stdio or when
camel.server.mcp-enabled is configured for HTTP transport. Not used by --mcp
(dev/diagnostics MCP). | | String
| `--metrics` _(deprecated)_ | Deprecated: use --observe instead. Metrics
(Micrometer and Prometheus) at /q/metrics on local HTTP server (port 8080 by
default) | false | boolean
| `--modeline` | Whether to support JBang style //DEPS to specify additional
dependencies | true | boolean
-| `--name` | The name of the Camel application | CamelJBang | String
+| `--name` | The name of the Camel application. By default the name of the
directory when running a whole directory (--source-dir, or a directory), else
the name of the first file | CamelJBang | String
| `--observe` | Enable observability services (health, metrics, dev console,
and lightweight Camel-only tracing in the TUI Spans tab) | false | boolean
| `--open-api` | Adds an OpenAPI spec from the given file (json or yaml file)
| | String
| `--open-telemetry-agent` | Enable OpenTelemetry Java Agent for
auto-instrumentation of third-party libraries (HTTP clients, JDBC, Kafka
clients, gRPC, etc.). Traces are shown in the TUI Spans tab with Camel spans in
cyan and 3rd-party agent spans in magenta. | false | boolean
diff --git
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
index 9924d6fc4309..51405a1c31f8 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
@@ -63,7 +63,7 @@ camel run [options]
| `--mcp-tags` | Comma-separated ai-tool tags to expose on the ai-tool MCP
server (maps to camel.server.mcp-tags). Use with --mcp-stdio or when
camel.server.mcp-enabled is configured for HTTP transport. Not used by --mcp
(dev/diagnostics MCP). | | String
| `--metrics` _(deprecated)_ | Deprecated: use --observe instead. Metrics
(Micrometer and Prometheus) at /q/metrics on local HTTP server (port 8080 by
default) | false | boolean
| `--modeline` | Whether to support JBang style //DEPS to specify additional
dependencies | true | boolean
-| `--name` | The name of the Camel application | CamelJBang | String
+| `--name` | The name of the Camel application. By default the name of the
directory when running a whole directory (--source-dir, or a directory), else
the name of the first file | CamelJBang | String
| `--observe` | Enable observability services (health, metrics, dev console,
and lightweight Camel-only tracing in the TUI Spans tab) | false | boolean
| `--open-api` | Adds an OpenAPI spec from the given file (json or yaml file)
| | String
| `--open-telemetry-agent` | Enable OpenTelemetry Java Agent for
auto-instrumentation of third-party libraries (HTTP clients, JDBC, Kafka
clients, gRPC, etc.). Traces are shown in the TUI Spans tab with Camel spans in
cyan and 3rd-party agent spans in magenta. | false | boolean
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 0e2ca16f0d15..e06099383f47 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
@@ -6,9 +6,9 @@
{ "name": "cmd", "fullName": "cmd", "description": "Performs commands in
the running Camel integrations, such as start\/stop route, or change logging
levels.", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.action.CamelAction", "options": [ {
"names": "-h,--help", "description": "Display the help and sub-commands",
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name":
"browse", "fullName": "cmd browse", "description": "Browse pending messages on
endpoints [...]
{ "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": "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": "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": "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 [...]
{ "name": "doctor", "fullName": "doctor", "description": "Checks the
environment and reports potential issues", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.Doctor", "options": [ { "names":
"--json", "description": "Output in JSON Format: the status (OK, WARN or FAIL)
of each check", "javaType": "boolean", "type": "boolean" }, { "names":
"-h,--help", "description": "Display the help and sub-commands", "javaType":
"boolean", "type": "boolean" } ] },
@@ -27,7 +27,7 @@
{ "name": "plugin", "fullName": "plugin", "description": "Manage plugins
that add sub-commands to this CLI", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.plugin.PluginCommand", "options": [ {
"names": "-h,--help", "description": "Display the help and sub-commands",
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "add",
"fullName": "plugin add", "description": "Add new plugin", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.plugin.PluginA [...]
{ "name": "ps", "fullName": "ps", "description": "List running Camel
integrations", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.process.ListProcess", "options": [ {
"names": "--duration", "description": "How long to keep watching or following,
in millis or as a duration such as 30s (default: until Enter is pressed)",
"javaType": "long", "type": "integer" }, { "names": "--json", "description":
"Output in JSON Format", "javaType": "boolean", "type": "boolean" }, { "names":
[...]
{ "name": "restart", "fullName": "restart", "description": "Restarts
running Camel integrations (stop + re-launch)", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.process.RestartProcess", "options": [
{ "names": "--timeout", "description": "How long to wait for the integration to
stop, and again to start, in millis or as a duration such as 30s",
"defaultValue": "60000", "javaType": "long", "type": "integer" }, { "names":
"-h,--help", "description": "Display the help and su [...]
- { "name": "run", "fullName": "run", "description": "Run as local Camel
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Run",
"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 seconds [...]
+ { "name": "run", "fullName": "run", "description": "Run as local Camel
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Run",
"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 seconds [...]
{ "name": "sbom", "fullName": "sbom", "description": "Generate a CycloneDX
or SPDX SBOM for a specific project", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.SBOMGenerator", "options": [ {
"names": "--build-property", "description": "Maven build properties, ex.
--build-property=prop1=foo", "javaType": "java.util.List", "type": "array" }, {
"names": "--camel-spring-boot-version", "description": "Camel version to use
with Spring Boot", "javaType": "java.lang.String", "type" [...]
{ "name": "script", "fullName": "script", "description": "Run Camel
integration as shell script for terminal scripting", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.Script", "options": [ { "names":
"--logging", "description": "Can be used to turn on logging (logs to file in
<user home>\/.camel directory)", "defaultValue": "false", "javaType":
"boolean", "type": "boolean" }, { "names": "--logging-level", "description":
"Logging level (ERROR, WARN, INFO, DEBUG, TRACE)", "d [...]
{ "name": "semantic", "fullName": "semantic", "description": "List
semantic definitions and expert contracts", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.action.CamelSemantic", "options": [ {
"names": "--expert", "description": "Show the operations and parameter contract
of this expert", "javaType": "java.lang.String", "type": "string" }, { "names":
"--json", "description": "Output a single JSON document", "javaType":
"boolean", "type": "boolean" }, { "names": "--timeou [...]
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 a778e6aaef1d..36b5e514b79d 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
@@ -308,7 +308,9 @@ public class Run extends CamelCommand {
description = "Additional JVM arguments (e.g. -Xmx256m -Xms128m)")
String jvmArgs;
- @Option(names = { "--name" }, defaultValue = "CamelJBang", description =
"The name of the Camel application")
+ @Option(names = { "--name" }, defaultValue = "CamelJBang",
+ description = "The name of the Camel application. By default the
name of the directory when running a whole directory"
+ + " (--source-dir, or a directory), else the name of
the first file")
String name;
@Option(names = { "--exclude" }, description = "Exclude files by name or
pattern")
@@ -1176,6 +1178,15 @@ public class Run extends CamelCommand {
}
}
+ if ("CamelJBang".equals(name) && sourceDir != null && !exportRun &&
!transformRun) {
+ // a whole directory is one app, named after the directory
(CAMEL-25830): its first file depends on the
+ // order of the file system, and can be any file, such as a Java
class that a route uses
+ String dirName = directoryName(sourceDir);
+ if (dirName != null) {
+ name = dirName;
+ }
+ }
+
for (String file : files) {
if (file.startsWith("clipboard") &&
!(Files.exists(Paths.get(file)))) {
file = loadFromClipboard(file);
@@ -3056,6 +3067,30 @@ public class Run extends CamelCommand {
}
}
+ /**
+ * The folders of a project layout, whose name says nothing about the app
(CAMEL-25830).
+ */
+ private static final Set<String> LAYOUT_FOLDERS = Set.of("src", "main",
"resources", "java", "camel", "routes");
+
+ /**
+ * The name of an app that is a whole directory: the name of the
directory, or of the first parent with a name that
+ * is not one of the folders of a project layout ({@code
src/main/resources/camel} is named after the project), and
+ * {@code .} is the working directory.
+ *
+ * @return the name, or <tt>null</tt> when the directory and its parents
have no such name
+ */
+ static String directoryName(String dir) {
+ Path path = Paths.get(dir).toAbsolutePath().normalize();
+ while (path != null && path.getFileName() != null) {
+ String answer = path.getFileName().toString();
+ if (!LAYOUT_FOLDERS.contains(answer.toLowerCase(Locale.ROOT))) {
+ return answer;
+ }
+ path = path.getParent();
+ }
+ return null;
+ }
+
protected int runKameletMain(KameletMain main) throws Exception {
Recording jfrRecording = startJfrRecording();
PreviousRun.Keeper previousLog = null;
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunTest.java
index 317c5ca1b55b..84b04bfc7616 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/RunTest.java
@@ -17,11 +17,14 @@
package org.apache.camel.dsl.jbang.core.commands;
+import java.nio.file.Path;
+
import org.apache.camel.dsl.jbang.core.common.RuntimeType;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.main.KameletMain;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
import picocli.CommandLine;
import static org.assertj.core.api.Assertions.assertThat;
@@ -110,6 +113,21 @@ class RunTest extends CamelCommandBaseTestSupport {
assertThat(command.hasNoRoutes(main)).isFalse();
}
+ @Test
+ void aWholeDirectoryIsNamedAfterTheDirectory(@TempDir Path dir) {
+ // CAMEL-25830
+ Path project = dir.resolve("orders-app");
+
assertThat(Run.directoryName(project.toString())).isEqualTo("orders-app");
+ // the folders of a project layout say nothing about the app: the
project does
+
assertThat(Run.directoryName(project.resolve("src/main/resources/camel").toString())).isEqualTo("orders-app");
+
assertThat(Run.directoryName(project.resolve("src/main/java").toString())).isEqualTo("orders-app");
+
assertThat(Run.directoryName(project.resolve("routes").toString())).isEqualTo("orders-app");
+ // the working directory
+
assertThat(Run.directoryName(".")).isEqualTo(Path.of("").toAbsolutePath().getFileName().toString());
+ // no name to give
+ assertThat(Run.directoryName("/src")).isNull();
+ }
+
@Test
public void shouldListExamples() throws Exception {
Run command = new Run(new CamelJBangMain().withPrinter(printer));