This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch fix/CAMEL-25817
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 126477047cea81c03e92d96cd9c00b509cac7d8d
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Oct 19 13:38:20 2026 +0200

    CAMEL-25817: camel-jbang - camel dirty removes only the files of processes 
that are gone
    
    camel dirty counted every file in ~/.camel without the pid of a live 
process in
    its name, and --clean deleted them: the log of a running Spring Boot or 
Quarkus
    app (<name>.log), the console output the AI tools keep, and the history of 
the
    AI prompt. Only regular files named after the pid of a process that is gone
    count now, directories are left alone, and --clean counts only what it 
removed.
    
    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 ++++
 .../ROOT/pages/camel-jbang-configuration.adoc      |  3 ++
 .../dsl/jbang/core/commands/process/Dirty.java     | 26 ++++++----
 .../dsl/jbang/core/commands/process/DirtyTest.java | 55 ++++++++++++++++++++++
 4 files changed, 84 insertions(+), 8 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 08ed3d96753b..67b87d0fa457 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
@@ -1700,3 +1700,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 - camel dirty removes only the files of processes that are gone
+
+`camel dirty` counts, and `camel dirty --clean` removes, only the files named 
after the pid of a process that is gone,
+such as `<pid>-status.json`. Before, it counted every other file in `~/.camel` 
too, and `--clean` removed them: the log of
+a Spring Boot or Quarkus app that `camel run` runs (`<name>.log`), also while 
the app ran, the console output that the AI
+tools keep, and the history of the AI prompt. Directories are not counted, and 
`--clean` counts only the files it
+removed.
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc
index 86fa276a28eb..abd5c0c7b9e5 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-configuration.adoc
@@ -131,6 +131,9 @@ camel dirty
 camel dirty --clean
 ----
 
+Only the files named after the pid of a process that is gone count, such as 
`<pid>-status.json`. Other files in
+`~/.camel`, such as the log of a Spring Boot or Quarkus app that `camel run` 
runs (`<name>.log`), are left alone.
+
 An error is printed as one line that starts with `ERROR:`. To see the stack 
trace of every error, set the
 environment variable `CAMEL_CLI_DEBUG` to `true`. See 
xref:camel-jbang-exit-codes.adoc[Exit Codes and Output]
 for what each exit code means.
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/Dirty.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/Dirty.java
index 6dcb4cae0dcb..b2fc8851e8e6 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/Dirty.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/Dirty.java
@@ -44,18 +44,19 @@ public class Dirty extends ProcessBaseCommand {
 
     @Override
     public Integer doCall() throws Exception {
-        File[] files = getCamelDir().toFile().listFiles(f -> 
!"camel-export.log".equals(f.getName()));
+        File[] files = getCamelDir().toFile().listFiles();
         if (files == null || files.length == 0) {
             return 0;
         }
 
         List<File> dirty = new ArrayList<>();
         for (File f : files) {
-            // the files of a process that is alive are in use (such as the 
launcher of an integration that runs in a
-            // child process, whose Camel has stopped), and so are the files 
of ourselves
-            Long pid = pidOf(f.getName());
-            boolean running = pid != null && 
ProcessHandle.of(pid).map(ProcessHandle::isAlive).orElse(false);
-            if (!running) {
+            // only the files of a process that is gone: a file named after 
its pid. The files of a process that is
+            // alive are in use (such as the launcher of an integration that 
runs in a child process, whose Camel has
+            // stopped), and so are the files of ourselves. A file that 
belongs to no process is left alone (such as the
+            // log of a Spring Boot or Quarkus app, <name>.log, also while the 
app runs), and so is a directory.
+            Long pid = f.isFile() ? pidOf(f.getName()) : null;
+            if (pid != null && 
!ProcessHandle.of(pid).map(ProcessHandle::isAlive).orElse(false)) {
                 dirty.add(f);
             }
         }
@@ -64,10 +65,19 @@ public class Dirty extends ProcessBaseCommand {
         }
 
         if (clean) {
+            int cleaned = 0;
+            List<String> left = new ArrayList<>();
             for (File f : dirty) {
-                FileUtil.deleteFile(f);
+                if (FileUtil.deleteFile(f)) {
+                    cleaned++;
+                } else {
+                    left.add(f.getName());
+                }
+            }
+            printer().println("Cleaned " + cleaned + " orphan files");
+            if (!left.isEmpty()) {
+                printer().printWarn("Cannot delete: " + String.join(", ", 
left));
             }
-            printer().println("Cleaned " + dirty.size() + " orphan files");
         } else {
             printer().println("There are " + dirty.size() + " orphan files");
         }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/DirtyTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/DirtyTest.java
index 0863681675bc..4e0921c98fee 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/DirtyTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/process/DirtyTest.java
@@ -23,6 +23,8 @@ import java.util.Optional;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledOnOs;
+import org.junit.jupiter.api.condition.OS;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.MockedStatic;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -102,6 +104,59 @@ class DirtyTest extends ProcessCommandTestSupport {
         }
     }
 
+    @Test
+    void testFilesThatBelongToNoProcessAreNotDirty() throws Exception {
+        Path dir = CommandLineHelper.getCamelDir();
+        // the log of a Spring Boot or Quarkus app, which writes it while it 
runs
+        Files.writeString(dir.resolve("orders.log"), "Started");
+        // the console output the AI tools keep, and other files of the CLI
+        Files.writeString(dir.resolve("camel-launch-123456.log"), "Started");
+        Files.writeString(dir.resolve("ai-prompt.history"), "hello");
+        Files.writeString(dir.resolve("camel-export.log"), "Exported");
+        // directories, such as a cache
+        
Files.createDirectories(dir.resolve("quarkus-extension-registries").resolve("cache"));
+        Files.createDirectories(dir.resolve("12345-dir"));
+
+        Dirty command = new Dirty(new CamelJBangMain().withPrinter(printer));
+        command.clean = true;
+
+        try (MockedStatic<ProcessHandle> mocked = 
mockStatic(ProcessHandle.class)) {
+            int exit = command.doCall();
+
+            assertEquals(0, exit);
+            assertEquals("", printer.getOutput().trim());
+            for (String name : new String[] {
+                    "orders.log", "camel-launch-123456.log", 
"ai-prompt.history", "camel-export.log",
+                    "quarkus-extension-registries", "12345-dir" }) {
+                assertTrue(Files.exists(dir.resolve(name)), name + " is left");
+            }
+        }
+    }
+
+    @Test
+    @DisabledOnOs(OS.WINDOWS)
+    void testOnlyTheFilesThatWereRemovedAreCounted() throws Exception {
+        Path dir = CommandLineHelper.getCamelDir();
+        Path orphan = dir.resolve("77777-status.json");
+        Files.writeString(orphan, "{}");
+        // a file in a directory that cannot be written cannot be deleted
+        assertTrue(dir.toFile().setWritable(false));
+
+        Dirty command = new Dirty(new CamelJBangMain().withPrinter(printer));
+        command.clean = true;
+
+        try (MockedStatic<ProcessHandle> mocked = 
mockStatic(ProcessHandle.class)) {
+            int exit = command.doCall();
+
+            assertEquals(0, exit);
+            assertTrue(printer.getOutput().contains("Cleaned 0 orphan files"), 
printer.getOutput());
+            assertTrue(printer.getOutput().contains("WARN: Cannot delete: 
77777-status.json"), printer.getOutput());
+            assertTrue(Files.exists(orphan));
+        } finally {
+            dir.toFile().setWritable(true);
+        }
+    }
+
     @Test
     void testFileBelongsToTheWholePid() {
         // pid 12 does not own the files of pid 123

Reply via email to