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 7eb751177953 CAMEL-26931: camel-jbang - a Camel version that does not
exist says so and exits with 3 (#29945)
7eb751177953 is described below
commit 7eb7511779530ae642b9557e5ae0fade6f0503c2
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Nov 14 20:35:19 2026 +0100
CAMEL-26931: camel-jbang - a Camel version that does not exist says so and
exits with 3 (#29945)
CAMEL-26931: camel-jbang - VersionSetTest checks that --repo is the
repository the version is checked in
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 9 ++
.../modules/ROOT/pages/camel-jbang-projects.adoc | 3 +
.../jbang-commands/camel-jbang-version-set.adoc | 2 +-
.../META-INF/camel-jbang-commands-metadata.json | 2 +-
.../core/commands/catalog/CatalogBaseCommand.java | 18 ++++
.../jbang/core/commands/version/VersionSet.java | 30 +++++-
.../dsl/jbang/core/common/CamelVersionCheck.java | 101 +++++++++++++++++++++
.../core/commands/version/VersionSetTest.java | 67 ++++++++++++--
.../jbang/core/common/CamelVersionCheckTest.java | 83 +++++++++++++++++
.../src/test/resources/golden/cli-help.txt | 3 +-
.../commands/validate/CatalogVersionMixin.java | 16 ++++
.../camel/dsl/jbang/run/common/CatalogLoader.java | 18 ++++
.../run/common/CatalogLoaderNotFoundTest.java | 86 ++++++++++++++++++
13 files changed, 425 insertions(+), 13 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 ad8cd3d20a85..386a3312bb40 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
@@ -1466,6 +1466,15 @@ with code `1` (fix the route) instead of `70` (internal
error), and prints the e
The stack trace is in the log of the integration, and is no longer printed a
second time on stderr (add `--verbose`
for it). Scripts that checked for `70` after a failed start should check for
`1`.
+=== camel-jbang - a Camel version that does not exist exits with 3
+
+A `--camel-version` that was never released, in `camel catalog`, `camel doc`
and `camel validate`, now says
+`Camel version X not found in <repositories>` and exits with code `3` (not
found) instead of `1` with "Cannot
+resolve dependencies". A Maven repository that cannot be reached still exits
with `1`. `camel version set` checks
+that the version exists before it stores it: a version that does not exist
exits with `3` and is not stored, and when
+the repositories cannot be reached the version is stored with a warning.
`camel version set` without a version,
+`--runtime`, `--repo` or `--reset` is now a usage error (exit code `2`)
instead of doing nothing.
+
=== camel-jbang - a name of a running integration matches only that integration
A name given to `camel stop`, `camel get`, `camel cmd`, `camel trace` and the
other commands for running integrations
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-projects.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang-projects.adoc
index 799b446e1b88..e99189cddf57 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-projects.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-projects.adoc
@@ -456,6 +456,9 @@ camel version set 3.18.2
camel version set --reset
----
+`camel version set` checks in the Maven repositories that the version exists,
and refuses one that does not
+(exit code 3). `--reset` removes the `camel-version`, `kamelets-version`,
`repos` and `runtime` settings.
+
IMPORTANT: You cannot combine `camel version set` with `--camel-version`.
For SNAPSHOT versions:
diff --git
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-version-set.adoc
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-version-set.adoc
index c9b5c7fe4759..dfebcca7e399 100644
---
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-version-set.adoc
+++
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-version-set.adoc
@@ -21,7 +21,7 @@ camel version set [options]
| Option | Description | Default | Type
| `--global` | Use global or local configuration | | boolean
| `--repo,--repos` | Maven repository for downloading the dependencies (Use
commas to separate multiple repositories) | | String
-| `--reset` | Reset by removing any custom version settings | | boolean
+| `--reset` | Reset by removing the version settings: camel-version,
kamelets-version, repos and runtime | | boolean
| `--runtime` | Runtime (camel-main, spring-boot, quarkus) | | RuntimeType
| `-h,--help` | Display this help and exit | | boolean
|===
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
index eace095add53..c4b8e2ec8a85 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
+++
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
@@ -38,7 +38,7 @@
{ "name": "trace", "fullName": "trace", "description": "Tail message
traces from running Camel integrations", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.action.CamelTraceAction", "options":
[ { "names": "--action", "description": "Action to start, stop, clear, list
status, or dump traces", "defaultValue": "status", "javaType":
"java.lang.String", "type": "string" }, { "names": "--ago", "description": "Use
ago instead of yyyy-MM-dd HH:mm:ss in timestamp.", "javaType": "b [...]
{ "name": "transform", "fullName": "transform", "description": "Transform
message or Camel routes", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.TransformCommand", "options": [ {
"names": "-h,--help", "description": "Display this help and exit", "javaType":
"boolean", "type": "boolean" } ], "subcommands": [ { "name": "dataweave",
"fullName": "transform dataweave", "description": "Convert DataWeave scripts to
DataSonnet format", "sourceClass": "org.apache.camel.dsl.jbang.c [...]
{ "name": "update", "fullName": "update", "description": "Update Camel
project", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.update.UpdateCommand", "options": [ {
"names": "-h,--help", "description": "Display this help and exit", "javaType":
"boolean", "type": "boolean" } ], "subcommands": [ { "name": "list",
"fullName": "update list", "description": "List available update versions for
Camel and its runtime variants", "sourceClass":
"org.apache.camel.dsl.jbang.core.comma [...]
- { "name": "version", "fullName": "version", "description": "Manage Camel
versions", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.version.VersionCommand", "options": [
{ "names": "-h,--help", "description": "Display this help and exit",
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "get",
"fullName": "version get", "description": "Displays current Camel version",
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.version.VersionGet",
"optio [...]
+ { "name": "version", "fullName": "version", "description": "Manage Camel
versions", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.version.VersionCommand", "options": [
{ "names": "-h,--help", "description": "Display this help and exit",
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "get",
"fullName": "version get", "description": "Displays current Camel version",
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.version.VersionGet",
"optio [...]
{ "name": "wrapper", "fullName": "wrapper", "description": "Install Camel
wrapper scripts for version pinning", "sourceClass":
"org.apache.camel.dsl.jbang.core.commands.WrapperCommand", "options": [ {
"names": "--camel-version", "description": "Camel version to pin (defaults to
current version)", "javaType": "java.lang.String", "type": "string" }, {
"names": "--command-name", "description": "The name of the command to use
(camel or camelw)", "defaultValue": "camel", "javaType": "java [...]
]
}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java
index 6c720d18ec5c..8748d1919136 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java
@@ -35,6 +35,8 @@ import
org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
import org.apache.camel.dsl.jbang.core.commands.MavenResolverMixin;
import org.apache.camel.dsl.jbang.core.commands.QuarkusPlatformMixin;
import org.apache.camel.dsl.jbang.core.common.CamelTableColumns;
+import org.apache.camel.dsl.jbang.core.common.CamelVersionCheck;
+import org.apache.camel.dsl.jbang.core.common.CliException;
import org.apache.camel.dsl.jbang.core.common.RowSort;
import org.apache.camel.dsl.jbang.core.common.RuntimeCompletionCandidates;
import org.apache.camel.dsl.jbang.core.common.RuntimeTypeConverter;
@@ -43,6 +45,7 @@ import org.apache.camel.dsl.jbang.core.model.CatalogBaseDTO;
import org.apache.camel.dsl.jbang.run.common.CatalogLoader;
import org.apache.camel.dsl.jbang.run.common.RuntimeType;
import org.apache.camel.dsl.jbang.run.common.VersionHelper;
+import org.apache.camel.main.download.DownloadException;
import org.apache.camel.main.util.SuggestSimilarHelper;
import org.apache.camel.tooling.maven.MavenGav;
import org.apache.camel.tooling.model.ArtifactModel;
@@ -116,6 +119,21 @@ public abstract class CatalogBaseCommand extends
CamelCommand {
static CamelCatalog loadCatalog(
RuntimeType runtime, String camelVersion, QuarkusPlatformMixin
quarkusPlatform, MavenResolverMixin mavenResolver)
throws Exception {
+ try {
+ return doLoadCatalog(runtime, camelVersion, quarkusPlatform,
mavenResolver);
+ } catch (DownloadException e) {
+ // a version that was never released says so, and is not found
(CAMEL-26931)
+ CliException notFound = CamelVersionCheck.notFound(e, runtime,
camelVersion);
+ if (notFound != null) {
+ throw notFound;
+ }
+ throw e;
+ }
+ }
+
+ private static CamelCatalog doLoadCatalog(
+ RuntimeType runtime, String camelVersion, QuarkusPlatformMixin
quarkusPlatform, MavenResolverMixin mavenResolver)
+ throws Exception {
if (RuntimeType.springBoot == runtime) {
return CatalogLoader.loadSpringBootCatalog(mavenResolver.repos(),
camelVersion, mavenResolver.download());
} else if (RuntimeType.quarkus == runtime) {
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionSet.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionSet.java
index f006d8ebced9..51785631cc30 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionSet.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/version/VersionSet.java
@@ -16,11 +16,14 @@
*/
package org.apache.camel.dsl.jbang.core.commands.version;
+import java.util.function.BiFunction;
+
import org.aesh.command.CommandDefinition;
import org.aesh.command.option.Argument;
import org.aesh.command.option.Option;
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.CamelVersionCheck;
import org.apache.camel.dsl.jbang.core.common.CliExitCode;
import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
import org.apache.camel.dsl.jbang.core.common.RuntimeCompletionCandidates;
@@ -46,12 +49,16 @@ public class VersionSet extends CamelCommand {
description = "Maven repository for downloading the dependencies
(Use commas to separate multiple repositories)")
String repo;
- @Option(name = "reset", hasValue = false, description = "Reset by removing
any custom version settings")
+ @Option(name = "reset", hasValue = false,
+ description = "Reset by removing the version settings:
camel-version, kamelets-version, repos and runtime")
boolean reset;
@Option(name = "global", hasValue = false, description = "Use global or
local configuration")
boolean global = true;
+ /** Whether a version exists, given the version and the extra
repositories: true, false, or null when unknown. */
+ BiFunction<String, String, Boolean> versionCheck =
CamelVersionCheck::exists;
+
public VersionSet(CamelJBangMain main) {
super(main);
}
@@ -59,6 +66,27 @@ public class VersionSet extends CamelCommand {
@Override
public Integer doCall() throws Exception {
boolean local = !global;
+ if (!reset && version == null && repo == null && runtime == null) {
+ printer().printErr("Give the Camel version to set, --runtime,
--repo, or --reset");
+ return CliExitCode.USAGE;
+ }
+ if (!reset && version != null) {
+ // a version that was never released fails every command that uses
it later (CAMEL-26931)
+ String repos = repo;
+ if (repos == null) {
+ String[] stored = new String[1];
+ CommandLineHelper.loadProperties(p -> stored[0] =
p.getProperty("repos"), local, printer());
+ repos = stored[0];
+ }
+ Boolean exists = versionCheck.apply(version, repos);
+ if (Boolean.FALSE.equals(exists)) {
+ printer().printErr("Camel version " + version + " not found
(camel version list shows the released versions)");
+ return CliExitCode.NOT_FOUND;
+ } else if (exists == null) {
+ printer().printDiagnostic("Cannot check that Camel version " +
version
+ + " exists: the Maven repositories
cannot be reached");
+ }
+ }
CommandLineHelper.createPropertyFile(local);
boolean ok = CommandLineHelper.loadProperties(properties -> {
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CamelVersionCheck.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CamelVersionCheck.java
new file mode 100644
index 000000000000..e3a79ca1746b
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CamelVersionCheck.java
@@ -0,0 +1,101 @@
+/*
+ * 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.common;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.camel.dsl.jbang.run.common.CatalogLoader;
+import org.apache.camel.dsl.jbang.run.common.RuntimeType;
+import org.apache.camel.main.download.DownloadException;
+import org.apache.camel.main.download.MavenDependencyDownloader;
+import org.apache.camel.tooling.maven.MavenResolutionException;
+
+/**
+ * Tells a Camel version that does not exist from a Maven repository that
cannot be reached, so a wrong
+ * {@code --camel-version} says so and exits with {@link
CliExitCode#NOT_FOUND} (CAMEL-26931).
+ */
+public final class CamelVersionCheck {
+
+ private CamelVersionCheck() {
+ }
+
+ /**
+ * The error for a {@code --camel-version} that the Maven repositories say
does not exist, or null when the
+ * exception is something else. With the Quarkus runtime the version picks
a platform, which reports its own errors.
+ *
+ * @param e the exception of loading the catalog of the version
+ * @param runtime the runtime, or null for Camel Main
+ * @param version the version, or null for the version of the CLI
+ */
+ public static CliException notFound(Throwable e, RuntimeType runtime,
String version) {
+ if (RuntimeType.quarkus == runtime) {
+ return null;
+ }
+ return notFound(e, RuntimeType.springBoot == runtime ? "Camel Spring
Boot" : "Camel", version);
+ }
+
+ /**
+ * The error for a version that the Maven repositories say does not exist,
or null when the exception is something
+ * else, such as a repository that cannot be reached.
+ *
+ * @param e the exception of loading the artifacts of the version
+ * @param what what the version is of, such as {@code Camel} or {@code
Camel Spring Boot}
+ * @param version the version
+ */
+ private static CliException notFound(Throwable e, String what, String
version) {
+ if (version == null || !CatalogLoader.isArtifactNotFound(e)) {
+ return null;
+ }
+ String repos = null;
+ for (Throwable c = e; c != null && repos == null; c = c.getCause()) {
+ if (c instanceof MavenResolutionException mre &&
mre.getRepositories() != null
+ && !mre.getRepositories().isEmpty()) {
+ repos = String.join(", ", mre.getRepositories());
+ }
+ }
+ return new CliException(
+ CliExitCode.NOT_FOUND,
+ what + " version " + version + " not found" + (repos != null ?
" in " + repos : "")
+ + " (camel version list shows the
released versions)",
+ e);
+ }
+
+ /**
+ * Whether the Camel version exists: {@code true} or {@code false} as the
Maven repositories answer, or null when
+ * they cannot be reached.
+ *
+ * @param version the Camel version
+ * @param repos extra Maven repositories (comma separated), or null
+ */
+ public static Boolean exists(String version, String repos) {
+ try (MavenDependencyDownloader downloader = new
MavenDependencyDownloader()) {
+ downloader.setRepositories(repos);
+ downloader.setDownload(true);
+ downloader.start();
+
downloader.resolveDependenciesViaAether(List.of("org.apache.camel:camel-api:pom:"
+ version), Set.of(),
+ false, version.endsWith("-SNAPSHOT"));
+ return true;
+ } catch (DownloadException e) {
+ return CatalogLoader.isArtifactNotFound(e) ? false : null;
+ } catch (IOException e) {
+ // closing the downloader; the answer is known already
+ return true;
+ }
+ }
+}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/version/VersionSetTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/version/VersionSetTest.java
index 5977c7e094bd..61800bde0f9d 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/version/VersionSetTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/version/VersionSetTest.java
@@ -44,7 +44,7 @@ class VersionSetTest extends BaseConfigTestSupport {
void shouldStoreVersionInGlobalConfig() throws Exception {
UserConfigHelper.createUserConfig("");
- VersionSet command = new VersionSet(new
CamelJBangMain().withPrinter(printer));
+ VersionSet command = command();
command.version = "4.10.0";
command.doCall();
@@ -60,11 +60,18 @@ class VersionSetTest extends BaseConfigTestSupport {
void shouldStoreVersionRepoAndRuntimeTogether() throws Exception {
UserConfigHelper.createUserConfig("");
- VersionSet command = new VersionSet(new
CamelJBangMain().withPrinter(printer));
+ VersionSet command = command();
command.version = "4.10.0";
command.repo = "https://repo.example.org/maven";
command.runtime = RuntimeType.springBoot;
+ String[] asked = new String[1];
+ command.versionCheck = (version, repos) -> {
+ asked[0] = repos;
+ return true;
+ };
command.doCall();
+ // the version is checked in the repository that is given
+ assertEquals("https://repo.example.org/maven", asked[0]);
CommandLineHelper.loadProperties(properties -> {
assertEquals("4.10.0", properties.get("camel-version"));
@@ -75,18 +82,55 @@ class VersionSetTest extends BaseConfigTestSupport {
}
@Test
- void shouldLeaveExistingConfigUntouchedWhenNothingProvided() throws
Exception {
+ void shouldRefuseACallWithNothingToSet() throws Exception {
UserConfigHelper.createUserConfig("""
camel-version=4.9.0
""");
- VersionSet command = new VersionSet(new
CamelJBangMain().withPrinter(printer));
- // no version, repo, runtime or reset: doCall must not change the
stored value
- command.doCall();
+ VersionSet command = command();
+ // no version, repo, runtime or reset: it was a silent no-op
(CAMEL-26931)
+ assertEquals(CliExitCode.USAGE, command.doCall());
+ assertTrue(printer.getStderr().contains("Give the Camel version to
set"), printer.getOutput());
+
+ CommandLineHelper.loadProperties(properties -> assertEquals("4.9.0",
properties.get("camel-version")));
+ }
+
+ @Test
+ void shouldRefuseAVersionThatDoesNotExist() throws Exception {
+ UserConfigHelper.createUserConfig("""
+ camel-version=4.9.0
+ """);
+
+ VersionSet command = command();
+ command.version = "9.9.9";
+ command.versionCheck = (version, repos) -> false;
+ assertEquals(CliExitCode.NOT_FOUND, command.doCall());
+ assertTrue(printer.getStderr().contains("Camel version 9.9.9 not
found"), printer.getOutput());
CommandLineHelper.loadProperties(properties -> assertEquals("4.9.0",
properties.get("camel-version")));
}
+ @Test
+ void shouldSetAVersionThatCannotBeCheckedWithAWarning() throws Exception {
+ UserConfigHelper.createUserConfig("""
+ repos=https://repo.example.org/maven
+ """);
+
+ String[] asked = new String[1];
+ VersionSet command = command();
+ command.version = "4.10.0";
+ command.versionCheck = (version, repos) -> {
+ asked[0] = repos;
+ return null;
+ };
+ assertEquals(CliExitCode.OK, command.doCall());
+ assertTrue(printer.getStderr().contains("Cannot check that Camel
version 4.10.0 exists"), printer.getOutput());
+ // the check asks the repositories that are set already
+ assertEquals("https://repo.example.org/maven", asked[0]);
+
+ CommandLineHelper.loadProperties(properties -> assertEquals("4.10.0",
properties.get("camel-version")));
+ }
+
@Test
void shouldRemoveAllSettingsOnReset() throws Exception {
UserConfigHelper.createUserConfig("""
@@ -97,7 +141,7 @@ class VersionSetTest extends BaseConfigTestSupport {
other=keep-me
""");
- VersionSet command = new VersionSet(new
CamelJBangMain().withPrinter(printer));
+ VersionSet command = command();
command.reset = true;
command.doCall();
@@ -113,7 +157,7 @@ class VersionSetTest extends BaseConfigTestSupport {
@Test
void shouldStoreVersionInLocalConfigWhenNotGlobal() throws Exception {
- VersionSet command = new VersionSet(new
CamelJBangMain().withPrinter(printer));
+ VersionSet command = command();
command.version = "4.10.0";
command.global = false;
command.doCall();
@@ -131,7 +175,7 @@ class VersionSetTest extends BaseConfigTestSupport {
String originalContent = Files.readString(
CommandLineHelper.getHomeDir().resolve(CommandLineHelper.USER_CONFIG));
- VersionSet command = new VersionSet(new
CamelJBangMain().withPrinter(printer));
+ VersionSet command = command();
command.version = "4.10.0";
Integer exitCode = command.doCall();
@@ -142,4 +186,9 @@ class VersionSetTest extends BaseConfigTestSupport {
assertEquals(originalContent, afterContent, "malformed config file
must not be overwritten");
}
+ private VersionSet command() {
+ VersionSet command = new VersionSet(new
CamelJBangMain().withPrinter(printer));
+ command.versionCheck = (version, repos) -> true;
+ return command;
+ }
}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/CamelVersionCheckTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/CamelVersionCheckTest.java
new file mode 100644
index 000000000000..e23c6cd7c838
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/CamelVersionCheckTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.common;
+
+import java.net.ConnectException;
+import java.util.List;
+
+import org.apache.camel.dsl.jbang.run.common.RuntimeType;
+import org.apache.camel.main.download.DownloadException;
+import org.apache.camel.tooling.maven.MavenResolutionException;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactRequest;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.transfer.ArtifactNotFoundException;
+import org.eclipse.aether.transfer.ArtifactTransferException;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * A {@code --camel-version} that was never released says so and exits with 3,
instead of "Cannot resolve dependencies"
+ * with exit 1 (CAMEL-26931).
+ */
+class CamelVersionCheckTest {
+
+ private static final DefaultArtifact CATALOG = new
DefaultArtifact("org.apache.camel:camel-core-catalog:9.9.9");
+ private static final RemoteRepository CENTRAL
+ = new RemoteRepository.Builder("central", "default",
"https://repo1.maven.org/maven2").build();
+
+ @Test
+ void aVersionThatWasNeverReleasedIsNotFound() {
+ CliException e = CamelVersionCheck.notFound(download(new
ArtifactNotFoundException(CATALOG, CENTRAL)), null, "9.9.9");
+ assertEquals(CliExitCode.NOT_FOUND, e.getExitCode());
+ assertEquals("Camel version 9.9.9 not found in
https://repo1.maven.org/maven2"
+ + " (camel version list shows the released versions)",
+ e.getMessage());
+
+ CliException sb = CamelVersionCheck.notFound(
+ download(new ArtifactNotFoundException(CATALOG, CENTRAL)),
RuntimeType.springBoot, "9.9.9");
+ assertEquals("Camel Spring Boot version 9.9.9 not found in
https://repo1.maven.org/maven2"
+ + " (camel version list shows the released versions)",
+ sb.getMessage());
+ }
+
+ @Test
+ void aRepositoryThatCannotBeReachedKeepsItsError() {
+ Exception refused = new ArtifactTransferException(CATALOG, CENTRAL,
new ConnectException("Connection refused"));
+ assertNull(CamelVersionCheck.notFound(download(refused), null,
"9.9.9"));
+ }
+
+ @Test
+ void theQuarkusPlatformReportsItsOwnErrors() {
+ assertNull(CamelVersionCheck.notFound(
+ download(new ArtifactNotFoundException(CATALOG, CENTRAL)),
RuntimeType.quarkus, "9.9.9"));
+ }
+
+ /** As {@code MavenDependencyDownloader} throws it: the resolver error in
a MavenResolutionException. */
+ private static DownloadException download(Exception error) {
+ ArtifactResult result = new ArtifactResult(new
ArtifactRequest(CATALOG, List.of(CENTRAL), null));
+ result.addException(error);
+ ArtifactResolutionException are = new
ArtifactResolutionException(List.of(result));
+ MavenResolutionException mre = new
MavenResolutionException(are.getMessage(), are);
+ mre.getRepositories().add("https://repo1.maven.org/maven2");
+ return new DownloadException("Cannot resolve dependencies in
https://repo1.maven.org/maven2", mre);
+ }
+}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/cli-help.txt
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/cli-help.txt
index 9b5fcecb2865..1bd030f2733e 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/cli-help.txt
+++ b/dsl/camel-jbang/camel-jbang-core/src/test/resources/golden/cli-help.txt
@@ -6857,7 +6857,8 @@ Options:
--repo, --repos=<repo>
Maven repository for downloading the dependencies
(Use commas to separate multiple repositories)
- --reset Reset by removing any custom version settings
+ --reset Reset by removing the version settings:
+ camel-version, kamelets-version, repos and runtime
--global Use global or local configuration
<version> Camel version
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/CatalogVersionMixin.java
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/CatalogVersionMixin.java
index e4ded1a62655..4f23380608c9 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/CatalogVersionMixin.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/CatalogVersionMixin.java
@@ -22,12 +22,15 @@ import org.apache.camel.catalog.CamelCatalog;
import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.dsl.jbang.core.commands.MavenResolverMixin;
import org.apache.camel.dsl.jbang.core.commands.QuarkusPlatformMixin;
+import org.apache.camel.dsl.jbang.core.common.CamelVersionCheck;
+import org.apache.camel.dsl.jbang.core.common.CliException;
import org.apache.camel.dsl.jbang.core.common.RuntimeCompletionCandidates;
import org.apache.camel.dsl.jbang.core.common.RuntimeTypeConverter;
import org.apache.camel.dsl.jbang.run.common.CatalogLoader;
import org.apache.camel.dsl.jbang.run.common.QuarkusHelper;
import org.apache.camel.dsl.jbang.run.common.RuntimeType;
import org.apache.camel.dsl.yaml.validator.YamlValidator;
+import org.apache.camel.main.download.DownloadException;
/**
* The Camel version and runtime a validation answers for (CAMEL-24711): the
catalog of that version and runtime, as
@@ -60,6 +63,19 @@ public class CatalogVersionMixin {
* Loads the catalog of the version and runtime asked for; the built-in
catalog when none is.
*/
public Loaded load() throws Exception {
+ try {
+ return doLoad();
+ } catch (DownloadException e) {
+ // a version that was never released says so, and is not found
(CAMEL-26931)
+ CliException notFound = CamelVersionCheck.notFound(e, runtime,
camelVersion);
+ if (notFound != null) {
+ throw notFound;
+ }
+ throw e;
+ }
+ }
+
+ private Loaded doLoad() throws Exception {
if (RuntimeType.springBoot == runtime) {
String version = camelVersion != null ? camelVersion : new
DefaultCamelCatalog().getCatalogVersion();
return new Loaded(
diff --git
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/common/CatalogLoader.java
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/common/CatalogLoader.java
index 7cb1acb0ba7d..37dd38d43cf8 100644
---
a/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/common/CatalogLoader.java
+++
b/dsl/camel-jbang/camel-jbang-run/src/main/java/org/apache/camel/dsl/jbang/run/common/CatalogLoader.java
@@ -43,6 +43,9 @@ import
org.apache.camel.main.download.MavenDependencyDownloader;
import org.apache.camel.support.ObjectHelper;
import org.apache.camel.tooling.maven.MavenArtifact;
import org.apache.camel.tooling.maven.MavenGav;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.transfer.ArtifactNotFoundException;
+import org.eclipse.aether.transfer.RepositoryOfflineException;
public final class CatalogLoader {
@@ -53,6 +56,21 @@ public final class CatalogLoader {
private CatalogLoader() {
}
+ /**
+ * Whether the Maven repositories answered that the artifact does not
exist, such as a Camel version that was never
+ * released. It is false when a repository could not be reached, or is
offline and has not got the artifact.
+ */
+ public static boolean isArtifactNotFound(Throwable e) {
+ for (Throwable c = e; c != null; c = c.getCause()) {
+ if (c instanceof ArtifactResolutionException are) {
+ List<Exception> errors = are.getResults().stream().flatMap(r
-> r.getExceptions().stream()).toList();
+ return !errors.isEmpty() && errors.stream().allMatch(
+ x -> x instanceof ArtifactNotFoundException &&
!(x.getCause() instanceof RepositoryOfflineException));
+ }
+ }
+ return false;
+ }
+
public static CamelCatalog loadCatalog(String repos, String version,
boolean download) throws Exception {
return loadCatalog(repos, version, null, download);
}
diff --git
a/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/common/CatalogLoaderNotFoundTest.java
b/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/common/CatalogLoaderNotFoundTest.java
new file mode 100644
index 000000000000..552e6cad4c7e
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-run/src/test/java/org/apache/camel/dsl/jbang/run/common/CatalogLoaderNotFoundTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.net.ConnectException;
+import java.util.List;
+
+import org.apache.camel.main.download.DownloadException;
+import org.apache.camel.tooling.maven.MavenResolutionException;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactRequest;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.transfer.ArtifactNotFoundException;
+import org.eclipse.aether.transfer.ArtifactTransferException;
+import org.eclipse.aether.transfer.RepositoryOfflineException;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * {@link CatalogLoader#isArtifactNotFound(Throwable)} tells a version that
does not exist from a repository that cannot
+ * be reached (CAMEL-26931).
+ */
+class CatalogLoaderNotFoundTest {
+
+ private static final DefaultArtifact CATALOG = new
DefaultArtifact("org.apache.camel:camel-core-catalog:9.9.9");
+ private static final RemoteRepository CENTRAL
+ = new RemoteRepository.Builder("central", "default",
"https://repo1.maven.org/maven2").build();
+ private static final RemoteRepository CUSTOM
+ = new RemoteRepository.Builder("custom1", "default",
"http://127.0.0.1:9").build();
+
+ @Test
+ void aVersionNoRepositoryHasIsNotFound() {
+ assertThat(CatalogLoader.isArtifactNotFound(download(new
ArtifactNotFoundException(CATALOG, CENTRAL)))).isTrue();
+ }
+
+ @Test
+ void aRepositoryThatCannotBeReachedIsNotANotFound() {
+ Exception refused = new ArtifactTransferException(CATALOG, CUSTOM, new
ConnectException("Connection refused"));
+
assertThat(CatalogLoader.isArtifactNotFound(download(refused))).isFalse();
+ // central has not got it, but the other repository was not asked
+ assertThat(CatalogLoader.isArtifactNotFound(download(new
ArtifactNotFoundException(CATALOG, CENTRAL), refused)))
+ .isFalse();
+ }
+
+ @Test
+ void anOfflineRepositoryIsNotANotFound() {
+ Exception offline = new ArtifactNotFoundException(
+ CATALOG, CENTRAL, "Cannot access central in offline mode", new
RepositoryOfflineException(CENTRAL));
+
assertThat(CatalogLoader.isArtifactNotFound(download(offline))).isFalse();
+ }
+
+ @Test
+ void anotherErrorIsNotANotFound() {
+ assertThat(CatalogLoader.isArtifactNotFound(new
DownloadException("Unknown error"))).isFalse();
+ assertThat(CatalogLoader.isArtifactNotFound(null)).isFalse();
+ }
+
+ /** As {@code MavenDependencyDownloader} throws it: the resolver error in
a MavenResolutionException. */
+ private static DownloadException download(Exception... errors) {
+ ArtifactResult result = new ArtifactResult(new
ArtifactRequest(CATALOG, List.of(CENTRAL, CUSTOM), null));
+ for (Exception e : errors) {
+ result.addException(e);
+ }
+ ArtifactResolutionException are = new
ArtifactResolutionException(List.of(result));
+ MavenResolutionException mre = new
MavenResolutionException(are.getMessage(), are);
+ mre.getRepositories().add("https://repo1.maven.org/maven2");
+ return new DownloadException("Cannot resolve dependencies in
https://repo1.maven.org/maven2", mre);
+ }
+}