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 f2c282236eb6 CAMEL-26930: camel-jbang - --exclude matches a path, and 
applies to the files of a source directory (#29951)
f2c282236eb6 is described below

commit f2c282236eb62eb5e2525420d9ba48ed73f9aaba
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Nov 14 21:57:22 2026 +0100

    CAMEL-26930: camel-jbang - --exclude matches a path, and applies to the 
files of a source directory (#29951)
    
    
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../resources/golden/expected/excludes.run.txt     |  2 +
 .../camel/dsl/jbang/run/CamelMainRunner.java       |  2 +-
 .../org/apache/camel/dsl/jbang/run/RunFiles.java   |  7 ++-
 .../apache/camel/dsl/jbang/run/RunPipeline.java    | 58 +++++++++++++++++++++-
 .../apache/camel/dsl/jbang/run/RunFilesTest.java   | 11 ++++
 .../dsl/jbang/run/RunPipelineExcludeTest.java      | 53 ++++++++++++++++++++
 6 files changed, 130 insertions(+), 3 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/expected/excludes.run.txt
 
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/expected/excludes.run.txt
index 580480158ee4..84556027b723 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/expected/excludes.run.txt
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/expected/excludes.run.txt
@@ -19,6 +19,7 @@ camel.jbang.javaVersion=21
 camel.jbang.kameletsVersion=${kamelets.version}
 camel.main.name=excludes
 
camel.main.routesIncludePattern=file\:src/test/resources/golden/excludes/**?optional\=true
+camel.main.routesExcludePattern=file\:src/test/resources/golden/excludes/**/*.xml
 
 # initial properties
 camel.component.kamelet.location=classpath:kamelets
@@ -38,6 +39,7 @@ camel.jbang.springBootVersion=${spring-boot.version}
 camel.jbang.verbose=false
 camel.main.modeline=true
 camel.main.name=excludes
+camel.main.routesExcludePattern=file:src/test/resources/golden/excludes/**/*.xml
 
camel.main.routesIncludePattern=file:src/test/resources/golden/excludes/**?optional=true
 
 # override properties
diff --git 
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/CamelMainRunner.java
 
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/CamelMainRunner.java
index 03616347461d..618c621fee74 100644
--- 
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/CamelMainRunner.java
+++ 
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/CamelMainRunner.java
@@ -267,7 +267,7 @@ final class CamelMainRunner {
         boolean recursive;
         if (request.sourceDir != null) {
             reloadDir = request.sourceDir;
-            pattern = RunPipeline.sourceDirReloadPattern(request.dataDir);
+            pattern = RunPipeline.sourceDirReloadPattern(request.dataDir) + 
RunPipeline.reloadExcludes(request.excludes);
             recursive = true;
         } else {
             // the folder that has all the files, not the one of the first 
file in another folder (CAMEL-26930)
diff --git 
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/RunFiles.java
 
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/RunFiles.java
index 86f4b3a30c57..bdee1f27ee68 100644
--- 
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/RunFiles.java
+++ 
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/RunFiles.java
@@ -148,6 +148,11 @@ public final class RunFiles {
             return false;
         }
 
+        // the path as given, for an exclude with a folder such as routes/** 
(CAMEL-26930)
+        String given = name.replace('\\', '/');
+        while (given.startsWith("./")) {
+            given = given.substring(2);
+        }
         // flatten file
         name = FileUtil.stripPath(name);
 
@@ -184,7 +189,7 @@ public final class RunFiles {
         }
 
         // is the file excluded?
-        if (isExcluded(name, excludes)) {
+        if (isExcluded(name, excludes) || isExcluded(given, excludes)) {
             return true;
         }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/RunPipeline.java
 
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/RunPipeline.java
index 08d35933ecbd..d6f175afe159 100644
--- 
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/RunPipeline.java
+++ 
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/RunPipeline.java
@@ -760,6 +760,16 @@ public class RunPipeline {
             String sdir = "file:" + request.sourceDir + "/**?optional=true";
             main.addInitialProperty("camel.main.routesIncludePattern", sdir);
             writeSettings("camel.main.routesIncludePattern", sdir);
+            String excluded = sourceDirExcludePattern(request.sourceDir, 
request.excludes);
+            if (excluded != null) {
+                // --exclude applies to the files of the directory too 
(CAMEL-26930)
+                String existing = 
main.getInitialProperties().getProperty("camel.main.routesExcludePattern");
+                if (existing != null && !existing.isBlank()) {
+                    excluded = existing + "," + excluded;
+                }
+                main.addInitialProperty("camel.main.routesExcludePattern", 
excluded);
+                writeSettings("camel.main.routesExcludePattern", excluded);
+            }
         } else if (js.length() > 0) {
             main.addInitialProperty("camel.main.routesIncludePattern", 
js.toString());
             writeSettings("camel.main.routesIncludePattern", js.toString());
@@ -1164,7 +1174,8 @@ public class RunPipeline {
             if (request.sourceDir != null) {
                 main.addInitialProperty(SOURCE_DIR, request.sourceDir);
                 main.addInitialProperty("camel.main.routesReloadDirectory", 
request.sourceDir);
-                main.addInitialProperty("camel.main.routesReloadPattern", 
sourceDirReloadPattern(request.dataDir));
+                main.addInitialProperty("camel.main.routesReloadPattern",
+                        sourceDirReloadPattern(request.dataDir) + 
reloadExcludes(request.excludes));
                 
main.addInitialProperty("camel.main.routesReloadDirectoryRecursive", "true");
             } else {
                 dataDirWithoutSourceDir.run();
@@ -1238,6 +1249,51 @@ public class RunPipeline {
         return answer != null ? answer : a.getRoot();
     }
 
+    /**
+     * The routes exclude pattern (camel.main.routesExcludePattern) of the 
--exclude patterns for the files of a source
+     * directory, or null when there are none. The files of the directory have 
the location file:dir/name; a pattern
+     * without a folder, such as *.xml, matches a file name in any folder, as 
it does for the files given by name.
+     */
+    public static String sourceDirExcludePattern(String sourceDir, 
List<String> excludes) {
+        if (excludes == null || excludes.isEmpty()) {
+            return null;
+        }
+        String dir = sourceDir.replace('\\', '/');
+        while (dir.length() > 1 && dir.endsWith("/")) {
+            dir = dir.substring(0, dir.length() - 1);
+        }
+        StringJoiner sj = new StringJoiner(",");
+        for (String pattern : excludes) {
+            pattern = pattern.trim().replace('\\', '/');
+            while (pattern.startsWith("./")) {
+                pattern = pattern.substring(2);
+            }
+            if (!pattern.isEmpty()) {
+                sj.add("file:" + dir + "/" + (pattern.contains("/") ? pattern 
: "**/" + pattern));
+            }
+        }
+        return sj.length() > 0 ? sj.toString() : null;
+    }
+
+    /**
+     * The --exclude patterns as excludes of the reload pattern, so a change 
of an excluded file does not load it.
+     */
+    public static String reloadExcludes(List<String> excludes) {
+        StringBuilder sb = new StringBuilder();
+        if (excludes != null) {
+            for (String pattern : excludes) {
+                pattern = pattern.trim().replace('\\', '/');
+                while (pattern.startsWith("./")) {
+                    pattern = pattern.substring(2);
+                }
+                if (!pattern.isEmpty()) {
+                    sb.append(",!").append(pattern);
+                }
+            }
+        }
+        return sb.toString();
+    }
+
     /**
      * The reload pattern of dev mode with a source directory: every file 
under it, except the test folder, which holds
      * the Citrus tests of the project (run with camel test run) and their 
data, none of them routes (CAMEL-26494), and
diff --git 
a/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/RunFilesTest.java
 
b/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/RunFilesTest.java
index df10c0390fba..7594ec28343a 100644
--- 
a/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/RunFilesTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/RunFilesTest.java
@@ -53,6 +53,17 @@ class RunFilesTest {
         assertThat(RunFiles.skipFile("orders.camel.yaml", List.of(), 
"")).isFalse();
     }
 
+    @Test
+    void anExcludeWithAFolderMatchesThePath() {
+        // the file name was matched only, so routes/** or sub/skip.camel.yaml 
never matched (CAMEL-26930)
+        assertThat(RunFiles.skipFile("sub/skip.camel.yaml", 
List.of("sub/skip.camel.yaml"), "")).isTrue();
+        assertThat(RunFiles.skipFile("./sub/skip.camel.yaml", 
List.of("sub/**"), "")).isTrue();
+        assertThat(RunFiles.skipFile("sub/b.camel.yaml", 
List.of("sub/skip.camel.yaml"), "")).isFalse();
+        // a pattern without a folder matches the file name, in any folder, as 
before
+        assertThat(RunFiles.skipFile("sub/skip.camel.yaml", 
List.of("skip.camel.yaml"), "")).isTrue();
+        assertThat(RunFiles.skipFile("a.camel.yaml", List.of("*.xml"), 
"")).isFalse();
+    }
+
     @ParameterizedTest
     @ValueSource(strings = {
             "orders.camel.yaml", "orders.yaml", "citrus.yaml", 
"orders.camel.xml", "Orders.java", "orders.groovy",
diff --git 
a/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/RunPipelineExcludeTest.java
 
b/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/RunPipelineExcludeTest.java
new file mode 100644
index 000000000000..ac07c438b5df
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/RunPipelineExcludeTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.run;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * --exclude for the files of a source directory: camel run ., --source-dir 
and camel dev (CAMEL-26930).
+ */
+class RunPipelineExcludeTest {
+
+    @Test
+    void theExcludesAreUnderTheSourceDirectory() {
+        // the files of the directory have the location file:./sub/b.camel.yaml
+        assertThat(RunPipeline.sourceDirExcludePattern(".", List.of("sub/**", 
"./skip.camel.yaml", "*.xml")))
+                
.isEqualTo("file:./sub/**,file:./**/skip.camel.yaml,file:./**/*.xml");
+        assertThat(RunPipeline.sourceDirExcludePattern("routes/", 
List.of("old/**")))
+                .isEqualTo("file:routes/old/**");
+    }
+
+    @Test
+    void noExcludesNoPattern() {
+        assertThat(RunPipeline.sourceDirExcludePattern(".", 
List.of())).isNull();
+        assertThat(RunPipeline.sourceDirExcludePattern(".", null)).isNull();
+        assertThat(RunPipeline.sourceDirExcludePattern(".", List.of(" 
"))).isNull();
+    }
+
+    @Test
+    void theExcludesAreLeftOutOfTheReload() {
+        assertThat(
+                RunPipeline.sourceDirReloadPattern(null) + 
RunPipeline.reloadExcludes(List.of("sub/**", "./skip.camel.yaml")))
+                .endsWith(",!sub/**,!skip.camel.yaml");
+        assertThat(RunPipeline.reloadExcludes(null)).isEmpty();
+    }
+}

Reply via email to