This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-25830 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 9ea3161a075ea78a51a52643bf3c5fe773d13e66 Author: Claus Ibsen <[email protected]> AuthorDate: Mon Oct 19 17:12:51 2026 +0200 CAMEL-25830: camel-jbang - camel run of a whole directory without --name is named after the directory It was named after the first file in the order of the file system, which could be any file, such as a Java class. A folder of a project layout (src, main, resources, java, camel, routes) gives way to the first parent with another name. Files that are given keep the name of the first file, and --name and camel.main.name still win. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m --- .../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 8cd5c733d623..34e6ec1d72d9 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 @@ -1787,3 +1787,11 @@ Content with more than one reading is refused as before. to the closest lines of the file when exactly one place matches well. It changes there the lines the model changed and keeps the file's version of the other lines. The edit stands only when its result is valid; otherwise the answer is the one of a miss, with the lines the file has there. `matchedLines` in the answer names the lines it matched. + +=== 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. 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 fcf519c28b5a..1dd87eec1c65 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 c81ca8ef00ee..c453e0a0f82b 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 @@ -305,7 +305,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") @@ -1173,6 +1175,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); @@ -3050,6 +3061,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(); try { 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));
