Skip to content

Commit

Permalink
Fix the problem of theme version compatibility (halo-dev#1705)
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnNiang authored Mar 5, 2022
1 parent 9897999 commit 03006e8
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
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;
Expand Down Expand Up @@ -32,6 +31,7 @@
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.
Expand Down Expand Up @@ -187,9 +187,10 @@ public void deleteTheme(ThemeProperty themeProperty) {
@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() {
Expand Down
85 changes: 68 additions & 17 deletions src/main/java/run/halo/app/utils/Version.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -21,7 +23,7 @@
@ToString
@EqualsAndHashCode
@Slf4j
public class Version implements Comparable<Version> {
public class Version {

/**
* Regex expression.
Expand Down Expand Up @@ -156,48 +158,92 @@ public static Optional<Version> resolve(@Nullable String 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;
}

/**
* Pre release enum.
*
* @author johnniang
*/
public enum PreRelease {
public enum PreRelease implements Comparator<PreRelease> {

/**
* Beta.
* Alpha.
*/
BETA,
ALPHA(1),

/**
* Alpha.
* Beta.
*/
ALPHA,
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) {
Expand All @@ -209,5 +255,10 @@ static PreRelease of(@Nullable String preReleaseStr) {
}
return null;
}

@Override
public int compare(PreRelease left, PreRelease right) {
return left.priority - right.priority;
}
}
}
13 changes: 0 additions & 13 deletions src/main/java/run/halo/app/utils/VersionUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
107 changes: 70 additions & 37 deletions src/test/java/run/halo/app/utils/VersionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@

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;
Expand Down Expand Up @@ -118,13 +123,13 @@ void preReleaseVersionResolve() {
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;
Expand All @@ -141,44 +146,72 @@ void unknownVersionTest() {
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));
}
}
24 changes: 1 addition & 23 deletions src/test/java/run/halo/app/utils/VersionUtilTest.java
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -14,26 +11,6 @@
*/
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(
Expand All @@ -53,4 +30,5 @@ void hasSameMajorAndMinorVersionTest() {

assertThat(VersionUtil.hasSameMajorAndMinorVersion(null, null)).isTrue();
}

}

0 comments on commit 03006e8

Please sign in to comment.