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));

Reply via email to