diff --git a/CHANGELOG.md b/CHANGELOG.md index ece1614065..32d579f91b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Introduced merge detection for receive hooks ([#1278](https://github.com/scm-manager/scm-manager/pull/1278)) - Anonymous mode for the web ui ([#1284](https://github.com/scm-manager/scm-manager/pull/1284)) +- Check versions of plugin dependencies on plugin installation ([#1283](https://github.com/scm-manager/scm-manager/pull/1283)) ### Fixed - Repository names may not end with ".git" ([#1277](https://github.com/scm-manager/scm-manager/pull/1277)) +- Add preselected value to options in dropdown component if missing ([#1287](https://github.com/scm-manager/scm-manager/pull/1287)) ## [2.3.1] - 2020-08-04 ### Added diff --git a/pom.xml b/pom.xml index 1754034a86..c3b5a43318 100644 --- a/pom.xml +++ b/pom.xml @@ -893,7 +893,7 @@ 3.1.0 2.1.1 - 4.5.5.Final + 4.5.6.Final 1.19.4 2.11.1 4.2.3 diff --git a/scm-core/src/main/java/sonia/scm/plugin/InstalledPluginDescriptor.java b/scm-core/src/main/java/sonia/scm/plugin/InstalledPluginDescriptor.java index faedb2a6f5..9d57c2a5dd 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/InstalledPluginDescriptor.java +++ b/scm-core/src/main/java/sonia/scm/plugin/InstalledPluginDescriptor.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- @@ -37,6 +37,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import java.util.Set; +import java.util.stream.Collectors; //~--- JDK imports ------------------------------------------------------------ @@ -67,7 +68,12 @@ public final class InstalledPluginDescriptor extends ScmModule implements Plugin * @param condition * @param childFirstClassLoader * @param dependencies + * + * @deprecated this constructor uses dependencies with plain strings, + * which is deprecated because the version information is missing. + * This class should not instantiated manually, it is designed to be loaded by jaxb. */ + @Deprecated public InstalledPluginDescriptor(int scmVersion, PluginInformation information, PluginResources resources, PluginCondition condition, boolean childFirstClassLoader, Set dependencies, Set optionalDependencies) @@ -77,8 +83,17 @@ public final class InstalledPluginDescriptor extends ScmModule implements Plugin this.resources = resources; this.condition = condition; this.childFirstClassLoader = childFirstClassLoader; - this.dependencies = dependencies; - this.optionalDependencies = optionalDependencies; + this.dependencies = mapToNameAndVersionSet(dependencies); + this.optionalDependencies = mapToNameAndVersionSet(optionalDependencies); + } + + private static Set mapToNameAndVersionSet(Set dependencies) { + if (dependencies == null){ + return ImmutableSet.of(); + } + return dependencies.stream() + .map(d -> new NameAndVersion(d, null)) + .collect(Collectors.toSet()); } //~--- methods -------------------------------------------------------------- @@ -173,13 +188,19 @@ public final class InstalledPluginDescriptor extends ScmModule implements Plugin * @since 2.0.0 */ @Override - public Set getDependencies() - { - if (dependencies == null) - { + public Set getDependencies() { + return mapToStringSet(getDependenciesWithVersion()); + } + + /** + * Returns name and versions of the plugins which are this plugin depends on. + * @return dependencies with their versions + * @since 2.4.0 + */ + public Set getDependenciesWithVersion() { + if (dependencies == null) { dependencies = ImmutableSet.of(); } - return dependencies; } @@ -193,11 +214,18 @@ public final class InstalledPluginDescriptor extends ScmModule implements Plugin */ @Override public Set getOptionalDependencies() { - if (optionalDependencies == null) - { + return mapToStringSet(getOptionalDependenciesWithVersion()); + } + + /** + * Returns name and versions of the plugins which are this plugin optional depends on. + * @return optional dependencies with their versions + * @since 2.4.0 + */ + public Set getOptionalDependenciesWithVersion() { + if (optionalDependencies == null) { optionalDependencies = ImmutableSet.of(); } - return optionalDependencies; } @@ -205,6 +233,12 @@ public final class InstalledPluginDescriptor extends ScmModule implements Plugin return ImmutableSet.copyOf(Iterables.concat(getDependencies(), getOptionalDependencies())); } + private Set mapToStringSet(Set dependencies) { + return dependencies.stream() + .map(NameAndVersion::getName) + .collect(Collectors.toSet()); + } + /** * Method description * @@ -263,12 +297,12 @@ public final class InstalledPluginDescriptor extends ScmModule implements Plugin /** Field description */ @XmlElement(name = "dependency") @XmlElementWrapper(name = "dependencies") - private Set dependencies; + private Set dependencies; /** Field description */ @XmlElement(name = "dependency") @XmlElementWrapper(name = "optional-dependencies") - private Set optionalDependencies; + private Set optionalDependencies; /** Field description */ @XmlElement(name = "information") diff --git a/scm-core/src/main/java/sonia/scm/plugin/NameAndVersion.java b/scm-core/src/main/java/sonia/scm/plugin/NameAndVersion.java new file mode 100644 index 0000000000..414d3fcf98 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/plugin/NameAndVersion.java @@ -0,0 +1,104 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.plugin; + +import com.google.common.base.Strings; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import sonia.scm.version.Version; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlValue; +import javax.xml.bind.annotation.adapters.XmlAdapter; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.util.Optional; + +/** + * @since 2.4.0 + */ +@Getter +@EqualsAndHashCode +@XmlAccessorType(XmlAccessType.FIELD) +public class NameAndVersion { + + @XmlValue + private String name; + + @XmlAttribute(name = "version") + @XmlJavaTypeAdapter(VersionXmlAdapter.class) + private Version version; + + NameAndVersion() { + // required for jaxb + } + + public NameAndVersion(String name) { + this(name, null); + } + + public NameAndVersion(String name, String version) { + this.name = name; + if (!Strings.isNullOrEmpty(version)) { + this.version = Version.parse(version); + } + } + + public Optional getVersion() { + return Optional.ofNullable(version); + } + + public Version mustGetVersion() { + if (version == null) { + throw new IllegalStateException("version is not set"); + } + return version; + } + + @Override + public String toString() { + return name + (version != null ? ":" + version.getParsedVersion() : ""); + } + + static class VersionXmlAdapter extends XmlAdapter { + + @Override + public Version unmarshal(String v) { + if (Strings.isNullOrEmpty(v)) { + return null; + } + return Version.parse(v); + } + + @Override + public String marshal(Version v) { + if (v != null) { + return v.getUnparsedVersion(); + } + return null; + } + } +} diff --git a/scm-core/src/main/java/sonia/scm/util/ValidationUtil.java b/scm-core/src/main/java/sonia/scm/util/ValidationUtil.java index 0342e9b392..730a08c545 100644 --- a/scm-core/src/main/java/sonia/scm/util/ValidationUtil.java +++ b/scm-core/src/main/java/sonia/scm/util/ValidationUtil.java @@ -47,7 +47,7 @@ public final class ValidationUtil public static final String REGEX_NAME = "^[A-Za-z0-9\\.\\-_][A-Za-z0-9\\.\\-_@]*$"; - public static final String REGEX_REPOSITORYNAME = "(?!^\\.\\.$)(?!^\\.$)(?!.*[\\\\\\[\\]])^[A-Za-z0-9\\.][A-Za-z0-9\\.\\-_]*$"; + public static final String REGEX_REPOSITORYNAME = "(?!^\\.\\.$)(?!^\\.$)(?!.*[\\\\\\[\\]])(?!.*[.]git$)^[A-Za-z0-9\\.][A-Za-z0-9\\.\\-_]*$"; /** Field description */ private static final Pattern PATTERN_REPOSITORYNAME = Pattern.compile(REGEX_REPOSITORYNAME); diff --git a/scm-core/src/main/java/sonia/scm/version/Version.java b/scm-core/src/main/java/sonia/scm/version/Version.java index c0190e6bd2..c6edb05daa 100644 --- a/scm-core/src/main/java/sonia/scm/version/Version.java +++ b/scm-core/src/main/java/sonia/scm/version/Version.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.version; //~--- non-JDK imports -------------------------------------------------------- @@ -268,11 +268,28 @@ public final class Version implements Comparable * * @return true if newer */ - public boolean isNewer(String versionString) - { - Version o = Version.parse(versionString); + public boolean isNewer(String versionString) { + return isNewer(Version.parse(versionString)); + } - return (o != null) && isNewer(o); + /** + * Returns true if the given version is newer or equal. + * @param versionString other version + * @return true if newer + * @since 2.4.0 + */ + public boolean isNewerOrEqual(String versionString) { + return isNewerOrEqual(Version.parse(versionString)); + } + + /** + * Returns true if the given version is newer or equal. + * @param o other version + * @return {@code true} if newer or equal + * @since 2.4.0 + */ + public boolean isNewerOrEqual(Version o) { + return compareTo(o) <= 0; } /** @@ -296,13 +313,31 @@ public final class Version implements Comparable * * @return true if older */ - public boolean isOlder(String versionString) - { - Version o = Version.parse(versionString); - - return (o != null) && isOlder(o); + public boolean isOlder(String versionString) { + return isOlder(Version.parse(versionString)); } + /** + * Returns true if the given version is older or equal. + * @param versionString other version + * @return {@code true} if older or equal + * @since 2.4.0 + */ + public boolean isOlderOrEqual(String versionString) { + return isOlderOrEqual(Version.parse(versionString)); + } + + /** + * Returns true if the given version is older or equal. + * @param o other version + * @return {@code true} if older or equal + * @since 2.4.0 + */ + public boolean isOlderOrEqual(Version o) { + return compareTo(o) >= 0; + } + + /** * Returns true if the version is a snapshot. * diff --git a/scm-core/src/test/java/sonia/scm/plugin/InstalledPluginDescriptorTest.java b/scm-core/src/test/java/sonia/scm/plugin/InstalledPluginDescriptorTest.java new file mode 100644 index 0000000000..ce78d659a5 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/plugin/InstalledPluginDescriptorTest.java @@ -0,0 +1,63 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.plugin; + +import com.google.common.io.Resources; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import javax.xml.bind.JAXB; +import java.net.URL; + +import static org.assertj.core.api.Assertions.assertThat; + +class InstalledPluginDescriptorTest { + + private static InstalledPluginDescriptor descriptor; + + @BeforeAll + @SuppressWarnings("UnstableApiUsage") + static void unmarshal() { + URL resource = Resources.getResource("sonia/scm/plugin/review-plugin.xml"); + descriptor = JAXB.unmarshal(resource, InstalledPluginDescriptor.class); + } + + @Test + void shouldUnmarshallDependencies() { + assertThat(descriptor.getDependencies()).containsOnly("scm-mail-plugin"); + assertThat(descriptor.getOptionalDependencies()).containsOnly("scm-editor-plugin", "scm-landingpage-plugin"); + assertThat(descriptor.getDependenciesInclusiveOptionals()).containsOnly("scm-mail-plugin", "scm-editor-plugin", "scm-landingpage-plugin"); + } + + @Test + void shouldUnmarshallDependenciesWithVersion() { + assertThat(descriptor.getDependenciesWithVersion()).containsOnly(new NameAndVersion("scm-mail-plugin", "2.1.0")); + assertThat(descriptor.getOptionalDependenciesWithVersion()).containsOnly( + new NameAndVersion("scm-landingpage-plugin", "1.0.0"), + new NameAndVersion("scm-editor-plugin") + ); + } + +} diff --git a/scm-core/src/test/java/sonia/scm/util/ValidationUtilTest.java b/scm-core/src/test/java/sonia/scm/util/ValidationUtilTest.java index 184d5c3bd2..a5f81a7150 100644 --- a/scm-core/src/test/java/sonia/scm/util/ValidationUtilTest.java +++ b/scm-core/src/test/java/sonia/scm/util/ValidationUtilTest.java @@ -202,7 +202,9 @@ public class ValidationUtilTest "scm/main", "scm/plugins/git-plugin", "_scm", - "-scm" + "-scm", + "scm.git", + "scm.git.git" }; for (String path : validPaths) { diff --git a/scm-core/src/test/java/sonia/scm/version/VersionTest.java b/scm-core/src/test/java/sonia/scm/version/VersionTest.java index cd222cf57a..b58d355831 100644 --- a/scm-core/src/test/java/sonia/scm/version/VersionTest.java +++ b/scm-core/src/test/java/sonia/scm/version/VersionTest.java @@ -21,96 +21,77 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.version; -//~--- non-JDK imports -------------------------------------------------------- -import org.junit.Test; - -import static org.junit.Assert.*; - -//~--- JDK imports ------------------------------------------------------------ +import org.junit.jupiter.api.Test; import java.util.Arrays; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + /** - * * @author Sebastian Sdorra */ -public class VersionTest -{ +class VersionTest { - /** - * Method description - * - */ @Test - public void parseSimpleVersion() - { + void parseSimpleVersion() { Version v = Version.parse("1.0"); - assertTrue(v.getMajor() == 1); - assertTrue(v.getMinor() == 0); - assertTrue(v.getPatch() == 0); - assertFalse(v.isSnapshot()); - assertTrue(v.getType() == VersionType.RELEASE); - assertEquals(v.getParsedVersion(), "1.0.0"); + assertThat(v.getMajor()).isOne(); + assertThat(v.getMinor()).isZero(); + assertThat(v.getPatch()).isZero(); + assertThat(v.isSnapshot()).isFalse(); + assertThat(v.getType()).isSameAs(VersionType.RELEASE); + assertThat(v.getParsedVersion()).isEqualTo("1.0.0"); // test with snapshot v = Version.parse("1.1-SNAPSHOT"); - assertTrue(v.getMajor() == 1); - assertTrue(v.getMinor() == 1); - assertTrue(v.getPatch() == 0); - assertTrue(v.isSnapshot()); - assertTrue(v.getType() == VersionType.RELEASE); - assertEquals(v.getParsedVersion(), "1.1.0-SNAPSHOT"); + assertThat(v.getMajor()).isOne(); + assertThat(v.getMinor()).isOne(); + assertThat(v.getPatch()).isZero(); + assertThat(v.isSnapshot()).isTrue(); + assertThat(v.getType()).isSameAs(VersionType.RELEASE); + assertThat(v.getParsedVersion()).isEqualTo("1.1.0-SNAPSHOT"); // test with maintenance v = Version.parse("2.3.14"); - assertTrue(v.getMajor() == 2); - assertTrue(v.getMinor() == 3); - assertTrue(v.getPatch() == 14); - assertFalse(v.isSnapshot()); - assertTrue(v.getType() == VersionType.RELEASE); - assertEquals(v.getParsedVersion(), "2.3.14"); + assertThat(v.getMajor()).isEqualTo(2); + assertThat(v.getMinor()).isEqualTo(3); + assertThat(v.getPatch()).isEqualTo(14); + assertThat(v.isSnapshot()).isFalse(); + assertThat(v.getType()).isSameAs(VersionType.RELEASE); + assertThat(v.getParsedVersion()).isEqualTo("2.3.14"); } - /** - * Method description - * - */ @Test - public void parseTypeVersions() - { + void parseTypeVersions() { Version v = Version.parse("1.0-alpha"); - assertTrue(v.getMajor() == 1); - assertTrue(v.getMinor() == 0); - assertTrue(v.getPatch() == 0); - assertFalse(v.isSnapshot()); - assertTrue(v.getType() == VersionType.ALPHA); - assertTrue(v.getTypeVersion() == 1); - assertEquals(v.getParsedVersion(), "1.0.0-alpha1"); + assertThat(v.getMajor()).isOne(); + assertThat(v.getMinor()).isZero(); + assertThat(v.getPatch()).isZero(); + assertThat(v.isSnapshot()).isFalse(); + assertThat(v.getType()).isSameAs(VersionType.ALPHA); + assertThat(v.getTypeVersion()).isOne(); + assertThat(v.getParsedVersion()).isEqualTo("1.0.0-alpha1"); // Test release candidate v = Version.parse("2.1.2-RC3"); - assertTrue(v.getMajor() == 2); - assertTrue(v.getMinor() == 1); - assertTrue(v.getPatch() == 2); - assertFalse(v.isSnapshot()); - assertTrue(v.getType() == VersionType.RELEASE_CANDIDAT); - assertTrue(v.getTypeVersion() == 3); - assertEquals(v.getParsedVersion(), "2.1.2-RC3"); + assertThat(v.getMajor()).isEqualTo(2); + assertThat(v.getMinor()).isEqualTo(1); + assertThat(v.getPatch()).isEqualTo(2); + assertThat(v.isSnapshot()).isFalse(); + assertThat(v.getType()).isSameAs(VersionType.RELEASE_CANDIDAT); + assertThat(v.getTypeVersion()).isEqualTo(3); + assertThat(v.getParsedVersion()).isEqualTo("2.1.2-RC3"); } - /** - * Method description - * - */ @Test - public void testCompareTo() - { + void testCompareTo() { Version[] versions = new Version[9]; versions[0] = Version.parse("2.3.1-SNAPSHOT"); @@ -123,48 +104,45 @@ public class VersionTest versions[7] = Version.parse("2.3"); versions[8] = Version.parse("2.4.6"); Arrays.sort(versions); - assertEquals(versions[0].getParsedVersion(), "2.4.6"); - assertEquals(versions[1].getParsedVersion(), "2.3.1"); - assertEquals(versions[2].getParsedVersion(), "2.3.1-SNAPSHOT"); - assertEquals(versions[3].getParsedVersion(), "2.3.1-RC1"); - assertEquals(versions[4].getParsedVersion(), "2.3.1-beta2"); - assertEquals(versions[5].getParsedVersion(), "2.3.1-beta1"); - assertEquals(versions[6].getParsedVersion(), "2.3.1-alpha2"); - assertEquals(versions[7].getParsedVersion(), "2.3.1-M1"); - assertEquals(versions[8].getParsedVersion(), "2.3.0"); + assertThat(versions[0].getParsedVersion()).isEqualTo("2.4.6"); + assertThat(versions[1].getParsedVersion()).isEqualTo("2.3.1"); + assertThat(versions[2].getParsedVersion()).isEqualTo("2.3.1-SNAPSHOT"); + assertThat(versions[3].getParsedVersion()).isEqualTo("2.3.1-RC1"); + assertThat(versions[4].getParsedVersion()).isEqualTo("2.3.1-beta2"); + assertThat(versions[5].getParsedVersion()).isEqualTo("2.3.1-beta1"); + assertThat(versions[6].getParsedVersion()).isEqualTo("2.3.1-alpha2"); + assertThat(versions[7].getParsedVersion()).isEqualTo("2.3.1-M1"); + assertThat(versions[8].getParsedVersion()).isEqualTo("2.3.0"); } - /** - * Method description - * - */ @Test - public void testIsNewer() - { - assertFalse(Version.parse("1.0").isNewer("1.0.1")); - assertTrue(Version.parse("1.1").isNewer("1.1-alpha1")); - assertTrue(Version.parse("1.1").isNewer("1.1-RC5")); + void testIsNewer() { + assertThat(Version.parse("1.0").isNewer("1.0.1")).isFalse(); + assertThat(Version.parse("1.1").isNewer("1.1-alpha1")).isTrue(); + assertThat(Version.parse("1.1").isNewer("1.1-RC5")).isTrue(); } - /** - * Method description - * - */ @Test - public void testIsOlder() - { - assertFalse(Version.parse("1.0.1").isOlder("1.0")); - assertTrue(Version.parse("1.1-alpha1").isOlder("1.1")); - assertTrue(Version.parse("1.1-RC5").isOlder("1.1")); + void testIsOlder() { + assertThat(Version.parse("1.0.1").isOlder("1.0")).isFalse(); + assertThat(Version.parse("1.1-alpha1").isOlder("1.1")).isTrue(); + assertThat(Version.parse("1.1-RC5").isOlder("1.1")).isTrue(); } - /** - * Method description - * - */ - @Test(expected = VersionParseException.class) - public void testUnparseable() - { - Version.parse("aaaa"); + @Test + void testIsOlderOrEqual() { + assertThat(Version.parse("1.0.0").isOlderOrEqual("1.0.1")).isTrue(); + assertThat(Version.parse("1.0.1").isOlderOrEqual("1.0.1")).isTrue(); + } + + @Test + void testINewerOrEqual() { + assertThat(Version.parse("1.0.1").isNewerOrEqual("1.0.0")).isTrue(); + assertThat(Version.parse("1.0.1").isOlderOrEqual("1.0.1")).isTrue(); + } + + @Test + void testUnparseable() { + assertThrows(VersionParseException.class, () -> Version.parse("aaaa")); } } diff --git a/scm-core/src/test/resources/sonia/scm/plugin/review-plugin.xml b/scm-core/src/test/resources/sonia/scm/plugin/review-plugin.xml new file mode 100644 index 0000000000..dccadb5b12 --- /dev/null +++ b/scm-core/src/test/resources/sonia/scm/plugin/review-plugin.xml @@ -0,0 +1,74 @@ + + + + 2 + + Review + Cloudogu GmbH + Workflow + scm-review-plugin + 2.3.0-SNAPSHOT + Depict a review process with pull requests + + + 2.4.0-SNAPSHOT + + + + + + com.cloudogu.scm.review.emailnotification.EmailNotificationHook + com.cloudogu.scm.review.pullrequest.service.PullRequestRejectedEvent + + + Each {@link Rule} class implementation defines a type of workflow rule.<br> + <br> + Rules applied to your repositories are represented by {@link AppliedRule}s<br> + to support multiple {@link Rule}s of the same type with distinct configuration. + + true + true + com.cloudogu.scm.review.workflow.Rule + + + v2/pull-requests + com.cloudogu.scm.review.config.api.RepositoryConfigResource + + + com.cloudogu.scm.review.pullrequest.service.PullRequestEvent + + + com.cloudogu.scm.review.ProcessChangedFilesHook + + + scm-mail-plugin + + + scm-editor-plugin + scm-landingpage-plugin + + diff --git a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap index 2ce63602df..17ba9f1493 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -38608,6 +38608,42 @@ exports[`Storyshots Forms|Checkbox With HelpText 1`] = ` `; +exports[`Storyshots Forms|DropDown Add preselect if missing in options 1`] = ` +
+ +
+`; + exports[`Storyshots Forms|DropDown Default 1`] = `
The Meaning Of Liff +
`; diff --git a/scm-ui/ui-components/src/forms/DropDown.stories.tsx b/scm-ui/ui-components/src/forms/DropDown.stories.tsx index e8c9006b50..7e1e3ed995 100644 --- a/scm-ui/ui-components/src/forms/DropDown.stories.tsx +++ b/scm-ui/ui-components/src/forms/DropDown.stories.tsx @@ -48,4 +48,14 @@ storiesOf("Forms|DropDown", module) // nothing to do }} /> + )) + .add("Add preselect if missing in options", () => ( + { + // nothing to do + }} + /> )); diff --git a/scm-ui/ui-components/src/forms/DropDown.tsx b/scm-ui/ui-components/src/forms/DropDown.tsx index 8462d5bcb4..c141b35cd5 100644 --- a/scm-ui/ui-components/src/forms/DropDown.tsx +++ b/scm-ui/ui-components/src/forms/DropDown.tsx @@ -36,6 +36,11 @@ type Props = { class DropDown extends React.Component { render() { const { options, optionValues, preselectedOption, className, disabled } = this.props; + + if (preselectedOption && options.filter(o => o === preselectedOption).length === 0) { + options.push(preselectedOption); + } + return (