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 dc2fa1aafb00 CAMEL-26527: camel debug --background bypasses TTY check 
for non-interactive sessions (#29153)
dc2fa1aafb00 is described below

commit dc2fa1aafb004d78c5c5dab969a23cebd291ccf5
Author: Guillaume Nodet <[email protected]>
AuthorDate: Mon Nov 9 11:09:15 2026 +0100

    CAMEL-26527: camel debug --background bypasses TTY check for 
non-interactive sessions (#29153)
    
    * CAMEL-26527: camel debug --background bypasses TTY check for 
non-interactive sessions
    * CAMEL-26527: address review — reject --background for SB/Quarkus paths, 
use runBackgroundProcess in plain mode, reject --background + --remote-attach
    * CAMEL-26527: address review — kind string, happy-path test, doc update
    * CAMEL-26527: address review — fix doc to match runBackgroundProcess 
semantics, add kind assertion to test
---
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |  2 +-
 .../modules/ROOT/pages/camel-jbang-debugging.adoc  | 23 +++++++++++
 .../modules/ROOT/pages/camel-jbang-exit-codes.adoc |  2 +-
 .../camel/dsl/jbang/core/commands/Debug.java       | 41 ++++++++++++++++++-
 .../core/commands/CommandInputErrorsTest.java      | 46 ++++++++++++++++++++++
 .../camel/dsl/jbang/it/CamelDebugITCase.java       |  2 +-
 6 files changed, 111 insertions(+), 5 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 327a8398dd8f..14c0c98d828d 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
@@ -2503,7 +2503,7 @@ Without an interactive terminal, such as in a script, a 
CI job (the `CI` environ
   `--open-url` (the old name still works).
 * `camel debug`, `camel nano`, `camel shell`, `camel edit`, `camel tui` 
(except with `--record`), `camel ask` without a
   question, `camel run --prompt` and `camel get history --it` exit with `2` 
and `requires an interactive terminal` on
-  stderr. `camel debug` used to wait at 100% CPU.
+  stderr. `camel debug` used to wait at 100% CPU. `camel debug --background` 
does not require an interactive terminal.
 
 `--follow` and `--open-url` without a value turn the option on. As boolean 
options that are on by default, they turned
 it off (use `--follow=false` to turn it off). In a terminal, the end of the 
input (Ctrl+D) stops a command that watches
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-debugging.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-debugging.adoc
index 79dc91ec33b1..cda7c972c642 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-debugging.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-debugging.adoc
@@ -72,6 +72,29 @@ camel debug hello.yaml --breakpoint=18
 camel debug hello.yaml --breakpoint=18,34
 ----
 
+=== Running the debugger in background mode
+
+Use `--background` to start the debug-enabled integration without an 
interactive terminal:
+
+[source,bash]
+----
+camel debug hello.yaml --background
+----
+
+This is useful in CI or non-interactive sessions where no TTY is available.
+Unlike the interactive mode, `camel debug --background` does not require a 
terminal:
+it launches the Camel integration with the route debugger enabled, writes its 
output to
+`~/.camel/<pid>-run.log`, and waits up to `--background-wait` (60 seconds by 
default) for
+the integration to start.
+It exits with `0` once the integration is running, with the integration's exit 
code when it
+fails to start, and with `124` when it is still starting at the end of the 
wait.
+
+NOTE: `--background` is not supported for Spring Boot or Quarkus projects.
+      `--background` and `--remote-attach` are mutually exclusive.
+
+Once the integration is running in the background with the route debugger 
enabled, you can
+attach to it from an interactive terminal with `camel debug --remote-attach 
<name>`.
+
 === Spring Boot route debugging
 
 *Available since Camel 4.14*
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-exit-codes.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-exit-codes.adoc
index b3753abb23ec..83b9584a4daa 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-exit-codes.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-exit-codes.adoc
@@ -143,7 +143,7 @@ so the commands do not wait for it:
 * A command that watches (`--watch`) or follows (`camel log`, `camel trace`, 
`camel cmd receive` and
   `camel infra log`) shows its output once. With `--duration`, such as 
`--duration=30s`, it keeps watching or following
   for that long, and then exits with `0`. With `--follow`, it follows until it 
is stopped.
-* A command that only works in a terminal, such as `camel debug`, `camel 
shell` or `camel tui`, exits with `2`.
+* A command that only works in a terminal, such as `camel debug` (except with 
`--background`), `camel shell` or `camel tui`, exits with `2`.
 
 [source,bash]
 ----
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java
index 6f788122dea0..6141251009d3 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Debug.java
@@ -42,6 +42,7 @@ import 
org.apache.camel.dsl.jbang.core.commands.action.MessageTableHelper;
 import org.apache.camel.dsl.jbang.core.common.ActionClient;
 import org.apache.camel.dsl.jbang.core.common.ActionClient.ActionResult;
 import org.apache.camel.dsl.jbang.core.common.CamelCommandHelper;
+import org.apache.camel.dsl.jbang.core.common.CliException;
 import org.apache.camel.dsl.jbang.core.common.CliExitCode;
 import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
 import org.apache.camel.dsl.jbang.core.common.EnvironmentHelper;
@@ -169,8 +170,13 @@ public class Debug extends Run {
 
     @Override
     public Integer doCall() throws Exception {
-        // the debugger reads its commands from the terminal
-        requireInteractiveTerminal("camel debug");
+        // the debugger reads its commands from the terminal; skip the check 
in background mode
+        if (!background) {
+            requireInteractiveTerminal("camel debug");
+        }
+        if (background && remoteAttach) {
+            throw CliException.usage("--background and --remote-attach are 
mutually exclusive");
+        }
         if (!remoteAttach) {
             checkFilesExist();
             skipCitrusTests();
@@ -187,6 +193,11 @@ public class Debug extends Run {
             return exit;
         }
 
+        // in background mode, the integration runs as a detached child 
process; return immediately
+        if (background) {
+            return 0;
+        }
+
         // to shut down debug process if ctrl + c
         if (stopOnExit) {
             installHangupInterceptor();
@@ -330,6 +341,22 @@ public class Debug extends Run {
         } while (!quit.get());
     }
 
+    @Override
+    protected int runSpringBoot() throws Exception {
+        if (background) {
+            throw CliException.usage("Debug Camel Spring Boot with 
--background is not supported");
+        }
+        return super.runSpringBoot();
+    }
+
+    @Override
+    protected int runQuarkus() throws Exception {
+        if (background) {
+            throw CliException.usage("Debug Camel Quarkus with --background is 
not supported");
+        }
+        return super.runQuarkus();
+    }
+
     @Override
     protected int runDebug(KameletMain main) throws Exception {
         File pom = new File("pom.xml");
@@ -337,8 +364,14 @@ public class Debug extends Run {
             try (InputStream is = new FileInputStream(pom)) {
                 String text = IOHelper.loadText(is);
                 if (text.contains("camel-spring-boot-bom")) {
+                    if (background) {
+                        throw CliException.usage("Debug Camel Spring Boot with 
--background is not supported");
+                    }
                     return doRunDebugSpringBoot(main);
                 } else if (text.contains("org.apache.camel.quarkus")) {
+                    if (background) {
+                        throw CliException.usage("Debug Camel Quarkus with 
--background is not supported");
+                    }
                     return doRunDebugQuarkus(main);
                 }
             }
@@ -579,6 +612,10 @@ public class Debug extends Run {
         ProcessBuilder pb = new ProcessBuilder();
         pb.command(cmds);
 
+        if (background) {
+            return runBackgroundProcess(pb, "Camel Debug");
+        }
+
         Process p = pb.start();
         this.spawnOutput = p.getInputStream();
         this.spawnError = p.getErrorStream();
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/CommandInputErrorsTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/CommandInputErrorsTest.java
index 431766650aa0..2b25452246a5 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/CommandInputErrorsTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/CommandInputErrorsTest.java
@@ -19,6 +19,7 @@ package org.apache.camel.dsl.jbang.core.commands;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.camel.dsl.jbang.core.commands.action.EvalExpressionCommand;
 import org.apache.camel.dsl.jbang.core.commands.action.TransformMessageAction;
@@ -154,6 +155,51 @@ class CommandInputErrorsTest {
                 .extracting(e -> ((CliException) 
e).getExitCode()).isEqualTo(CliExitCode.USAGE);
     }
 
+    @Test
+    void debugSpringBootInTheBackground() throws Exception {
+        // Spring Boot registers stopOnExit hooks that kill the child when 
this JVM exits; --background exits
+        // immediately and would stop the integration before it starts
+        Path pom = Files.writeString(dir.resolve("pom.xml"),
+                "<project><dependencyManagement><dependencies>"
+                                                             + 
"<dependency><groupId>org.apache.camel.springboot</groupId>"
+                                                             + 
"<artifactId>camel-spring-boot-bom</artifactId><version>4.9.0</version>"
+                                                             + 
"<type>pom</type><scope>import</scope></dependency>"
+                                                             + 
"</dependencies></dependencyManagement></project>");
+
+        assertThatThrownBy(() -> call(new Debug(main), "--background", 
pom.toString()))
+                .isInstanceOf(CliException.class)
+                .hasMessage("Debug Camel Spring Boot with --background is not 
supported")
+                .extracting(e -> ((CliException) 
e).getExitCode()).isEqualTo(CliExitCode.USAGE);
+    }
+
+    @Test
+    void debugInTheBackgroundSkipsTtyCheck() throws Exception {
+        // --background makes camel debug run without an interactive terminal:
+        // the TTY check that guards the interactive path must be bypassed
+        Path file = Files.writeString(dir.resolve("orders.yaml"), "- from:\n   
 uri: timer:x\n");
+        assumeFalse(EnvironmentHelper.isInteractiveTerminal(), "needs a 
non-interactive terminal");
+        // Override runBackgroundProcess so the test does not actually spawn a 
child JVM,
+        // and record the kind string to verify the background branch was taken
+        AtomicReference<String> capturedKind = new AtomicReference<>();
+        Debug debug = new Debug(main) {
+            @Override
+            protected int runBackgroundProcess(ProcessBuilder pb, String kind) 
{
+                capturedKind.set(kind);
+                return 0;
+            }
+        };
+        assertThat(call(debug, "--background", file.toString())).isZero();
+        assertThat(capturedKind.get()).isEqualTo("Camel Debug");
+    }
+
+    @Test
+    void debugBackgroundAndRemoteAttachAreMutuallyExclusive() {
+        assertThatThrownBy(() -> call(new Debug(main), "--background", 
"--remote-attach"))
+                .isInstanceOf(CliException.class)
+                .hasMessage("--background and --remote-attach are mutually 
exclusive")
+                .extracting(e -> ((CliException) 
e).getExitCode()).isEqualTo(CliExitCode.USAGE);
+    }
+
     @ParameterizedTest
     @ValueSource(strings = { "missing.yaml", "missing.xml", "missing.java", 
"file:missing.camel.yaml" })
     void runAFileThatDoesNotExist(String file) {
diff --git 
a/dsl/camel-jbang/camel-jbang-it/src/test/java/org/apache/camel/dsl/jbang/it/CamelDebugITCase.java
 
b/dsl/camel-jbang/camel-jbang-it/src/test/java/org/apache/camel/dsl/jbang/it/CamelDebugITCase.java
index 910eded0830a..9b186fe2d924 100644
--- 
a/dsl/camel-jbang/camel-jbang-it/src/test/java/org/apache/camel/dsl/jbang/it/CamelDebugITCase.java
+++ 
b/dsl/camel-jbang/camel-jbang-it/src/test/java/org/apache/camel/dsl/jbang/it/CamelDebugITCase.java
@@ -29,7 +29,7 @@ public class CamelDebugITCase extends JBangTestSupport {
     @Test
     public void testDebug() throws IOException {
         copyResourceInDataFolder(TestResources.ROUTE2);
-        execNohup(String.format("debug %s/route2.yaml", mountPoint()));
+        executeBackground(String.format("debug %s/route2.yaml", mountPoint()));
         checkLogContains("Debugger JMXConnector listening at:");
     }
 }

Reply via email to