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

View File

@ -1,5 +1,7 @@
package run.halo.app.utils; package run.halo.app.utils;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -21,7 +23,7 @@ import run.halo.app.model.support.HaloConst;
@ToString @ToString
@EqualsAndHashCode @EqualsAndHashCode
@Slf4j @Slf4j
public class Version implements Comparable<Version> { public class Version {
/** /**
* Regex expression. * Regex expression.
@ -156,25 +158,59 @@ public class Version implements Comparable<Version> {
StringUtils.isNotBlank(preReleaseMajor) ? Long.parseLong(preReleaseMajor) : null)); 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 // compare major
int majorCompare = Long.compare(major, anotherVersion.major); int majorCompare = Long.compare(major, targetVersion.major);
if (majorCompare != 0) { if (majorCompare != 0) {
return majorCompare; return majorCompare > 0;
} }
// compare minor // compare minor
int minorCompare = Long.compare(minor, anotherVersion.minor); int minorCompare = Long.compare(minor, targetVersion.minor);
if (minorCompare != 0) { if (minorCompare != 0) {
return minorCompare; return minorCompare > 0;
} }
// compare patch // compare patch
int patchCompare = Long.compare(patch, anotherVersion.patch); int patchCompare = Long.compare(patch, targetVersion.patch);
if (patchCompare != 0) { 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 * @author johnniang
*/ */
public enum PreRelease { public enum PreRelease implements Comparator<PreRelease> {
/**
* Beta.
*/
BETA,
/** /**
* Alpha. * Alpha.
*/ */
ALPHA, ALPHA(1),
/**
* Beta.
*/
BETA(2),
/** /**
* Release candidate. * 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 @Nullable
static PreRelease of(@Nullable String preReleaseStr) { static PreRelease of(@Nullable String preReleaseStr) {
@ -209,5 +255,10 @@ public class Version implements Comparable<Version> {
} }
return null; 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() { 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. * 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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; 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 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.Optional;
import java.util.stream.Stream; import java.util.stream.Stream;
import lombok.Data;
import org.apache.commons.lang3.RandomUtils; import org.apache.commons.lang3.RandomUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
@ -118,13 +123,13 @@ class VersionTest {
String version = major + "." + minor + "." + patch + "-alpha." + preReleaseMajor; String version = major + "." + minor + "." + patch + "-alpha." + preReleaseMajor;
Optional<Version> versionOpt = Version.resolve(version); Optional<Version> versionOpt = Version.resolve(version);
assertTrue(versionOpt.isPresent()); assertTrue(versionOpt.isPresent());
assertEquals(new Version(major, minor, patch, Version.PreRelease.ALPHA, preReleaseMajor), assertEquals(new Version(major, minor, patch, ALPHA, preReleaseMajor),
versionOpt.get()); versionOpt.get());
version = major + "." + minor + "." + patch + "-beta." + preReleaseMajor; version = major + "." + minor + "." + patch + "-beta." + preReleaseMajor;
versionOpt = Version.resolve(version); versionOpt = Version.resolve(version);
assertTrue(versionOpt.isPresent()); assertTrue(versionOpt.isPresent());
assertEquals(new Version(major, minor, patch, Version.PreRelease.BETA, preReleaseMajor), assertEquals(new Version(major, minor, patch, BETA, preReleaseMajor),
versionOpt.get()); versionOpt.get());
version = major + "." + minor + "." + patch + "-rc." + preReleaseMajor; version = major + "." + minor + "." + patch + "-rc." + preReleaseMajor;
@ -141,44 +146,72 @@ class VersionTest {
unknownVersionOpt.get()); 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 @NonNull
Version getVersion(String version) { Version getVersion(String version) {
return Version.resolve(version) return Version.resolve(version)
.orElseThrow(() -> new IllegalArgumentException("Invalid 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; package run.halo.app.utils;
import static org.assertj.core.api.Assertions.assertThat; 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 org.junit.jupiter.api.Test;
import run.halo.app.model.support.HaloConst; import run.halo.app.model.support.HaloConst;
@ -14,26 +11,6 @@ import run.halo.app.model.support.HaloConst;
*/ */
class VersionUtilTest { 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 @Test
void hasSameMajorAndMinorVersionTest() { void hasSameMajorAndMinorVersionTest() {
assertThat( assertThat(
@ -53,4 +30,5 @@ class VersionUtilTest {
assertThat(VersionUtil.hasSameMajorAndMinorVersion(null, null)).isTrue(); assertThat(VersionUtil.hasSameMajorAndMinorVersion(null, null)).isTrue();
} }
} }