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 8832b022893d CAMEL-25817: camel-jbang - camel dirty removes only the 
files of processes that are gone (#28109)
8832b022893d is described below

commit 8832b022893d6554f7949fa1d3dd400560554cde
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Oct 19 21:54:00 2026 +0200

    CAMEL-25817: camel-jbang - camel dirty removes only the files of processes 
that are gone (#28109)
    
    Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
 .../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 a29fa617f76d..4a521a7823f7 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
@@ -1699,6 +1699,14 @@ A name in camel case that starts with a lower-case 
letter, such as the datasourc
 looks like an option name and is written in dash case as a whole, underscores 
included (`my-orders`,
 `my-data-source`). Quote it to keep it: 
`quarkus.datasource."myOrders".db-kind`.
 
+=== 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.
+
 === camel-mongodb-gridfs - the consumer routes a file once
 
 With the `TimeStamp` (default) and `PersistentTimestamp` query strategies, the 
`mongodb-gridfs` consumer routed
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 357fe0a30c2f..142cd4d99f47 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.
+
 The log of the previous run of an integration (`~/.camel/<name>-previous.log`, 
see `camel log --previous`) is kept on
 purpose, so `camel dirty` leaves it.
 
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 5f887eb9b9b5..e1a73c12026e 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
@@ -45,7 +45,7 @@ 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;
         }
@@ -56,11 +56,12 @@ public class Dirty extends ProcessBaseCommand {
                 // kept on purpose: the log of the previous run of an 
integration (camel log --previous)
                 continue;
             }
-            // 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);
             }
         }
@@ -69,10 +70,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 9e1026d7c90e..85c2629710de 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;
@@ -123,6 +125,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