From b7ce72991428a1126facc8e70a8cbcac757aca74 Mon Sep 17 00:00:00 2001 From: Jeff Mesnil Date: Wed, 1 Jun 2022 15:02:54 +0200 Subject: [PATCH] WIP: add stream.versions field to support multiple version Signed-off-by: Jeff Mesnil --- .../java/org/wildfly/channel/Channel.java | 14 ++++- .../org/wildfly/channel/ChannelRecorder.java | 2 +- .../org/wildfly/channel/ChannelSession.java | 14 +++-- .../main/java/org/wildfly/channel/Stream.java | 35 +++++++++-- .../org/wildfly/channel/channel-schema.json | 9 +++ .../channel/ChannelRecorderTestCase.java | 10 +-- .../channel/ChannelSessionTestCase.java | 62 +++++++++++++++++-- .../ChannelWithRequirementsTestCase.java | 4 +- .../channel/mapping/StreamTestCase.java | 19 ++++++ .../streams/stream-with-versions.yaml | 5 ++ doc/spec.adoc | 6 +- 11 files changed, 151 insertions(+), 29 deletions(-) create mode 100644 core/src/test/resources/streams/stream-with-versions.yaml diff --git a/core/src/main/java/org/wildfly/channel/Channel.java b/core/src/main/java/org/wildfly/channel/Channel.java index 7dfdb893..6baf3bfc 100644 --- a/core/src/main/java/org/wildfly/channel/Channel.java +++ b/core/src/main/java/org/wildfly/channel/Channel.java @@ -33,11 +33,13 @@ import java.util.Optional; import java.util.Set; import java.util.TreeSet; +import java.util.regex.Pattern; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import org.wildfly.channel.spi.MavenVersionsResolver; import org.wildfly.channel.version.VersionMatcher; +import org.wildfly.channel.version.VersionPatternMatcher; /** * Java representation of a Channel. @@ -177,7 +179,7 @@ static class ResolveLatestVersionResult { } - Optional resolveLatestVersion(String groupId, String artifactId, String extension, String classifier) { + Optional resolveLatestVersion(String groupId, String artifactId, String extension, String classifier, String baseVersion) { requireNonNull(groupId); requireNonNull(artifactId); requireNonNull(resolver); @@ -190,7 +192,7 @@ Optional resolveLatestVersion(String groupId, String // we return the latest value from the required channels Map foundVersions = new HashMap<>(); for (Channel requiredChannel : requiredChannels) { - Optional found = requiredChannel.resolveLatestVersion(groupId, artifactId, extension, classifier); + Optional found = requiredChannel.resolveLatestVersion(groupId, artifactId, extension, classifier, baseVersion); if (found.isPresent()) { foundVersions.put(found.get().version, found.get().channel); } @@ -211,6 +213,14 @@ Optional resolveLatestVersion(String groupId, String // if there is a version pattern, we resolve all versions from Maven to find the latest one Set versions = resolver.getAllVersions(groupId, artifactId, extension, classifier); foundVersion = foundStream.get().getVersionComparator().matches(versions); + } else if (stream.getVersions() != null) { + Map versionStreams = stream.getVersions(); + for (Map.Entry entry : versionStreams.entrySet()) { + if (Pattern.compile(entry.getKey()).matcher(baseVersion).matches()) { + Set versions = resolver.getAllVersions(groupId, artifactId, extension, classifier); + foundVersion = new VersionPatternMatcher(entry.getValue()).matches(versions); + } + } } if (foundVersion.isPresent()) { diff --git a/core/src/main/java/org/wildfly/channel/ChannelRecorder.java b/core/src/main/java/org/wildfly/channel/ChannelRecorder.java index 15da075b..5bc587b7 100644 --- a/core/src/main/java/org/wildfly/channel/ChannelRecorder.java +++ b/core/src/main/java/org/wildfly/channel/ChannelRecorder.java @@ -29,7 +29,7 @@ class ChannelRecorder { void recordStream(String groupId, String artifactId, String version) { boolean isRecorded = recordedChannel.getStreams().stream().anyMatch(s -> s.getGroupId().equals(groupId) && s.getArtifactId().equals(artifactId) && s.getVersion().equals(version)); if (!isRecorded) { - recordedChannel.addStream(new Stream(groupId, artifactId, version, null)); + recordedChannel.addStream(new Stream(groupId, artifactId, version, null, null)); } } diff --git a/core/src/main/java/org/wildfly/channel/ChannelSession.java b/core/src/main/java/org/wildfly/channel/ChannelSession.java index 2b23aaf6..396b4bab 100644 --- a/core/src/main/java/org/wildfly/channel/ChannelSession.java +++ b/core/src/main/java/org/wildfly/channel/ChannelSession.java @@ -55,11 +55,12 @@ public ChannelSession(List channels, MavenVersionsResolver.Factory fact * @param artifactId - required * @param extension - can be null * @param classifier - can be null + * @param baseVersion - required * @return the Maven Artifact (with a file corresponding to the artifact). * @throws UnresolvedMavenArtifactException if the latest version can not be resolved or the artifact itself can not be resolved */ - public MavenArtifact resolveMavenArtifact(String groupId, String artifactId, String extension, String classifier) throws UnresolvedMavenArtifactException { - Channel.ResolveLatestVersionResult result = findChannelWithLatestVersion(groupId, artifactId, extension, classifier); + public MavenArtifact resolveMavenArtifact(String groupId, String artifactId, String extension, String classifier, String baseVersion) throws UnresolvedMavenArtifactException { + Channel.ResolveLatestVersionResult result = findChannelWithLatestVersion(groupId, artifactId, extension, classifier, baseVersion); String latestVersion = result.version; Channel channel = result.channel; @@ -98,11 +99,12 @@ public MavenArtifact resolveDirectMavenArtifact(String groupId, String artifactI * @param artifactId - required * @param extension - can be null * @param classifier - can be null + * @param baseVersion - required * @return the latest version if a Maven artifact * @throws UnresolvedMavenArtifactException if the latest version cannot be established */ - public String findLatestMavenArtifactVersion(String groupId, String artifactId, String extension, String classifier) throws UnresolvedMavenArtifactException { - return findChannelWithLatestVersion(groupId, artifactId, extension, classifier).version; + public String findLatestMavenArtifactVersion(String groupId, String artifactId, String extension, String classifier, String baseVersion) throws UnresolvedMavenArtifactException { + return findChannelWithLatestVersion(groupId, artifactId, extension, classifier, baseVersion).version; } @Override @@ -125,12 +127,12 @@ public Channel getRecordedChannel() { return recorder.getRecordedChannel(); } - private Channel.ResolveLatestVersionResult findChannelWithLatestVersion(String groupId, String artifactId, String extension, String classifier) throws UnresolvedMavenArtifactException { + private Channel.ResolveLatestVersionResult findChannelWithLatestVersion(String groupId, String artifactId, String extension, String classifier, String baseVersion) throws UnresolvedMavenArtifactException { requireNonNull(groupId); requireNonNull(artifactId); for (Channel channel : channels) { - Optional result = channel.resolveLatestVersion(groupId, artifactId, extension, classifier); + Optional result = channel.resolveLatestVersion(groupId, artifactId, extension, classifier, baseVersion); if (result.isPresent()) { return result.get(); } diff --git a/core/src/main/java/org/wildfly/channel/Stream.java b/core/src/main/java/org/wildfly/channel/Stream.java index e038e540..c46a6d40 100644 --- a/core/src/main/java/org/wildfly/channel/Stream.java +++ b/core/src/main/java/org/wildfly/channel/Stream.java @@ -19,6 +19,7 @@ import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; import static java.util.Objects.requireNonNull; +import java.util.Map; import java.util.regex.Pattern; import com.fasterxml.jackson.annotation.JsonCreator; @@ -61,17 +62,28 @@ public class Stream implements Comparable { */ private final Pattern versionPattern; + /** + * Multiple versions stream + * The key is a regular expression to express the baseline version. + * The value is a regular expression that matches the latest version of that baseline. + * + * Only one of {@code version}, {@code versionPattern}, {@code versions} must be set. + */ + private final Map versions; + private VersionMatcher versionMatcher; @JsonCreator public Stream(@JsonProperty(value = "groupId", required = true) String groupId, @JsonProperty(value = "artifactId", required = true) String artifactId, @JsonProperty("version") String version, - @JsonProperty("versionPattern") Pattern versionPattern) { + @JsonProperty("versionPattern") Pattern versionPattern, + @JsonProperty("versions") Map versions) { this.groupId = groupId; this.artifactId = artifactId; this.version = version; this.versionPattern = versionPattern; + this.versions = versions; validate(); initVersionMatcher(); } @@ -79,10 +91,12 @@ public Stream(@JsonProperty(value = "groupId", required = true) String groupId, private void initVersionMatcher() { if (version != null) { versionMatcher = new FixedVersionMatcher(version); - } else { + } else if (versionPattern != null ){ requireNonNull(versionPattern); // let's instead find a version matching the pattern versionMatcher = new VersionPatternMatcher(versionPattern); + } else { + //TODO } } @@ -92,10 +106,13 @@ private void validate() { String.format("Invalid stream. the groupId does not accept wildcard '*'")); } - if ((version != null && versionPattern != null) || - (version == null && versionPattern == null )) { + if ((version == null && versionPattern == null && versions == null) || + (version != null && (versionPattern != null || versions != null)) || + (versionPattern != null && (version != null || versions != null)) || + (versions != null && (version != null || versionPattern != null)) + ) { throw new IllegalArgumentException( - String.format("Invalid stream. Only one of version, versionPattern field must be set")); + String.format("Invalid stream. Only one of version, versionPattern, versionStreams field must be set")); } } @@ -117,6 +134,11 @@ public Pattern getVersionPattern() { return versionPattern; } + @JsonInclude(NON_NULL) + public Map getVersions() { + return versions; + } + @JsonIgnore public VersionMatcher getVersionComparator() { return versionMatcher; @@ -128,7 +150,8 @@ public String toString() { "groupId='" + groupId + '\'' + ", artifactId='" + artifactId + '\'' + ", version='" + version + '\'' + - ", versionPattern=" + versionPattern + + ", versionPattern=" + versionPattern + '\'' + + ", versions=" + versions + ", versionComparator=" + versionMatcher + '}'; } diff --git a/core/src/main/resources/org/wildfly/channel/channel-schema.json b/core/src/main/resources/org/wildfly/channel/channel-schema.json index 65613c51..33ee2c2f 100644 --- a/core/src/main/resources/org/wildfly/channel/channel-schema.json +++ b/core/src/main/resources/org/wildfly/channel/channel-schema.json @@ -76,6 +76,11 @@ "versionPattern" : { "description": "VersionPattern of the stream. This is a regular expression that matches any version from this stream. Only one of version, versionPattern must be set.", "type": "string" + }, + "versionStreams" : { + "description": "Multiple versions of the stream. The key are regular expressions that matches the baseline version, the value are regular expresssion to determine the latest versio for that baseline", + "type": "object", + "additionalProperties": { "type": "string" } } }, "required": ["groupId", "artifactId"], @@ -85,7 +90,11 @@ }, { "required": ["versionPattern"] + }, + { + "required": ["versions"] } + ] } } diff --git a/core/src/test/java/org/wildfly/channel/ChannelRecorderTestCase.java b/core/src/test/java/org/wildfly/channel/ChannelRecorderTestCase.java index 28bf7fe9..d355d77b 100644 --- a/core/src/test/java/org/wildfly/channel/ChannelRecorderTestCase.java +++ b/core/src/test/java/org/wildfly/channel/ChannelRecorderTestCase.java @@ -75,12 +75,12 @@ public void testChannelRecorder() throws IOException, UnresolvedMavenArtifactExc .thenReturn(mock(File.class)); try (ChannelSession session = new ChannelSession(channels, factory)) { - session.resolveMavenArtifact("org.wildfly", "wildfly-ee-galleon-pack", null, null); - session.resolveMavenArtifact("org.wildfly.core", "wildfly.core.cli", null, null); - session.resolveMavenArtifact("io.undertow", "undertow-core", null, null); - session.resolveMavenArtifact("io.undertow", "undertow-servlet", null, null); + session.resolveMavenArtifact("org.wildfly", "wildfly-ee-galleon-pack", null, null, "24.0.0.Final"); + session.resolveMavenArtifact("org.wildfly.core", "wildfly.core.cli", null, null, "13.0.0.Final"); + session.resolveMavenArtifact("io.undertow", "undertow-core", null, null, "2.0.0.Final"); + session.resolveMavenArtifact("io.undertow", "undertow-servlet", null, null, "2.0.0.Final"); // This should not be recorded, size should remain 4. - session.resolveMavenArtifact("io.undertow", "undertow-servlet", null, null); + session.resolveMavenArtifact("io.undertow", "undertow-servlet", null, null, "2.0.0.Final"); Channel recordedChannel = session.getRecordedChannel(); System.out.println(ChannelMapper.toYaml(recordedChannel)); diff --git a/core/src/test/java/org/wildfly/channel/ChannelSessionTestCase.java b/core/src/test/java/org/wildfly/channel/ChannelSessionTestCase.java index 5dc8d83e..9dd65ada 100644 --- a/core/src/test/java/org/wildfly/channel/ChannelSessionTestCase.java +++ b/core/src/test/java/org/wildfly/channel/ChannelSessionTestCase.java @@ -20,6 +20,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.mock; @@ -54,7 +55,7 @@ public void testFindLatestMavenArtifactVersion() throws UnresolvedMavenArtifactE when(resolver.getAllVersions("org.wildfly", "wildfly-ee-galleon-pack", null, null)).thenReturn(singleton("25.0.0.Final")); try (ChannelSession session = new ChannelSession(channels, factory)) { - String version = session.findLatestMavenArtifactVersion("org.wildfly", "wildfly-ee-galleon-pack", null, null); + String version = session.findLatestMavenArtifactVersion("org.wildfly", "wildfly-ee-galleon-pack", null, null, "24.0.0.Final"); assertEquals("25.0.0.Final", version); } @@ -78,7 +79,7 @@ public void testFindLatestMavenArtifactVersionThrowsUnresolvedMavenArtifactExcep try (ChannelSession session = new ChannelSession(channels, factory)) { try { - session.findLatestMavenArtifactVersion("org.wildfly", "wildfly-ee-galleon-pack", null, null); + session.findLatestMavenArtifactVersion("org.wildfly", "wildfly-ee-galleon-pack", null, null, "24.0.0.Final"); fail("Must throw a UnresolvedMavenArtifactException"); } catch (UnresolvedMavenArtifactException e) { // pass @@ -107,7 +108,7 @@ public void testResolveLatestMavenArtifact() throws UnresolvedMavenArtifactExcep try (ChannelSession session = new ChannelSession(channels, factory)) { - MavenArtifact artifact = session.resolveMavenArtifact("org.wildfly", "wildfly-ee-galleon-pack", null, null); + MavenArtifact artifact = session.resolveMavenArtifact("org.wildfly", "wildfly-ee-galleon-pack", null, null, "24.0.0.Final"); assertNotNull(artifact); assertEquals("org.wildfly", artifact.getGroupId()); @@ -121,6 +122,57 @@ public void testResolveLatestMavenArtifact() throws UnresolvedMavenArtifactExcep verify(resolver, times(2)).close(); } + + @Test + public void testResolveLatestMavenArtifactWithVersionStreams() throws UnresolvedMavenArtifactException { + List channels = ChannelMapper.fromString("streams:\n" + + " - groupId: org.wildfly\n" + + " artifactId: '*'\n" + + " versions:\n" + + " '25\\..*': '25\\.\\d+\\.\\d+.Final'\n" + + " '26\\..*': '26\\.1\\.\\d+.Final'"); + assertNotNull(channels); + assertEquals(1, channels.size()); + + MavenVersionsResolver.Factory factory = mock(MavenVersionsResolver.Factory.class); + MavenVersionsResolver resolver = mock(MavenVersionsResolver.class); + File resolvedArtifactFile_25 = mock(File.class); + File resolvedArtifactFile_26 = mock(File.class); + + when(factory.create()).thenReturn(resolver); + when(resolver.getAllVersions("org.wildfly", "wildfly-ee-galleon-pack", null, null)).thenReturn(Set.of("25.0.0.Final", "25.0.1.Final", "26.0.0.Final", "26.1.0.Final", "26.1.1.Final", "26.2.0.Final")); + when(resolver.resolveArtifact("org.wildfly", "wildfly-ee-galleon-pack", null, null, "25.0.1.Final")).thenReturn(resolvedArtifactFile_25); + when(resolver.resolveArtifact("org.wildfly", "wildfly-ee-galleon-pack", null, null, "26.1.1.Final")).thenReturn(resolvedArtifactFile_26); + + try (ChannelSession session = new ChannelSession(channels, factory)) { + + + Assertions.assertThrows(UnresolvedMavenArtifactException.class, () -> { + session.resolveMavenArtifact("org.wildfly", "wildfly-ee-galleon-pack", null, null, "24.0.0.Final"); + }); + + MavenArtifact artifact_25 = session.resolveMavenArtifact("org.wildfly", "wildfly-ee-galleon-pack", null, null, "25.0.0.Final"); + assertNotNull(artifact_25); + assertEquals("org.wildfly", artifact_25.getGroupId()); + assertEquals("wildfly-ee-galleon-pack", artifact_25.getArtifactId()); + assertNull(artifact_25.getExtension()); + assertNull(artifact_25.getClassifier()); + assertEquals("25.0.1.Final", artifact_25.getVersion()); + assertEquals(resolvedArtifactFile_25, artifact_25.getFile()); + + MavenArtifact artifact_26 = session.resolveMavenArtifact("org.wildfly", "wildfly-ee-galleon-pack", null, null, "26.0.0.Final"); + assertNotNull(artifact_26); + assertEquals("org.wildfly", artifact_26.getGroupId()); + assertEquals("wildfly-ee-galleon-pack", artifact_26.getArtifactId()); + assertNull(artifact_26.getExtension()); + assertNull(artifact_26.getClassifier()); + assertEquals("26.1.1.Final", artifact_26.getVersion()); + assertEquals(resolvedArtifactFile_26, artifact_26.getFile()); + } + + verify(resolver, times(2)).close(); + } + @Test public void testResolveLatestMavenArtifactThrowUnresolvedMavenArtifactException() { List channels = ChannelMapper.fromString("streams:\n" + @@ -138,7 +190,7 @@ public void testResolveLatestMavenArtifactThrowUnresolvedMavenArtifactException( try (ChannelSession session = new ChannelSession(channels, factory)) { try { - session.resolveMavenArtifact("org.wildfly", "wildfly-ee-galleon-pack", null, null); + session.resolveMavenArtifact("org.wildfly", "wildfly-ee-galleon-pack", null, null, "24.0.0.Final"); fail("Must throw a UnresolvedMavenArtifactException"); } catch (UnresolvedMavenArtifactException e) { // pass @@ -167,7 +219,7 @@ public void testResolveDirectMavenArtifact() throws UnresolvedMavenArtifactExcep try (ChannelSession session = new ChannelSession(channels, factory)) { Assertions.assertThrows(UnresolvedMavenArtifactException.class, () -> { - session.resolveMavenArtifact("org.bar", "bar", null, null); + session.resolveMavenArtifact("org.bar", "bar", null, null, "24.0.0.Final"); }); MavenArtifact artifact = session.resolveDirectMavenArtifact("org.bar", "bar", null, null, "1.0.0.Final"); diff --git a/core/src/test/java/org/wildfly/channel/ChannelWithRequirementsTestCase.java b/core/src/test/java/org/wildfly/channel/ChannelWithRequirementsTestCase.java index 0c430768..90121556 100644 --- a/core/src/test/java/org/wildfly/channel/ChannelWithRequirementsTestCase.java +++ b/core/src/test/java/org/wildfly/channel/ChannelWithRequirementsTestCase.java @@ -66,7 +66,7 @@ public void testChannelWhichRequiresAnotherChannel() throws UnresolvedMavenArtif assertEquals(1, channels.size()); try (ChannelSession session = new ChannelSession(channels, factory)) { - MavenArtifact artifact = session.resolveMavenArtifact("org.example", "foo-bar", null, null); + MavenArtifact artifact = session.resolveMavenArtifact("org.example", "foo-bar", null, null, "1.0.0"); assertNotNull(artifact); assertEquals("org.example", artifact.getGroupId()); @@ -105,7 +105,7 @@ public void testChannelWhichRequiresAnotherVersionedChannel() throws UnresolvedM assertEquals(1, channels.size()); try (ChannelSession session = new ChannelSession(channels, factory)) { - MavenArtifact artifact = session.resolveMavenArtifact("org.example", "foo-bar", null, null); + MavenArtifact artifact = session.resolveMavenArtifact("org.example", "foo-bar", null, null, "1.0.0.Final"); assertNotNull(artifact); assertEquals("org.example", artifact.getGroupId()); diff --git a/core/src/test/java/org/wildfly/channel/mapping/StreamTestCase.java b/core/src/test/java/org/wildfly/channel/mapping/StreamTestCase.java index cbd9755b..af61c608 100644 --- a/core/src/test/java/org/wildfly/channel/mapping/StreamTestCase.java +++ b/core/src/test/java/org/wildfly/channel/mapping/StreamTestCase.java @@ -16,6 +16,7 @@ */ package org.wildfly.channel.mapping; +import static java.util.regex.Pattern.compile; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; @@ -24,6 +25,8 @@ import java.io.IOException; import java.net.URL; +import java.util.Map; +import java.util.regex.Pattern; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; @@ -117,4 +120,20 @@ public void tesVersionAndVersionPatternAreBothDefined() { "versionPattern: \"2\\\\.2\\\\..*\""); }); } + + @Test + public void testValidStreamWithVersionStreams() throws IOException { + ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + URL file = tccl.getResource("streams/stream-with-versions.yaml"); + Stream stream = fromURL(file); + assertEquals("org.example", stream.getGroupId()); + assertEquals("foo", stream.getArtifactId()); + assertNull(stream.getVersion()); + assertNull(stream.getVersionPattern()); + Map versionStreams = stream.getVersions(); + assertEquals(2, versionStreams.size()); + + assertEquals("2\\.4\\..*", versionStreams.get("2\\..*").pattern()); + assertEquals("3\\.0\\.1\\.Final", versionStreams.get("3\\..*").pattern()); + } } diff --git a/core/src/test/resources/streams/stream-with-versions.yaml b/core/src/test/resources/streams/stream-with-versions.yaml new file mode 100644 index 00000000..94caaa03 --- /dev/null +++ b/core/src/test/resources/streams/stream-with-versions.yaml @@ -0,0 +1,5 @@ +groupId: org.example +artifactId: foo +versions: + '2\..*': '2\.4\..*' + '3\..*': '3\.0\.1\.Final' \ No newline at end of file diff --git a/doc/spec.adoc b/doc/spec.adoc index 3b6b9dbf..9f139fd8 100644 --- a/doc/spec.adoc +++ b/doc/spec.adoc @@ -54,8 +54,10 @@ Each element is composed of: ** A required `groupId` that corresponds to Maven GroupId to pull artifacts (it is not allowed to specify `*` for the groupId). ** A required `artifactId` that corresponds to Maven ArtifactId to pull artifacts. Special syntax `*` can be used to match _any_ artifactId. ** One of the following fields (which are mutually exclusive) that provides rules to resolve the Maven artifacts to provision. At most one field must be present in the stream definition. -*** `versionPattern` corresponds to a Pattern through which the available versions are matched (e.g. `2\.2\..*`) *** `version` corresponds to a single version (e.g. `2.2.Final`) +*** `versionPattern` corresponds to a Pattern through which the available versions are matched (e.g. `2\.2\..*`) +*** `versions` corresponds to a map of versions when multiple version of the same stream needs to be determined. The key corresponds to a Pattern that matches the baseline version, the value +corresponds to a Pattern to determine the latest version for this baseline version. A channel does not define the Maven repositories that contain the resolved Maven artifacts from any of its streams. It is up to the provisioning tooling to properly configure the required Maven repositories. @@ -105,4 +107,4 @@ If no stream that matches the artifact have been found, an error is returned to If the stream defines a `version`, the artifact will be resolved based on this version. If that version of the artifact can not be pulled from the Maven repositories, an error is returned to the caller. If the stream defines a `versionPattern`, the version will be determined by querying the version of the artifacts from the -Maven repositories and use the latest version that matches the pattern. If no version matches the pattern, an error is returned to the caller. \ No newline at end of file +Maven repositories and use the latest version that matches the pattern. If no version matches the pattern, an error is returned to the caller.