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 5c6edcdddb10 CAMEL-26912: camel-jbang - a drive letter on Windows is 
not a scheme, and mvn is mvn.cmd (#29824)
5c6edcdddb10 is described below

commit 5c6edcdddb1096fdb6389985dc4734e6a624483d
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Nov 13 16:41:27 2026 +0100

    CAMEL-26912: camel-jbang - a drive letter on Windows is not a scheme, and 
mvn is mvn.cmd (#29824)
    
    C:\\path was taken for a file with the scheme C, so the export dropped 
absolute files, and a process cannot be started as mvn on Windows.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../dsl/jbang/core/commands/CamelCommand.java      |  6 ++-
 .../camel/dsl/jbang/run/common/MavenCommand.java   | 14 ++++--
 .../camel/dsl/jbang/run/export/Exporter.java       |  6 ++-
 .../dsl/jbang/run/common/MavenCommandTest.java     | 53 ++++++++++++++++++++++
 .../camel/dsl/jbang/run/export/ExporterTest.java   | 11 +++++
 5 files changed, 85 insertions(+), 5 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java
index ed001f6b7903..174f542d7945 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelCommand.java
@@ -234,9 +234,13 @@ public abstract class CamelCommand implements 
Command<CommandInvocation>, Comman
         return values != null ? values.toArray(new String[0]) : null;
     }
 
+    /**
+     * The scheme of a file, such as classpath or github, or null for a file 
without scheme. A single letter is the
+     * drive of a path on Windows (C:\work\route.camel.yaml), not a scheme 
(CAMEL-26912).
+     */
     protected static String getScheme(String name) {
         int pos = name.indexOf(":");
-        if (pos != -1) {
+        if (pos > 1) {
             return name.substring(0, pos);
         }
         return null;
diff --git 
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/common/MavenCommand.java
 
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/common/MavenCommand.java
index fa51644a869f..2fc3c0487b97 100644
--- 
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/common/MavenCommand.java
+++ 
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/common/MavenCommand.java
@@ -37,10 +37,18 @@ public final class MavenCommand {
     }
 
     /**
-     * The Maven wrapper of the directory when it has one that can run, and 
otherwise mvn.
+     * The Maven wrapper of the directory when it has one that can run, and 
otherwise mvn (mvn.cmd on Windows, which a
+     * process cannot be started with as mvn).
      */
     public static String of(Path dir) {
-        Path wrapper = wrapper(dir);
-        return Files.isExecutable(wrapper) ? wrapper.toString() : "mvn";
+        return of(dir, FileUtil.isWindows());
+    }
+
+    static String of(Path dir, boolean windows) {
+        Path wrapper = dir.resolve(windows ? "mvnw.cmd" : "mvnw");
+        if (Files.isExecutable(wrapper)) {
+            return wrapper.toString();
+        }
+        return windows ? "mvn.cmd" : "mvn";
     }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/export/Exporter.java
 
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/export/Exporter.java
index a20af4603aaa..4c1c985dbd7c 100644
--- 
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/export/Exporter.java
+++ 
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/export/Exporter.java
@@ -1444,9 +1444,13 @@ abstract class Exporter {
         copyApplicationPropertiesFiles(srcResourcesDir.toPath());
     }
 
+    /**
+     * The scheme of a file, such as classpath or github, or null for a file 
without scheme. A single letter is the
+     * drive of a path on Windows (C:\work\route.camel.yaml), not a scheme 
(CAMEL-26912).
+     */
     protected static String getScheme(String name) {
         int pos = name.indexOf(":");
-        if (pos != -1) {
+        if (pos > 1) {
             return name.substring(0, pos);
         }
         return null;
diff --git 
a/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/common/MavenCommandTest.java
 
b/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/common/MavenCommandTest.java
new file mode 100644
index 000000000000..717719b7fcc3
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/common/MavenCommandTest.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.common;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.apache.camel.util.FileUtil;
+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.io.TempDir;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * The Maven that builds a project: its wrapper, else mvn from the path.
+ */
+class MavenCommandTest {
+
+    @TempDir
+    Path dir;
+
+    @Test
+    void mvnFromThePathWithoutWrapper() {
+        assertThat(MavenCommand.of(dir, false)).isEqualTo("mvn");
+        // a process cannot be started as mvn on Windows, where it is mvn.cmd
+        assertThat(MavenCommand.of(dir, true)).isEqualTo("mvn.cmd");
+    }
+
+    @Test
+    @DisabledOnOs(value = OS.WINDOWS, disabledReason = "a file is executable 
on Windows without the permission")
+    void theWrapperOfTheProject() throws Exception {
+        Path mvnw = Files.writeString(dir.resolve("mvnw"), "#!/bin/sh");
+        assertThat(MavenCommand.of(dir, false)).isEqualTo("mvn");
+        FileUtil.setPosixFilePermissions(mvnw, "rwxr-xr-x");
+        assertThat(MavenCommand.of(dir, false)).isEqualTo(mvnw.toString());
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/export/ExporterTest.java
 
b/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/export/ExporterTest.java
index c6604242803e..03c613f04ce2 100644
--- 
a/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/export/ExporterTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/export/ExporterTest.java
@@ -35,4 +35,15 @@ class ExporterTest {
                 .containsEntry("id", "custom1")
                 .containsEntry("url", "https://other.repo/releases";);
     }
+
+    @Test
+    public void aDriveOfWindowsIsNoScheme() {
+        
assertThat(Exporter.getScheme("classpath:tls/keystore.jks")).isEqualTo("classpath");
+        
assertThat(Exporter.getScheme("file:C:\\certs\\keystore.jks")).isEqualTo("file");
+        
assertThat(Exporter.getScheme("github:apache:camel-kamelets-examples:jbang/hello-java")).isEqualTo("github");
+        // the export kept \certs\keystore.jks of it, which is not the file
+        assertThat(Exporter.getScheme("C:\\certs\\keystore.jks")).isNull();
+        assertThat(Exporter.getScheme("c:/certs/keystore.jks")).isNull();
+        assertThat(Exporter.getScheme("route.camel.yaml")).isNull();
+    }
 }

Reply via email to