Fix the problem of theme version compatibility (#1705)

pull/1715/head
John Niang 2022-03-05 20:24:03 +08:00 committed by GitHub
parent 9897999e51
commit 03006e8f35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 146 additions and 96 deletions

View File

@ -4,7 +4,6 @@ import static run.halo.app.model.properties.PrimaryProperties.THEME;
import static run.halo.app.model.support.HaloConst.DEFAULT_THEME_ID;
import static run.halo.app.utils.FileUtils.copyFolder;
import static run.halo.app.utils.FileUtils.deleteFolderQuietly;
import static run.halo.app.utils.VersionUtil.compareVersion;
import java.io.IOException;
import java.nio.file.Path;
@ -32,6 +31,7 @@ import run.halo.app.model.entity.Option;
import run.halo.app.model.support.HaloConst;
import run.halo.app.theme.ThemePropertyScanner;
import run.halo.app.utils.FileUtils;
import run.halo.app.utils.Version;
/**
* Theme repository implementation.
@ -187,9 +187,10 @@ public class ThemeRepositoryImpl
@Override
public boolean checkThemePropertyCompatibility(ThemeProperty themeProperty) {
// check version compatibility
// Not support current halo version.
return StringUtils.isNotEmpty(themeProperty.getRequire())
&& !compareVersion(HaloConst.HALO_VERSION, themeProperty.getRequire());
String requiredVersion = themeProperty.getRequire();
return Version.resolve(HaloConst.HALO_VERSION)
.map(current -> current.compatible(requiredVersion))
.orElse(false);
}
private Path getThemeRootPath() {

View File

@ -1,5 +1,7 @@
package run.halo.app.utils;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -21,7 +23,7 @@ import run.halo.app.model.support.HaloConst;
@ToString
@EqualsAndHashCode
@Slf4j
public class Version implements Comparable<Version> {
public class Version {
/**
* Regex expression.
@ -156,25 +158,59 @@ public class Version implements Comparable<Version> {
StringUtils.isNotBlank(preReleaseMajor) ? Long.parseLong(preReleaseMajor) : null));
}
@Override
public int compareTo(@NonNull Version anotherVersion) {
/**
* Check if the current version is compatible with the target version.
*
* @param target target version must not be blank.
* @return true if the current version is compatible with the target version; false otherwise.
*/
public boolean compatible(String target) {
Version targetVersion = resolve(target).orElse(emptyVersion());
// compare major
int majorCompare = Long.compare(major, anotherVersion.major);
int majorCompare = Long.compare(major, targetVersion.major);
if (majorCompare != 0) {
return majorCompare;
return majorCompare > 0;
}
// compare minor
int minorCompare = Long.compare(minor, anotherVersion.minor);
int minorCompare = Long.compare(minor, targetVersion.minor);
if (minorCompare != 0) {
return minorCompare;
return minorCompare > 0;
}
// compare patch
int patchCompare = Long.compare(patch, anotherVersion.patch);
int patchCompare = Long.compare(patch, targetVersion.patch);
if (patchCompare != 0) {
return patchCompare;
return patchCompare > 0;
}
// if all the major, minor and patch are the same, then compare pre release number
return Long.compare(preReleaseMajor, anotherVersion.preReleaseMajor);
// here means that all major, minor and patch number are the same.
if (!this.isPreRelease() || !targetVersion.isPreRelease()) {
return true;
}
// compare pre-release tag
int preReleaseTagCompare =
PreRelease.ALPHA.compare(
Objects.requireNonNull(this.preRelease),
Objects.requireNonNull(targetVersion.preRelease));
if (preReleaseTagCompare != 0) {
return preReleaseTagCompare > 0;
}
// compare pre-release number
long preReleaseNumberCompare = this.preReleaseMajor - targetVersion.preReleaseMajor;
if (preReleaseNumberCompare != 0) {
return preReleaseNumberCompare > 0;
}
return true;
}
/**
* Check if current version is a pre-release version.
*
* @return true if current version is a pre-release version; false otherwise.
*/
public boolean isPreRelease() {
return preRelease != null;
}
/**
@ -182,22 +218,32 @@ public class Version implements Comparable<Version> {
*
* @author johnniang
*/
public enum PreRelease {
/**
* Beta.
*/
BETA,
public enum PreRelease implements Comparator<PreRelease> {
/**
* Alpha.
*/
ALPHA,
ALPHA(1),
/**
* Beta.
*/
BETA(2),
/**
* Release candidate.
*/
RC;
RC(3);
/**
* Lower priority means the pre-release is earlier than others.
* Compare order: ALPHA < BETA < RC
*/
private final int priority;
PreRelease(int priority) {
this.priority = priority;
}
@Nullable
static PreRelease of(@Nullable String preReleaseStr) {
@ -209,5 +255,10 @@ public class Version implements Comparable<Version> {
}
return null;
}
@Override
public int compare(PreRelease left, PreRelease right) {
return left.priority - right.priority;
}
}
}

View File

@ -16,19 +16,6 @@ public final class VersionUtil {
private VersionUtil() {
}
/**
* Compare version.
*
* @param current current version.
* @param require require version.
* @return true or false.
*/
public static boolean compareVersion(String current, String require) {
Version leftVersion = Version.resolve(current).orElse(Version.emptyVersion());
Version rightVersion = Version.resolve(require).orElse(Version.emptyVersion());
return leftVersion.compareTo(rightVersion) >= 0;
}
/**
* Compare two versions to see if they have the same major and minor versions.
*

View File

@ -2,10 +2,15 @@ package run.halo.app.utils;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static run.halo.app.utils.Version.PreRelease.ALPHA;
import static run.halo.app.utils.Version.PreRelease.BETA;
import static run.halo.app.utils.Version.PreRelease.RC;
import java.util.Optional;
import java.util.stream.Stream;
import lombok.Data;
import org.apache.commons.lang3.RandomUtils;
import org.junit.jupiter.api.Test;
import org.springframework.lang.NonNull;
@ -118,13 +123,13 @@ class VersionTest {
String version = major + "." + minor + "." + patch + "-alpha." + preReleaseMajor;
Optional<Version> versionOpt = Version.resolve(version);
assertTrue(versionOpt.isPresent());
assertEquals(new Version(major, minor, patch, Version.PreRelease.ALPHA, preReleaseMajor),
assertEquals(new Version(major, minor, patch, ALPHA, preReleaseMajor),
versionOpt.get());
version = major + "." + minor + "." + patch + "-beta." + preReleaseMajor;
versionOpt = Version.resolve(version);
assertTrue(versionOpt.isPresent());
assertEquals(new Version(major, minor, patch, Version.PreRelease.BETA, preReleaseMajor),
assertEquals(new Version(major, minor, patch, BETA, preReleaseMajor),
versionOpt.get());
version = major + "." + minor + "." + patch + "-rc." + preReleaseMajor;
@ -141,44 +146,72 @@ class VersionTest {
unknownVersionOpt.get());
}
@Test
void compareTest() {
final Version leftVersion = getVersion("1.2.3");
// compare with own
assertEquals(0, leftVersion.compareTo(leftVersion));
// compare with others
Version rightVersion = getVersion("1.2.4");
assertTrue(leftVersion.compareTo(rightVersion) < 0);
rightVersion = getVersion("1.3.3");
assertTrue(leftVersion.compareTo(rightVersion) < 0);
rightVersion = getVersion("2.2.3");
assertTrue(leftVersion.compareTo(rightVersion) < 0);
rightVersion = getVersion("0.2.3");
assertTrue(leftVersion.compareTo(rightVersion) > 0);
rightVersion = getVersion("1.1.3");
assertTrue(leftVersion.compareTo(rightVersion) > 0);
rightVersion = getVersion("1.2.2");
assertTrue(leftVersion.compareTo(rightVersion) > 0);
rightVersion = getVersion("1.2.3-alpha.0");
assertTrue(leftVersion.compareTo(rightVersion) > 0);
rightVersion = getVersion("1.2.4-alpha.0");
assertTrue(leftVersion.compareTo(rightVersion) < 0);
// compare with unknown version
assertTrue(leftVersion.compareTo(getVersion(HaloConst.UNKNOWN_VERSION)) < 0);
}
@NonNull
Version getVersion(String version) {
return Version.resolve(version)
.orElseThrow(() -> new IllegalArgumentException("Invalid version"));
}
@Test
void compatibleTest() {
@Data
class Table {
final String current;
final String required;
final boolean wantCompatible;
}
for (Table tt : new Table[] {
// compatible
new Table("1.5.0", "1.5.0", true),
new Table("1.5.0", "1.4.0", true),
new Table("1.5.0", "0.5.0", true),
new Table("1.5.0-alpha.1", "1.5.0", true),
new Table("1.5.0-beta.1", "1.5.0", true),
new Table("1.5.0-rc.1", "1.5.0", true),
new Table("1.5.0-alpha.2", "1.5.0-alpha.1", true),
new Table("1.5.0-beta.1", "1.5.0-alpha.2", true),
new Table("1.5.0-rc.1", "1.5.0-beta.2", true),
new Table("1.5.0", "1.5.0-alpha.1", true),
new Table("1.5.0", "1.5.0-beta.1", true),
new Table("1.5.0", "1.5.0-rc.1", true),
new Table("1.5.0", "1.5.0-rc.1", true),
new Table("1.5.0", "", true),
new Table("1.5.0", null, true),
new Table(HaloConst.UNKNOWN_VERSION, "1.5.0", true),
// incompatible
new Table("1.5.0", "1.5.1", false),
new Table("1.5.0", "1.6.0", false),
new Table("1.5.0", "2.5.0", false),
new Table("1.5.0-alpha.1", "1.5.0-alpha.2", false),
new Table("1.5.0-alpha.2", "1.5.0-beta.1", false),
new Table("1.5.0-beta.2", "1.5.0-rc.1", false),
}) {
Version current = Version.resolve(tt.current).orElse(null);
assertNotNull(current);
boolean compatible = current.compatible(tt.required);
assertEquals(tt.wantCompatible, compatible,
() -> String.format("Want: %s, but got compatible: %s", tt, compatible));
}
}
@Test
void isPreRelease() {
assertFalse(new Version(0, 0, 0, null, 0L).isPreRelease());
assertTrue(new Version(0, 0, 0, ALPHA, 0L).isPreRelease());
}
@Test
void preReleaseTagCompare() {
assertTrue(ALPHA.compare(ALPHA, BETA) < 0);
assertTrue(ALPHA.compare(ALPHA, RC) < 0);
assertTrue(ALPHA.compare(BETA, ALPHA) > 0);
assertTrue(ALPHA.compare(BETA, RC) < 0);
assertTrue(ALPHA.compare(RC, ALPHA) > 0);
assertTrue(ALPHA.compare(RC, BETA) > 0);
assertEquals(0, ALPHA.compare(ALPHA, ALPHA));
assertEquals(0, ALPHA.compare(BETA, BETA));
assertEquals(0, ALPHA.compare(RC, RC));
}
}

View File

@ -1,10 +1,7 @@
package run.halo.app.utils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.jupiter.api.Test;
import run.halo.app.model.support.HaloConst;
@ -14,26 +11,6 @@ import run.halo.app.model.support.HaloConst;
*/
class VersionUtilTest {
@Test
void compareVersion() {
assertTrue(VersionUtil.compareVersion("1.2.0", "1.1.1"));
assertTrue(VersionUtil.compareVersion("1.2.1", "1.2.0"));
assertTrue(VersionUtil.compareVersion("1.2.0", "1.1.1"));
assertTrue(VersionUtil.compareVersion("1.2.0", "0.4.4"));
assertFalse(VersionUtil.compareVersion("1.1.1", "1.2.0"));
assertFalse(VersionUtil.compareVersion("0.0.1", "1.2.0"));
}
@Test
void unknownVersionCompareTest() {
// build a random version
String randomVersion = String.join(".",
RandomStringUtils.randomNumeric(1),
RandomStringUtils.randomNumeric(2),
RandomStringUtils.randomNumeric(3));
assertTrue(VersionUtil.compareVersion(HaloConst.UNKNOWN_VERSION, randomVersion));
}
@Test
void hasSameMajorAndMinorVersionTest() {
assertThat(
@ -53,4 +30,5 @@ class VersionUtilTest {
assertThat(VersionUtil.hasSameMajorAndMinorVersion(null, null)).isTrue();
}
}