You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/03/13 15:21:27 UTC

[camel] branch camel-3.20.x updated: CAMEL-19128: camel-jbang - Add version command (#9519)

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

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


The following commit(s) were added to refs/heads/camel-3.20.x by this push:
     new 84dd0e4f68d CAMEL-19128: camel-jbang - Add version command (#9519)
84dd0e4f68d is described below

commit 84dd0e4f68de141a3fd9d61a6402d932a40a78d3
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Mon Mar 13 15:58:44 2023 +0100

    CAMEL-19128: camel-jbang - Add version command (#9519)
    
    CAMEL-19128: camel-jbang - Add version command
---
 .../dsl/jbang/core/commands/CamelJBangMain.java    |   8 +-
 .../core/commands/version/VersionCommand.java      |  37 ++++
 .../jbang/core/commands/version/VersionGet.java    |  53 ++++++
 .../jbang/core/commands/version/VersionList.java   | 191 +++++++++++++++++++++
 .../camel/dsl/jbang/core/common/VersionHelper.java |  44 ++++-
 .../camel/main/download/DependencyDownloader.java  |  14 ++
 .../main/download/MavenDependencyDownloader.java   | 150 ++++++++++++++++
 .../org/apache/camel/main/util}/VersionHelper.java |  38 +++-
 .../java/org/apache/camel/main/util/XmlHelper.java |  63 +++++++
 9 files changed, 584 insertions(+), 14 deletions(-)

diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
index a8178d43e1b..fd129df2e6f 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
@@ -66,6 +66,9 @@ import org.apache.camel.dsl.jbang.core.commands.process.ListProcess;
 import org.apache.camel.dsl.jbang.core.commands.process.ListService;
 import org.apache.camel.dsl.jbang.core.commands.process.ListVault;
 import org.apache.camel.dsl.jbang.core.commands.process.StopProcess;
+import org.apache.camel.dsl.jbang.core.commands.version.VersionCommand;
+import org.apache.camel.dsl.jbang.core.commands.version.VersionGet;
+import org.apache.camel.dsl.jbang.core.commands.version.VersionList;
 import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
 import picocli.CommandLine;
 import picocli.CommandLine.Command;
@@ -134,7 +137,10 @@ public class CamelJBangMain implements Callable<Integer> {
                         .addSubcommand("list", new CommandLine(new ConfigList(main)))
                         .addSubcommand("get", new CommandLine(new ConfigGet(main)))
                         .addSubcommand("unset", new CommandLine(new ConfigUnset(main)))
-                        .addSubcommand("set", new CommandLine(new ConfigSet(main))));
+                        .addSubcommand("set", new CommandLine(new ConfigSet(main))))
+                .addSubcommand("version", new CommandLine(new VersionCommand(main))
+                        .addSubcommand("get", new CommandLine(new VersionGet(main)))
+                        .addSubcommand("list", new CommandLine(new VersionList(main))));
 
         commandLine.getCommandSpec().versionProvider(() -> {
             CamelCatalog catalog = new DefaultCamelCatalog();
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionCommand.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionCommand.java
new file mode 100644
index 00000000000..d305b45866c
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionCommand.java
@@ -0,0 +1,37 @@
+/*
+ * 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.core.commands.version;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import picocli.CommandLine;
+
+@CommandLine.Command(name = "version",
+                     description = "Manage Camel versions (use config --help to see sub commands)")
+public class VersionCommand extends CamelCommand {
+
+    public VersionCommand(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer call() throws Exception {
+        // defaults to get
+        new CommandLine(new VersionGet(getMain())).execute();
+        return 0;
+    }
+}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionGet.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionGet.java
new file mode 100644
index 00000000000..87e486ecff0
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionGet.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.core.commands.version;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
+import picocli.CommandLine;
+
+@CommandLine.Command(name = "get", description = "Displays current Camel version")
+public class VersionGet extends CamelCommand {
+
+    @CommandLine.Option(names = { "--runtime" },
+                        description = "Runtime (spring-boot, quarkus, or camel-main)")
+    protected String runtime;
+
+    public VersionGet(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer call() throws Exception {
+        CamelCatalog catalog = new DefaultCamelCatalog();
+        String v = catalog.getCatalogVersion();
+        System.out.println("Camel CLI version: " + v);
+
+        CommandLineHelper.loadProperties(properties -> {
+            String uv = properties.getProperty("camel-version");
+            if (uv != null) {
+                System.out.println("User configuration Camel version: " + uv);
+            }
+        });
+
+        return 0;
+    }
+
+}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionList.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionList.java
new file mode 100644
index 00000000000..3946440410a
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionList.java
@@ -0,0 +1,191 @@
+/*
+ * 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.core.commands.version;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.github.freva.asciitable.AsciiTable;
+import com.github.freva.asciitable.Column;
+import com.github.freva.asciitable.HorizontalAlign;
+import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.VersionHelper;
+import org.apache.camel.main.KameletMain;
+import org.apache.camel.main.download.MavenDependencyDownloader;
+import picocli.CommandLine;
+
+@CommandLine.Command(name = "list", description = "Displays available Camel versions")
+public class VersionList extends CamelCommand {
+
+    @CommandLine.Option(names = { "--sort" },
+                        description = "Sort by version", defaultValue = "version")
+    String sort;
+
+    @CommandLine.Option(names = { "--runtime" },
+                        description = "Runtime (spring-boot, quarkus, or camel-main)")
+    String runtime;
+
+    @CommandLine.Option(names = { "--minimum-version" },
+                        description = "Minimum Camel version to avoid resolving too old releases", defaultValue = "3.14.0")
+    String minimumVersion = "3.14.0";
+
+    @CommandLine.Option(names = { "--repo", "--repos" }, description = "Maven repository for downloading available versions")
+    String repo;
+
+    @CommandLine.Option(names = { "--lts" }, description = "Only show LTS supported releases")
+    boolean lts;
+
+    @CommandLine.Option(names = { "--fresh" }, description = "Make sure we use fresh (i.e. non-cached) resources")
+    boolean fresh;
+
+    public VersionList(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer call() throws Exception {
+        configureLoggingOff();
+        KameletMain main = new KameletMain();
+
+        List<String[]> versions;
+        try {
+            main.setFresh(fresh);
+            main.setRepos(repo);
+            main.start();
+
+            // use kamelet-main to download from maven
+            MavenDependencyDownloader downloader = main.getCamelContext().hasService(MavenDependencyDownloader.class);
+
+            String g = "org.apache.camel";
+            String a = "camel-catalog";
+            if ("spring-boot".equalsIgnoreCase(runtime)) {
+                g = "org.apache.camel.springboot";
+                a = "camel-spring-boot";
+            } else if ("quarkus".equalsIgnoreCase(runtime)) {
+                g = "org.apache.camel.quarkus";
+                a = "camel-quarkus-catalog";
+            }
+
+            versions = downloader.resolveAvailableVersions(g, a, minimumVersion, repo);
+            versions = versions.stream().filter(v -> acceptVersion(v[0])).collect(Collectors.toList());
+
+            main.stop();
+        } catch (Exception e) {
+            System.out.println("Error downloading available Camel versions");
+            return 1;
+        }
+
+        List<Row> rows = new ArrayList<>();
+        for (String[] v : versions) {
+            Row row = new Row();
+            rows.add(row);
+            row.coreVersion = v[0];
+            row.runtimeVersion = v[1];
+        }
+
+        // sort rows
+        rows.sort(this::sortRow);
+
+        System.out.println(AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, Arrays.asList(
+                new Column().header("CAMEL VERSION")
+                        .headerAlign(HorizontalAlign.CENTER).dataAlign(HorizontalAlign.CENTER).with(r -> r.coreVersion),
+                new Column().header("QUARKUS").visible("quarkus".equalsIgnoreCase(runtime))
+                        .headerAlign(HorizontalAlign.CENTER).dataAlign(HorizontalAlign.CENTER).with(r -> r.runtimeVersion),
+                new Column().header("SPRING-BOOT").visible("spring-boot".equalsIgnoreCase(runtime))
+                        .headerAlign(HorizontalAlign.CENTER).dataAlign(HorizontalAlign.CENTER).with(r -> r.runtimeVersion),
+                new Column().header("JDK")
+                        .headerAlign(HorizontalAlign.CENTER).dataAlign(HorizontalAlign.RIGHT).with(this::jdkVersion),
+                new Column().header("SUPPORT")
+                        .headerAlign(HorizontalAlign.CENTER).dataAlign(HorizontalAlign.CENTER).with(this::lts))));
+
+        return 0;
+    }
+
+    protected int sortRow(Row o1, Row o2) {
+        String s = sort;
+        int negate = 1;
+        if (s.startsWith("-")) {
+            s = s.substring(1);
+            negate = -1;
+        }
+        switch (s) {
+            case "version":
+                return VersionHelper.compare(o1.coreVersion, o2.coreVersion) * negate;
+            default:
+                return 0;
+        }
+    }
+
+    private String jdkVersion(Row r) {
+        if (VersionHelper.isGE(r.coreVersion, "4.0")) {
+            return "17";
+        } else if (VersionHelper.isGE(r.coreVersion, "3.15")) {
+            return "11, 17";
+        } else if (VersionHelper.isGE(r.coreVersion, "3.15")) {
+            return "11, 17";
+        } else if (VersionHelper.isGE(r.coreVersion, "3.0")) {
+            return "8, 11";
+        } else {
+            return "8";
+        }
+    }
+
+    private String lts(Row r) {
+        return isLtsRelease(r.coreVersion) ? "LTS" : "";
+    }
+
+    private static boolean isLtsRelease(String version) {
+        if (VersionHelper.isBetween(version, "4.0.0", "4.1")) {
+            return true;
+        } else if (VersionHelper.isBetween(version, "3.20", "3.99")) {
+            return true;
+        } else if (VersionHelper.isBetween(version, "3.18", "3.19")) {
+            return true;
+        } else if (VersionHelper.isBetween(version, "3.14", "3.15")) {
+            return true;
+        } else if (VersionHelper.isBetween(version, "3.11", "3.12")) {
+            return true;
+        } else if (VersionHelper.isBetween(version, "3.11", "3.12")) {
+            return true;
+        } else if (VersionHelper.isBetween(version, "3.7", "3.8")) {
+            return true;
+        } else if (VersionHelper.isBetween(version, "3.4", "3.5")) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean acceptVersion(String version) {
+        if (version == null) {
+            return false;
+        }
+        boolean accept = VersionHelper.isGE(version, minimumVersion);
+        if (accept && lts) {
+            accept = isLtsRelease(version);
+        }
+        return accept;
+    }
+
+    private static class Row {
+        String coreVersion;
+        String runtimeVersion;
+    }
+
+}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/VersionHelper.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/VersionHelper.java
index cc22c2985e5..3c60bd603da 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/VersionHelper.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/VersionHelper.java
@@ -24,8 +24,20 @@ public final class VersionHelper {
     }
 
     public static boolean isGE(String source, String target) {
+        return compare(source, target) >= 0;
+    }
+
+    public static boolean isLE(String source, String target) {
+        return compare(source, target) <= 0;
+    }
+
+    public static boolean isBetween(String source, String inclusive, String exclusive) {
+        return compare(source, inclusive) >= 0 && compare(source, exclusive) < 0;
+    }
+
+    public static int compare(String source, String target) {
         if (source == null || target == null) {
-            return false;
+            return 0;
         }
         String s1 = StringHelper.before(source, ".");
         String s2 = StringHelper.after(source, ".");
@@ -39,7 +51,18 @@ public final class VersionHelper {
             t1 = StringHelper.before(target, ",");
             t2 = StringHelper.after(target, ",");
         }
-
+        String s3 = StringHelper.after(s2, ".");
+        if (s3 != null) {
+            s2 = StringHelper.before(s2, ".");
+        } else {
+            s3 = "";
+        }
+        String t3 = StringHelper.after(t2, ".");
+        if (t3 != null) {
+            t2 = StringHelper.before(t2, ".");
+        } else {
+            t3 = "";
+        }
         // convert to 2-digit numbers
         if (s1.length() < 2) {
             s1 = "0" + s1;
@@ -47,16 +70,25 @@ public final class VersionHelper {
         if (s2.length() < 2) {
             s2 = "0" + s2;
         }
+        if (s2.length() < 2) {
+            s2 = "0" + s2;
+        }
+        if (s3.length() < 2) {
+            s3 = "0" + s3;
+        }
         if (t1.length() < 2) {
             t1 = "0" + t1;
         }
         if (t2.length() < 2) {
             t2 = "0" + t2;
         }
+        if (t3.length() < 2) {
+            t3 = "0" + t3;
+        }
 
-        String s = s1 + s2;
-        String t = t1 + t2;
-        int n = s.compareTo(t);
-        return n >= 0;
+        String s = s1 + s2 + s3;
+        String t = t1 + t2 + t3;
+        return s.compareTo(t);
     }
+
 }
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java
index 1ebf173e5a3..2ab1eb1921e 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.main.download;
 
+import java.util.List;
+
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.StaticService;
 
@@ -120,6 +122,18 @@ public interface DependencyDownloader extends CamelContextAware, StaticService {
      */
     MavenArtifact downloadArtifact(String groupId, String artifactId, String version);
 
+    /**
+     * Resolves the available versions for the given maven artifact
+     *
+     * @param  groupId        maven group id
+     * @param  artifactId     maven artifact id
+     * @param  minimumVersion optional minimum version to avoid resolving too old releases
+     * @param  repo           to use specific maven repository instead of maven central
+     * @return                list of versions of the given artifact (0=camel-core version, 1=runtime version, such as
+     *                        spring-boot or quarkus)
+     */
+    List<String[]> resolveAvailableVersions(String groupId, String artifactId, String minimumVersion, String repo);
+
     /**
      * Checks whether the dependency is already on the classpath
      *
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java
index 97068ed130a..4cba111ee26 100644
--- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java
@@ -37,9 +37,18 @@ import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
 
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.main.injection.DIRegistry;
+import org.apache.camel.main.util.VersionHelper;
+import org.apache.camel.main.util.XmlHelper;
 import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.support.service.ServiceSupport;
 import org.apache.camel.util.FileUtil;
@@ -144,6 +153,8 @@ import org.eclipse.aether.internal.impl.synccontext.named.GAVNameMapper;
 import org.eclipse.aether.internal.impl.synccontext.named.NameMapper;
 import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactorySelector;
 import org.eclipse.aether.internal.impl.synccontext.named.SimpleNamedLockFactorySelector;
+import org.eclipse.aether.metadata.DefaultMetadata;
+import org.eclipse.aether.metadata.Metadata;
 import org.eclipse.aether.named.NamedLockFactory;
 import org.eclipse.aether.named.providers.FileLockNamedLockFactory;
 import org.eclipse.aether.named.providers.LocalReadWriteLockNamedLockFactory;
@@ -158,6 +169,8 @@ import org.eclipse.aether.resolution.ArtifactRequest;
 import org.eclipse.aether.resolution.DependencyRequest;
 import org.eclipse.aether.resolution.DependencyResolutionException;
 import org.eclipse.aether.resolution.DependencyResult;
+import org.eclipse.aether.resolution.MetadataRequest;
+import org.eclipse.aether.resolution.MetadataResult;
 import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
@@ -207,6 +220,8 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende
     private static final Logger LOG = LoggerFactory.getLogger(MavenDependencyDownloader.class);
     private static final String CP = System.getProperty("java.class.path");
 
+    private static final String MINIMUM_QUARKUS_VERSION = "2.0.0";
+
     private static final RepositoryPolicy POLICY_DEFAULT = new RepositoryPolicy(
             true, RepositoryPolicy.UPDATE_POLICY_NEVER, RepositoryPolicy.CHECKSUM_POLICY_WARN);
     private static final RepositoryPolicy POLICY_FRESH = new RepositoryPolicy(
@@ -446,6 +461,23 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende
         return null;
     }
 
+    @Override
+    public List<String[]> resolveAvailableVersions(String groupId, String artifactId, String minimumVersion, String repo) {
+        String gav = groupId + ":" + artifactId;
+        LOG.debug("DownloadAvailableVersions: {}", gav);
+
+        // repo 0 is maven central
+        RemoteRepository repository = remoteRepositories.get(0);
+        if (repo != null) {
+            List<RemoteRepository> extra = resolveExtraRepositories(repo);
+            if (!extra.isEmpty()) {
+                repository = extra.get(0);
+            }
+        }
+        List<String[]> versions = resolveAvailableVersions(groupId, artifactId, minimumVersion, repository);
+        return versions;
+    }
+
     public boolean alreadyOnClasspath(String groupId, String artifactId, String version) {
         return alreadyOnClasspath(groupId, artifactId, version, true);
     }
@@ -1270,6 +1302,124 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende
         }
     }
 
+    public List<String[]> resolveAvailableVersions(
+            String groupId, String artifactId, String minimumVersion, RemoteRepository repository) {
+
+        List<String[]> answer = new ArrayList<>();
+
+        try {
+            MetadataRequest ar = new MetadataRequest();
+            ar.setRepository(repository);
+            ar.setFavorLocalRepository(false);
+            ar.setMetadata(new DefaultMetadata(groupId, artifactId, "maven-metadata.xml", Metadata.Nature.RELEASE));
+
+            List<MetadataResult> result = repositorySystem.resolveMetadata(repositorySystemSession, List.of(ar));
+            for (MetadataResult mr : result) {
+                if (mr.isResolved() && mr.getMetadata().getFile() != null) {
+                    File f = mr.getMetadata().getFile();
+                    if (f.exists() && f.isFile()) {
+                        DocumentBuilderFactory dbf = XmlHelper.createDocumentBuilderFactory();
+                        DocumentBuilder db = dbf.newDocumentBuilder();
+                        Document dom = db.parse(f);
+                        NodeList nl = dom.getElementsByTagName("version");
+                        for (int i = 0; i < nl.getLength(); i++) {
+                            Element node = (Element) nl.item(i);
+                            String v = node.getTextContent();
+                            if (v != null) {
+                                if ("camel-spring-boot".equals(artifactId)) {
+                                    String sbv = null;
+                                    if (VersionHelper.isGE(v, minimumVersion)) {
+                                        sbv = resolveSpringBootVersionByCamelVersion(v, repository);
+                                    }
+                                    answer.add(new String[] { v, sbv });
+                                } else if ("camel-quarkus-catalog".equals(artifactId)) {
+                                    if (VersionHelper.isGE(v, MINIMUM_QUARKUS_VERSION)) {
+                                        String cv = resolveCamelVersionByQuarkusVersion(v, repository);
+                                        if (cv != null && VersionHelper.isGE(cv, minimumVersion)) {
+                                            answer.add(new String[] { cv, v });
+                                        }
+                                    }
+                                } else {
+                                    answer.add(new String[] { v, null });
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace(); // TODO:
+            String msg = "Cannot resolve available versions in " + repository.getUrl();
+            throw new DownloadException(msg, e);
+        }
+
+        return answer;
+    }
+
+    private String resolveCamelVersionByQuarkusVersion(String quarkusVersion, RemoteRepository repository) throws Exception {
+        String gav = "org.apache.camel.quarkus" + ":" + "camel-quarkus" + ":pom:" + quarkusVersion;
+        // always include maven central
+        List<RemoteRepository> repos;
+        if (repository == remoteRepositories.get(0)) {
+            repos = List.of(repository);
+        } else {
+            repos = List.of(remoteRepositories.get(0), repository);
+        }
+        List<MavenArtifact> artifacts = resolveDependenciesViaAether(List.of(gav), repos, false);
+        if (!artifacts.isEmpty()) {
+            MavenArtifact ma = artifacts.get(0);
+            if (ma != null && ma.getFile() != null) {
+                String name = ma.getFile().getAbsolutePath();
+                File file = new File(name);
+                if (file.exists()) {
+                    DocumentBuilderFactory dbf = XmlHelper.createDocumentBuilderFactory();
+                    DocumentBuilder db = dbf.newDocumentBuilder();
+                    Document dom = db.parse(file);
+                    // the camel version is in <parent>
+                    NodeList nl = dom.getElementsByTagName("parent");
+                    if (nl.getLength() == 1) {
+                        Element node = (Element) nl.item(0);
+                        return node.getElementsByTagName("version").item(0).getTextContent();
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private String resolveSpringBootVersionByCamelVersion(String camelVersion, RemoteRepository repository) throws Exception {
+        String gav = "org.apache.camel.springboot" + ":" + "spring-boot" + ":pom:" + camelVersion;
+        // always include maven central
+        List<RemoteRepository> repos;
+        if (repository == remoteRepositories.get(0)) {
+            repos = List.of(repository);
+        } else {
+            repos = List.of(remoteRepositories.get(0), repository);
+        }
+        List<MavenArtifact> artifacts = resolveDependenciesViaAether(List.of(gav), repos, false);
+        if (!artifacts.isEmpty()) {
+            MavenArtifact ma = artifacts.get(0);
+            if (ma != null && ma.getFile() != null) {
+                String name = ma.getFile().getAbsolutePath();
+                File file = new File(name);
+                if (file.exists()) {
+                    DocumentBuilderFactory dbf = XmlHelper.createDocumentBuilderFactory();
+                    DocumentBuilder db = dbf.newDocumentBuilder();
+                    Document dom = db.parse(file);
+                    // the camel version is in <properties>
+                    NodeList nl = dom.getElementsByTagName("properties");
+                    if (nl.getLength() > 0) {
+                        Element node = (Element) nl.item(0);
+                        return node.getElementsByTagName("spring-boot-version").item(0).getTextContent();
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
     private static class AcceptAllDependencyFilter implements DependencyFilter {
         @Override
         public boolean accept(DependencyNode node, List<DependencyNode> parents) {
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/VersionHelper.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/VersionHelper.java
similarity index 69%
copy from dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/VersionHelper.java
copy to dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/VersionHelper.java
index cc22c2985e5..5ada3e4c766 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/VersionHelper.java
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/VersionHelper.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.dsl.jbang.core.common;
+package org.apache.camel.main.util;
 
 import org.apache.camel.util.StringHelper;
 
@@ -24,8 +24,12 @@ public final class VersionHelper {
     }
 
     public static boolean isGE(String source, String target) {
+        return compare(source, target) >= 0;
+    }
+
+    public static int compare(String source, String target) {
         if (source == null || target == null) {
-            return false;
+            return 0;
         }
         String s1 = StringHelper.before(source, ".");
         String s2 = StringHelper.after(source, ".");
@@ -39,7 +43,18 @@ public final class VersionHelper {
             t1 = StringHelper.before(target, ",");
             t2 = StringHelper.after(target, ",");
         }
-
+        String s3 = StringHelper.after(s2, ".");
+        if (s3 != null) {
+            s2 = StringHelper.before(s2, ".");
+        } else {
+            s3 = "";
+        }
+        String t3 = StringHelper.after(t2, ".");
+        if (t3 != null) {
+            t2 = StringHelper.before(t2, ".");
+        } else {
+            t3 = "";
+        }
         // convert to 2-digit numbers
         if (s1.length() < 2) {
             s1 = "0" + s1;
@@ -47,16 +62,25 @@ public final class VersionHelper {
         if (s2.length() < 2) {
             s2 = "0" + s2;
         }
+        if (s2.length() < 2) {
+            s2 = "0" + s2;
+        }
+        if (s3.length() < 2) {
+            s3 = "0" + s3;
+        }
         if (t1.length() < 2) {
             t1 = "0" + t1;
         }
         if (t2.length() < 2) {
             t2 = "0" + t2;
         }
+        if (t3.length() < 2) {
+            t3 = "0" + t3;
+        }
 
-        String s = s1 + s2;
-        String t = t1 + t2;
-        int n = s.compareTo(t);
-        return n >= 0;
+        String s = s1 + s2 + s3;
+        String t = t1 + t2 + t3;
+        return s.compareTo(t);
     }
+
 }
diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/XmlHelper.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/XmlHelper.java
new file mode 100644
index 00000000000..52b843abfcf
--- /dev/null
+++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/XmlHelper.java
@@ -0,0 +1,63 @@
+/*
+ * 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.main.util;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.camel.util.ObjectHelper;
+
+public final class XmlHelper {
+
+    private XmlHelper() {
+    }
+
+    public static DocumentBuilderFactory createDocumentBuilderFactory() {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        factory.setIgnoringElementContentWhitespace(true);
+        factory.setIgnoringComments(true);
+        try {
+            // Set secure processing
+            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
+        } catch (ParserConfigurationException e) {
+        }
+        try {
+            // Disable the external-general-entities by default
+            factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+        } catch (ParserConfigurationException e) {
+        }
+        try {
+            // Disable the external-parameter-entities by default
+            factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+        } catch (ParserConfigurationException e) {
+        }
+        // setup the SecurityManager by default if it's apache xerces
+        try {
+            Class<?> smClass = ObjectHelper.loadClass("org.apache.xerces.util.SecurityManager");
+            if (smClass != null) {
+                Object sm = smClass.getDeclaredConstructor().newInstance();
+                // Here we just use the default setting of the SeurityManager
+                factory.setAttribute("http://apache.org/xml/properties/security-manager", sm);
+            }
+        } catch (Exception e) {
+        }
+        return factory;
+    }
+
+}