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
