mirror of https://github.com/halo-dev/halo
* add release access and branch access(#515) * add release and branch access(#515) * always update to latest release(#515) * #515 * #515 * add testcases(#515) * fix 515 * fix 515 * deal with connection refused * disable disk operation related test * fix #592 * fix #515 & #592 * mockito test * ignored network related test * resolve conflictpull/943/head
parent
cd9aa23706
commit
2b99bd599f
12
build.gradle
12
build.gradle
|
@ -69,9 +69,13 @@ ext {
|
|||
jedisVersion= '3.3.0'
|
||||
zxingVersion = "3.4.0"
|
||||
huaweiObsVersion = "3.19.7"
|
||||
githubApiVersion = "1.84"
|
||||
powermockVersion = "1.6.6"
|
||||
mockitoVersion = "1.10.19"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.kohsuke:github-api:$githubApiVersion"
|
||||
implementation "org.springframework.boot:spring-boot-starter-actuator"
|
||||
implementation "org.springframework.boot:spring-boot-starter-data-jpa"
|
||||
implementation "org.springframework.boot:spring-boot-starter-web"
|
||||
|
@ -131,5 +135,13 @@ dependencies {
|
|||
|
||||
testImplementation "org.springframework.boot:spring-boot-starter-test"
|
||||
|
||||
testCompile group: 'org.mockito', name: 'mockito-all', version: "$mockitoVersion"
|
||||
testCompile group: 'org.powermock', name: 'powermock-api-mockito', version: "$powermockVersion"
|
||||
testCompile group: 'org.powermock', name: 'powermock-module-junit4', version: "$powermockVersion"
|
||||
|
||||
developmentOnly "org.springframework.boot:spring-boot-devtools"
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
|
@ -175,10 +175,39 @@ public class ThemeController {
|
|||
return themeService.fetch(uri);
|
||||
}
|
||||
|
||||
@PostMapping("fetchingBranches")
|
||||
@ApiOperation("Fetches all branches")
|
||||
public List<ThemeProperty> fetchBranches(@RequestParam("uri") String uri) {
|
||||
return themeService.fetchBranches(uri);
|
||||
}
|
||||
|
||||
@PostMapping("fetchingReleases")
|
||||
@ApiOperation("Fetches all releases")
|
||||
public List<ThemeProperty> fetchReleases(@RequestParam("uri") String uri) {
|
||||
return themeService.fetchReleases(uri);
|
||||
}
|
||||
|
||||
@GetMapping("fetchingRelease")
|
||||
@ApiOperation("Fetches a specific release")
|
||||
public ThemeProperty fetchRelease(@RequestParam("uri") String uri, @RequestParam("tag") String tagName) {
|
||||
return themeService.fetchRelease(uri, tagName);
|
||||
}
|
||||
|
||||
@GetMapping("fetchBranch")
|
||||
@ApiOperation("Fetch specific branch")
|
||||
public ThemeProperty fetchBranch(@RequestParam("uri") String uri, @RequestParam("branch") String branchName) {
|
||||
return themeService.fetchBranch(uri, branchName);
|
||||
}
|
||||
|
||||
@GetMapping("fetchLatestRelease")
|
||||
@ApiOperation("Fetch latest release")
|
||||
public ThemeProperty fetchLatestRelease(@RequestParam("uri") String uri) {
|
||||
return themeService.fetchLatestRelease(uri);
|
||||
}
|
||||
|
||||
@PutMapping("fetching/{themeId}")
|
||||
@ApiOperation("Upgrades theme by remote")
|
||||
public ThemeProperty updateThemeByFetching(@PathVariable("themeId") String themeId) {
|
||||
|
||||
return themeService.update(themeId);
|
||||
}
|
||||
|
||||
|
|
|
@ -88,13 +88,23 @@ public interface ThemeService {
|
|||
/**
|
||||
* Theme provider remote name.
|
||||
*/
|
||||
String THEME_PROVIDER_REMOTE_NAME = "theme-provider";
|
||||
String THEME_PROVIDER_REMOTE_NAME = "origin";
|
||||
|
||||
/**
|
||||
* Default remote branch name.
|
||||
*/
|
||||
String DEFAULT_REMOTE_BRANCH = "master";
|
||||
|
||||
/**
|
||||
* Key to access the zip file url which is in the http response
|
||||
*/
|
||||
String ZIP_FILE_KEY = "zipball_url";
|
||||
|
||||
/**
|
||||
* Key to access the tag name which is in the http response
|
||||
*/
|
||||
String TAG_KEY = "tag_name";
|
||||
|
||||
/**
|
||||
* Get theme property by theme id.
|
||||
*
|
||||
|
@ -303,6 +313,53 @@ public interface ThemeService {
|
|||
@NonNull
|
||||
ThemeProperty fetch(@NonNull String uri);
|
||||
|
||||
/**
|
||||
* Fetches the latest release
|
||||
*
|
||||
* @param uri theme remote uri must not be null
|
||||
* @return theme property
|
||||
*/
|
||||
@NonNull
|
||||
ThemeProperty fetchLatestRelease(@NonNull String uri);
|
||||
|
||||
/**
|
||||
* Fetches all the branches info
|
||||
*
|
||||
* @param uri theme remote uri must not be null
|
||||
* @return list of theme properties
|
||||
*/
|
||||
@NonNull
|
||||
List<ThemeProperty> fetchBranches(@NonNull String uri);
|
||||
|
||||
/**
|
||||
* Fetches all the release info
|
||||
*
|
||||
* @param uri theme remote uri must not be null
|
||||
* @return list of theme properties
|
||||
*/
|
||||
@NonNull
|
||||
List<ThemeProperty> fetchReleases(@NonNull String uri);
|
||||
|
||||
/**
|
||||
* Fetches a specific release
|
||||
*
|
||||
* @param uri theme remote uri must not be null
|
||||
* @param tagName release tag name must not be null
|
||||
* @return theme property
|
||||
*/
|
||||
@NonNull
|
||||
ThemeProperty fetchRelease(@NonNull String uri, @NonNull String tagName);
|
||||
|
||||
/**
|
||||
* Fetches a specific branch (clone)
|
||||
*
|
||||
* @param uri theme remote uri must not be null
|
||||
* @param branchName wanted branch must not be null
|
||||
* @return theme property
|
||||
*/
|
||||
@NonNull
|
||||
ThemeProperty fetchBranch(@NonNull String uri, @NonNull String branchName);
|
||||
|
||||
/**
|
||||
* Reloads themes
|
||||
*/
|
||||
|
|
|
@ -493,9 +493,9 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
if (StringUtils.endsWithIgnoreCase(uri, ".zip")) {
|
||||
downloadZipAndUnzip(uri, themeTmpPath);
|
||||
} else {
|
||||
uri = StringUtils.appendIfMissingIgnoreCase(uri, ".git", ".git");
|
||||
String repoUrl = StringUtils.appendIfMissingIgnoreCase(uri, ".git", ".git");
|
||||
// Clone from git
|
||||
GitUtils.cloneFromGit(uri, themeTmpPath);
|
||||
GitUtils.cloneFromGit(repoUrl, themeTmpPath);
|
||||
}
|
||||
|
||||
return add(themeTmpPath);
|
||||
|
@ -506,6 +506,125 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThemeProperty fetchBranch(String uri, String branchName) {
|
||||
Assert.hasText(uri, "Theme remote uri must not be blank");
|
||||
|
||||
Path tmpPath = null;
|
||||
|
||||
try {
|
||||
// Create temp path
|
||||
tmpPath = FileUtils.createTempDirectory();
|
||||
// Create temp path
|
||||
Path themeTmpPath = tmpPath.resolve(HaloUtils.randomUUIDWithoutDash());
|
||||
|
||||
String repoUrl = StringUtils.appendIfMissingIgnoreCase(uri, ".git", ".git");
|
||||
GitUtils.cloneFromGit(repoUrl, themeTmpPath, branchName);
|
||||
|
||||
return add(themeTmpPath);
|
||||
} catch (IOException | GitAPIException e) {
|
||||
throw new ServiceException("主题拉取失败 " + uri, e);
|
||||
} finally {
|
||||
FileUtils.deleteFolderQuietly(tmpPath);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThemeProperty fetchRelease(@NonNull String uri, @NonNull String tagName) {
|
||||
Assert.hasText(uri, "Theme remote uri must not be blank");
|
||||
Assert.hasText(tagName, "Theme remote tagName must not be blank");
|
||||
|
||||
Path tmpPath = null;
|
||||
try {
|
||||
tmpPath = FileUtils.createTempDirectory();
|
||||
|
||||
Path themeTmpPath = tmpPath.resolve(HaloUtils.randomUUIDWithoutDash());
|
||||
|
||||
Map<String, Object> releaseInfo = GithubUtils.getRelease(uri, tagName);
|
||||
|
||||
if (releaseInfo == null) {
|
||||
throw new ServiceException("主题拉取失败" + uri);
|
||||
}
|
||||
|
||||
String zipUrl = (String) releaseInfo.get(ZIP_FILE_KEY);
|
||||
|
||||
downloadZipAndUnzip(zipUrl, themeTmpPath);
|
||||
|
||||
return add(themeTmpPath);
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("主题拉取失败 " + uri, e);
|
||||
} finally {
|
||||
FileUtils.deleteFolderQuietly(tmpPath);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThemeProperty fetchLatestRelease(@NonNull String uri) {
|
||||
Assert.hasText(uri, "Theme remote uri must not be blank");
|
||||
|
||||
Path tmpPath = null;
|
||||
try {
|
||||
tmpPath = FileUtils.createTempDirectory();
|
||||
|
||||
Path themeTmpPath = tmpPath.resolve(HaloUtils.randomUUIDWithoutDash());
|
||||
|
||||
Map<String, Object> releaseInfo = GithubUtils.getLatestRelease(uri);
|
||||
|
||||
if (releaseInfo == null) {
|
||||
throw new ServiceException("主题拉取失败" + uri);
|
||||
}
|
||||
|
||||
String zipUrl = (String) releaseInfo.get(ZIP_FILE_KEY);
|
||||
|
||||
downloadZipAndUnzip(zipUrl, themeTmpPath);
|
||||
|
||||
return add(themeTmpPath);
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("主题拉取失败 " + uri, e);
|
||||
} finally {
|
||||
FileUtils.deleteFolderQuietly(tmpPath);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ThemeProperty> fetchBranches(String uri) {
|
||||
Assert.hasText(uri, "Theme remote uri must not be blank");
|
||||
|
||||
String repoUrl = StringUtils.appendIfMissingIgnoreCase(uri, ".git",".git");
|
||||
List<String> branches = GitUtils.getAllBranches(repoUrl);
|
||||
|
||||
List<ThemeProperty> themeProperties = new ArrayList<>();
|
||||
|
||||
branches.forEach(branch -> {
|
||||
ThemeProperty themeProperty = new ThemeProperty();
|
||||
themeProperty.setBranch(branch);
|
||||
themeProperties.add(themeProperty);
|
||||
});
|
||||
|
||||
return themeProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ThemeProperty> fetchReleases(@NonNull String uri) {
|
||||
Assert.hasText(uri, "Theme remote uri must not be blank");
|
||||
|
||||
List<String> releases = GithubUtils.getReleases(uri);
|
||||
|
||||
List<ThemeProperty> themeProperties = new ArrayList<>();
|
||||
|
||||
if (releases == null) {
|
||||
throw new ServiceException("主题拉取失败");
|
||||
}
|
||||
|
||||
releases.forEach(tagName -> {
|
||||
ThemeProperty themeProperty = new ThemeProperty();
|
||||
themeProperty.setBranch(tagName);
|
||||
themeProperties.add(themeProperty);
|
||||
});
|
||||
|
||||
return themeProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
eventPublisher.publishEvent(new ThemeUpdatedEvent(this));
|
||||
|
@ -519,6 +638,7 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
|
||||
try {
|
||||
pullFromGit(updatingTheme);
|
||||
|
||||
} catch (Exception e) {
|
||||
if (e instanceof ThemeNotSupportException) {
|
||||
throw (ThemeNotSupportException) e;
|
||||
|
@ -597,7 +717,7 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
|
||||
// Get branch
|
||||
String branch = StringUtils.isBlank(themeProperty.getBranch()) ?
|
||||
DEFAULT_REMOTE_BRANCH : themeProperty.getBranch();
|
||||
DEFAULT_REMOTE_BRANCH : themeProperty.getBranch();
|
||||
|
||||
Git git = null;
|
||||
|
||||
|
@ -606,6 +726,13 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
|
||||
Repository repository = git.getRepository();
|
||||
|
||||
// Add all changes
|
||||
git.add()
|
||||
.addFilepattern(".")
|
||||
.call();
|
||||
// Commit the changes
|
||||
git.commit().setMessage("Commit by halo automatically").call();
|
||||
|
||||
RevWalk revWalk = new RevWalk(repository);
|
||||
|
||||
Ref ref = repository.findRef(Constants.HEAD);
|
||||
|
@ -617,38 +744,31 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
// Force to set remote name
|
||||
git.remoteRemove().setRemoteName(THEME_PROVIDER_REMOTE_NAME).call();
|
||||
RemoteConfig remoteConfig = git.remoteAdd()
|
||||
.setName(THEME_PROVIDER_REMOTE_NAME)
|
||||
.setUri(new URIish(themeProperty.getRepo()))
|
||||
.call();
|
||||
|
||||
// Add all changes
|
||||
git.add()
|
||||
.addFilepattern(".")
|
||||
.call();
|
||||
// Commit the changes
|
||||
git.commit().setMessage("Commit by halo automatically").call();
|
||||
.setName(THEME_PROVIDER_REMOTE_NAME)
|
||||
.setUri(new URIish(themeProperty.getRepo()))
|
||||
.call();
|
||||
|
||||
// Check out to specified branch
|
||||
if (!StringUtils.equalsIgnoreCase(branch, git.getRepository().getBranch())) {
|
||||
boolean present = git.branchList()
|
||||
.call()
|
||||
.stream()
|
||||
.map(Ref::getName)
|
||||
.anyMatch(name -> StringUtils.equalsIgnoreCase(name, branch));
|
||||
.call()
|
||||
.stream()
|
||||
.map(Ref::getName)
|
||||
.anyMatch(name -> StringUtils.equalsIgnoreCase(name, branch));
|
||||
|
||||
git.checkout()
|
||||
.setCreateBranch(true)
|
||||
.setForced(!present)
|
||||
.setName(branch)
|
||||
.call();
|
||||
.setCreateBranch(true)
|
||||
.setForced(!present)
|
||||
.setName(branch)
|
||||
.call();
|
||||
}
|
||||
|
||||
// Pull with rebasing
|
||||
PullResult pullResult = git.pull()
|
||||
.setRemote(remoteConfig.getName())
|
||||
.setRemoteBranchName(branch)
|
||||
.setRebase(true)
|
||||
.call();
|
||||
.setRemote(remoteConfig.getName())
|
||||
.setRemoteBranchName(branch)
|
||||
.setRebase(true)
|
||||
.call();
|
||||
|
||||
if (!pullResult.isSuccessful()) {
|
||||
log.debug("Rebase result: [{}]", pullResult.getRebaseResult());
|
||||
|
@ -657,6 +777,9 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
throw new ThemeUpdateException("拉取失败!您与主题作者可能同时更改了同一个文件");
|
||||
}
|
||||
|
||||
String latestTagName = (String) GithubUtils.getLatestRelease(themeProperty.getRepo()).get(TAG_KEY);
|
||||
git.checkout().setName(latestTagName).call();
|
||||
|
||||
// updated successfully.
|
||||
ThemeProperty updatedThemeProperty = getProperty(Paths.get(themeProperty.getThemePath()));
|
||||
|
||||
|
@ -664,9 +787,9 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
if (StringUtils.isNotEmpty(updatedThemeProperty.getRequire()) && !VersionUtil.compareVersion(HaloConst.HALO_VERSION, updatedThemeProperty.getRequire())) {
|
||||
// reset theme version
|
||||
git.reset()
|
||||
.setMode(ResetCommand.ResetType.HARD)
|
||||
.setRef(lastCommit.getName())
|
||||
.call();
|
||||
.setMode(ResetCommand.ResetType.HARD)
|
||||
.setRef(lastCommit.getName())
|
||||
.call();
|
||||
throw new ThemeNotSupportException("新版本主题仅支持 Halo " + updatedThemeProperty.getRequire() + " 以上的版本");
|
||||
}
|
||||
} finally {
|
||||
|
@ -696,7 +819,6 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
}
|
||||
|
||||
log.debug("Downloaded [{}]", zipUrl);
|
||||
|
||||
// Unzip it
|
||||
FileUtils.unzip(downloadResponse.getBody(), targetPath);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import run.halo.app.exception.ForbiddenException;
|
|||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -131,6 +132,19 @@ public class FileUtils {
|
|||
|
||||
zipEntry = zis.getNextEntry();
|
||||
}
|
||||
File targetDir = targetPath.toFile();
|
||||
List<File> files = Arrays.asList(targetDir.listFiles());
|
||||
// if zip file has root file
|
||||
if (files.size() == 1 && files.get(0).isDirectory()) {
|
||||
String rootPath = files.get(0).toPath().toString();
|
||||
String rootFile = rootPath.substring(rootPath.lastIndexOf("/", rootPath.length() - 1) + 1,rootPath.length());
|
||||
List<File> propertyFiles = Arrays.asList(files.get(0).listFiles());
|
||||
for (File propertyFile : propertyFiles) {
|
||||
String filePath = propertyFile.toPath().toString();
|
||||
String destPath = filePath.replace(rootFile, "");
|
||||
Files.copy(propertyFile.toPath(), Paths.get(destPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,13 +3,17 @@ package run.halo.app.utils;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.api.errors.InvalidRemoteException;
|
||||
import org.eclipse.jgit.api.errors.TransportException;
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.storage.file.WindowCacheConfig;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Git utilities.
|
||||
|
@ -59,6 +63,43 @@ public class GitUtils {
|
|||
return git;
|
||||
}
|
||||
|
||||
public static void cloneFromGit(@NonNull String repoUrl, @NonNull Path targetPath, @NonNull String branchName) throws GitAPIException {
|
||||
Assert.hasText(repoUrl, "Repository remote url must not be blank");
|
||||
Assert.notNull(targetPath, "Target path must not be null");
|
||||
|
||||
Git git = null;
|
||||
try {
|
||||
git = Git.cloneRepository()
|
||||
.setURI(repoUrl)
|
||||
.setDirectory(targetPath.toFile())
|
||||
.setBranchesToClone(Arrays.asList("refs/heads/" + branchName))
|
||||
.setBranch("refs/heads/" + branchName)
|
||||
.call();
|
||||
} finally {
|
||||
closeQuietly(git);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> getAllBranches(@NonNull String repoUrl) {
|
||||
List<String> branches = new ArrayList<String>();
|
||||
try {
|
||||
Collection<Ref> refs = Git.lsRemoteRepository()
|
||||
.setHeads(true)
|
||||
.setRemote(repoUrl)
|
||||
.call();
|
||||
for (Ref ref : refs) {
|
||||
branches.add(ref.getName().substring(ref.getName().lastIndexOf("/") + 1, ref.getName().length()));
|
||||
}
|
||||
} catch (InvalidRemoteException e) {
|
||||
log.warn("Git url is not valid", e);
|
||||
} catch (TransportException e) {
|
||||
log.warn("Transport exception", e);
|
||||
} catch (GitAPIException e) {
|
||||
log.warn("Git api exception", e);
|
||||
}
|
||||
return branches;
|
||||
}
|
||||
|
||||
public static void closeQuietly(Git git) {
|
||||
if (git != null) {
|
||||
git.getRepository().close();
|
||||
|
|
|
@ -0,0 +1,368 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.kohsuke.github.*;
|
||||
import run.halo.app.service.ThemeService;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* GithubUtils send request to api.github.com
|
||||
*
|
||||
* @author bigbang019
|
||||
* @date 2020-05-31
|
||||
*/
|
||||
@Slf4j
|
||||
public class GithubUtils {
|
||||
|
||||
/**
|
||||
* The prefix need to remove
|
||||
*/
|
||||
static final String PREFIX = "https://github.com/";
|
||||
|
||||
/**
|
||||
* Get latest release
|
||||
* @param uri repository url must not be null
|
||||
* @return the map object containning tagname and zipfile url
|
||||
*/
|
||||
public static Map<String, Object> getLatestRelease(String uri) {
|
||||
String repoUrl = StringUtils.removeStartIgnoreCase(uri, PREFIX);
|
||||
|
||||
try {
|
||||
GithubLatestRelease githubLatestRelease = new GithubLatestRelease(repoUrl);
|
||||
|
||||
Thread thread = new Thread(githubLatestRelease);
|
||||
|
||||
thread.start();
|
||||
|
||||
thread.join(10 * 1000);
|
||||
|
||||
return githubLatestRelease.result;
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Interrupted", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get release information
|
||||
* @param uri repository url must not be null
|
||||
* @return list of tagname of releases
|
||||
*/
|
||||
public static List<String> getReleases(String uri) {
|
||||
String repoUrl = StringUtils.removeStartIgnoreCase(uri, PREFIX);
|
||||
|
||||
try {
|
||||
GithubReleases githubReleases = new GithubReleases(repoUrl);
|
||||
|
||||
Thread thread = new Thread(githubReleases);
|
||||
|
||||
thread.start();
|
||||
|
||||
thread.join(10 * 1000);
|
||||
|
||||
return githubReleases.result;
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Interrupted", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get release information
|
||||
* @param uri repository url must not be null
|
||||
* @param tagName tag must not be null
|
||||
* @return the map object containning tagname and zipfile url
|
||||
*/
|
||||
public static Map<String, Object> getRelease(String uri, String tagName) {
|
||||
String repoUrl = StringUtils.removeStartIgnoreCase(uri, PREFIX);
|
||||
|
||||
try {
|
||||
GithubRelease githubRelease = new GithubRelease(repoUrl, tagName);
|
||||
|
||||
Thread thread = new Thread(githubRelease);
|
||||
|
||||
thread.start();
|
||||
|
||||
thread.join(10 * 1000);
|
||||
|
||||
return githubRelease.result;
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Interrupted", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content of theme.yaml/theme.yml
|
||||
* @param uri repository url must not be null
|
||||
* @param branch branch must not be null
|
||||
* @return content of the file
|
||||
*/
|
||||
public static String accessThemeProperty(String uri, String branch) {
|
||||
String repoUrl = StringUtils.removeStartIgnoreCase(uri, PREFIX);
|
||||
|
||||
try {
|
||||
GithubFile githubFile = new GithubFile(repoUrl, branch);
|
||||
|
||||
Thread thread = new Thread(githubFile);
|
||||
|
||||
thread.start();
|
||||
|
||||
thread.join(10 * 1000);
|
||||
|
||||
return githubFile.result;
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Interrupted", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class GithubRelease implements Runnable {
|
||||
|
||||
/**
|
||||
* The return result is zip url and tag name etc.
|
||||
*/
|
||||
private HashMap<String, Object> result;
|
||||
|
||||
/**
|
||||
* should be in format of "username/reponame"
|
||||
*/
|
||||
private String repoUrl;
|
||||
|
||||
private String tagName;
|
||||
|
||||
public GithubRelease(String repoUrl, String tagName) {
|
||||
this.repoUrl = repoUrl;
|
||||
this.tagName = tagName;
|
||||
result = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
GitHub gitHub = GitHub.connectAnonymously();
|
||||
GHRepository ghRepository = gitHub.getRepository(repoUrl);
|
||||
List<GHRelease> ghReleaseList = ghRepository.getReleases();
|
||||
|
||||
if (ghReleaseList.size() == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
Optional<GHRelease> res = ghReleaseList.stream()
|
||||
.filter(release -> StringUtils.equalsIgnoreCase(release.getTagName(), tagName))
|
||||
.findFirst();
|
||||
|
||||
if (res.isPresent()) {
|
||||
GHRelease ghRelease = res.get();
|
||||
|
||||
result = new HashMap<String, Object>() {
|
||||
{
|
||||
put(ThemeService.ZIP_FILE_KEY, ghRelease.getZipballUrl());
|
||||
put(ThemeService.TAG_KEY, ghRelease.getTagName());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
} catch (Exception e) {
|
||||
if (e instanceof HttpException) {
|
||||
int code = ((HttpException) e).getResponseCode();
|
||||
if (code != -1) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class GithubReleases implements Runnable {
|
||||
|
||||
private List<String> result;
|
||||
|
||||
private String repoUrl;
|
||||
|
||||
public GithubReleases(String repoUrl) {
|
||||
this.repoUrl = repoUrl;
|
||||
result = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
GitHub gitHub = GitHub.connectAnonymously();
|
||||
GHRepository ghRepository = gitHub.getRepository(repoUrl);
|
||||
List<GHRelease> ghReleaseList = ghRepository.getReleases();
|
||||
|
||||
result = new ArrayList<String>();
|
||||
|
||||
for (GHRelease ghRelease : ghReleaseList) {
|
||||
result.add(ghRelease.getTagName());
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
} catch (Exception e) {
|
||||
if (e instanceof HttpException) {
|
||||
int code = ((HttpException) e).getResponseCode();
|
||||
if (code != -1) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class GithubLatestRelease implements Runnable {
|
||||
|
||||
/**
|
||||
* The return result is zip url and tag name etc.
|
||||
*/
|
||||
private HashMap<String, Object> result;
|
||||
|
||||
/**
|
||||
* should be in format of "username/reponame"
|
||||
*/
|
||||
private String repoUrl;
|
||||
|
||||
public GithubLatestRelease(String repoUrl) {
|
||||
this.repoUrl = repoUrl;
|
||||
result = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
GitHub gitHub = GitHub.connectAnonymously();
|
||||
GHRepository ghRepository = gitHub.getRepository(repoUrl);
|
||||
List<GHRelease> ghReleaseList = ghRepository.getReleases();
|
||||
|
||||
if (ghReleaseList.size() == 0) {
|
||||
break;
|
||||
}
|
||||
GHRelease ghRelease = ghReleaseList.get(0);
|
||||
|
||||
result = new HashMap<String, Object>() {
|
||||
{
|
||||
put(ThemeService.ZIP_FILE_KEY, ghRelease.getZipballUrl());
|
||||
put(ThemeService.TAG_KEY, ghRelease.getTagName());
|
||||
}
|
||||
};
|
||||
|
||||
break;
|
||||
|
||||
} catch (Exception e) {
|
||||
if (e instanceof HttpException) {
|
||||
int code = ((HttpException) e).getResponseCode();
|
||||
if (code != -1) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class GithubFile implements Runnable {
|
||||
|
||||
/**
|
||||
* result is file content
|
||||
*/
|
||||
private String result;
|
||||
|
||||
/**
|
||||
* should be in format of "username/reponame"
|
||||
*/
|
||||
private String repoUrl;
|
||||
|
||||
/**
|
||||
* the branch name
|
||||
*/
|
||||
private String branch;
|
||||
|
||||
public GithubFile(String repoUrl, String branch) {
|
||||
this.repoUrl = repoUrl;
|
||||
this.branch = branch;
|
||||
result = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
GitHub gitHub = GitHub.connectAnonymously();
|
||||
|
||||
GHRepository ghRepository = gitHub.getRepository(repoUrl);
|
||||
|
||||
GHContent ghContent = null;
|
||||
|
||||
for (String themePropertyFile : ThemeService.THEME_PROPERTY_FILE_NAMES) {
|
||||
|
||||
try {
|
||||
ghContent = ghRepository.getFileContent(themePropertyFile, branch);
|
||||
} catch (FileNotFoundException e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (ghContent == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
result = ghContent.getContent();
|
||||
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
if (e instanceof HttpException) {
|
||||
int code = ((HttpException) e).getResponseCode();
|
||||
if (code != -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import run.halo.app.cache.AbstractStringCacheStore;
|
||||
import run.halo.app.model.entity.Option;
|
||||
import run.halo.app.model.properties.QiniuOssProperties;
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
package run.halo.app.service.impl;
|
||||
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.*;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import run.halo.app.cache.AbstractStringCacheStore;
|
||||
import run.halo.app.config.properties.HaloProperties;
|
||||
import run.halo.app.handler.theme.config.ThemeConfigResolver;
|
||||
import run.halo.app.handler.theme.config.support.ThemeProperty;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.utils.FileUtils;
|
||||
import run.halo.app.utils.GitUtils;
|
||||
import run.halo.app.utils.GithubUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({GitUtils.class, GithubUtils.class, FileUtils.class, ThemeServiceImpl.class})
|
||||
public class ThemeServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private HaloProperties haloProperties;
|
||||
|
||||
@Mock
|
||||
private OptionService optionService;
|
||||
|
||||
@Mock
|
||||
private AbstractStringCacheStore cacheStore;
|
||||
|
||||
@Mock
|
||||
private ThemeConfigResolver themeConfigResolver;
|
||||
|
||||
@Mock
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@Mock
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@InjectMocks
|
||||
public ThemeServiceImpl themeService;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
//Static Method
|
||||
PowerMockito.mockStatic(GithubUtils.class);
|
||||
PowerMockito.mockStatic(GitUtils.class);
|
||||
PowerMockito.mockStatic(FileUtils.class);
|
||||
|
||||
PowerMockito.doReturn(Arrays.asList("master", "dev")).when(GitUtils.class, "getAllBranches", Mockito.any(String.class));
|
||||
PowerMockito.doNothing().when(GitUtils.class, "cloneFromGit", Mockito.any(String.class), Mockito.any(Path.class));
|
||||
|
||||
PowerMockito.doReturn("propertyContent").when(GithubUtils.class, "accessThemeProperty", Mockito.any(String.class), Mockito.any(String.class));
|
||||
|
||||
PowerMockito.doReturn(new File("tmpPath").toPath()).when(FileUtils.class, "createTempDirectory");
|
||||
PowerMockito.doNothing().when(FileUtils.class, "deleteFolderQuietly", Mockito.any(Path.class));
|
||||
|
||||
//Method
|
||||
themeService = PowerMockito.spy(new ThemeServiceImpl(haloProperties, optionService, cacheStore, themeConfigResolver, restTemplate, eventPublisher));
|
||||
|
||||
Mockito.doNothing().when(eventPublisher).publishEvent(Mockito.any(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fetchGitTest() throws Exception {
|
||||
String uri = "https://github.com/halo-dev/halo-theme-pinghsu";
|
||||
PowerMockito.doNothing().when(themeService, "downloadZipAndUnzip", Mockito.any(String.class), Mockito.any(Path.class));
|
||||
PowerMockito.doReturn(new ThemeProperty()).when(themeService, "add", Mockito.any(Path.class));
|
||||
ThemeProperty themeProperty = themeService.fetch(uri);
|
||||
Assert.assertNotNull(themeProperty);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fetchZipTest() throws Exception {
|
||||
String uri = "https://github.com/halo-dev/halo-theme-pinghsu/archive/master.zip";
|
||||
PowerMockito.doNothing().when(themeService, "downloadZipAndUnzip", Mockito.any(String.class), Mockito.any(Path.class));
|
||||
PowerMockito.doReturn(new ThemeProperty()).when(themeService, "add", Mockito.any(Path.class));
|
||||
ThemeProperty themeProperty = themeService.fetch(uri);
|
||||
Assert.assertNotNull(themeProperty);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fetchBranchesTest() {
|
||||
String uri = "https://github.com/halo-dev/halo-theme-hux";
|
||||
|
||||
List<ThemeProperty> themeProperties = themeService.fetchBranches(uri);
|
||||
|
||||
Assert.assertNotNull(themeProperties);
|
||||
Assert.assertEquals(themeProperties.size(), 2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void fetchBranchTest() throws Exception {
|
||||
String uri = "https://github.com/halo-dev/halo-theme-casper";
|
||||
String branch = "master";
|
||||
PowerMockito.doNothing().when(themeService, "downloadZipAndUnzip", Mockito.any(String.class), Mockito.any(Path.class));
|
||||
PowerMockito.doReturn(new ThemeProperty()).when(themeService, "add", Mockito.any(Path.class));
|
||||
ThemeProperty themeProperty = themeService.fetchBranch(uri, branch);
|
||||
Assert.assertNotNull(themeProperty);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fetchLatestReleaseTest() throws Exception {
|
||||
String uri = "https://github.com/halo-dev/halo-theme-casper";
|
||||
PowerMockito.doNothing().when(themeService, "downloadZipAndUnzip", Mockito.any(String.class), Mockito.any(Path.class));
|
||||
PowerMockito.doReturn(new ThemeProperty()).when(themeService, "add", Mockito.any(Path.class));
|
||||
ThemeProperty themeProperty = themeService.fetchLatestRelease(uri);
|
||||
Assert.assertNotNull(themeProperty);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateTest() throws Exception {
|
||||
PowerMockito.doNothing().when(themeService, "downloadZipAndUnzip", Mockito.any(String.class), Mockito.any(Path.class));
|
||||
PowerMockito.doReturn(new ThemeProperty()).when(themeService, "add", Mockito.any(Path.class));
|
||||
PowerMockito.doNothing().when(themeService, "pullFromGit", Mockito.any(ThemeProperty.class));
|
||||
PowerMockito.doReturn(new ThemeProperty()).when(themeService, "getThemeOfNonNullBy", Mockito.any(String.class));
|
||||
ThemeProperty themeProperty = themeService.update("String");
|
||||
Assert.assertNotNull(themeProperty);
|
||||
}
|
||||
}
|
|
@ -7,10 +7,7 @@ import org.eclipse.jgit.api.errors.GitAPIException;
|
|||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.eclipse.jgit.transport.RemoteConfig;
|
||||
import org.eclipse.jgit.transport.URIish;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
|
@ -80,6 +77,21 @@ public class GitTest {
|
|||
git.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getAllBranchesTest() {
|
||||
List<String> branches = GitUtils.getAllBranches("https://github.com/halo-dev/halo-theme-hux.git");
|
||||
Assert.assertNotNull(branches);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getAllBranchesWithInvalidURL() {
|
||||
List<String> branches = GitUtils.getAllBranches("https://github.com/halo-dev/halo-theme.git");
|
||||
Assert.assertNotNull(branches);
|
||||
Assert.assertEquals(branches.size(), 0);
|
||||
}
|
||||
|
||||
private Git cloneRepository() throws GitAPIException {
|
||||
return Git.cloneRepository()
|
||||
.setURI("https://github.com/halo-dev/halo-theme-pinghsu.git")
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
public class GithubUtilsTest {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getLatestReleasesWithValidURL() {
|
||||
Map<String, Object> map = GithubUtils.getLatestRelease("https://github.com/halo-dev/halo-theme-hux");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getLatestReleasesWithInvalidURL() {
|
||||
Map<String, Object> map = GithubUtils.getLatestRelease("https://github.com/halo-dev/halo-theme-hu");
|
||||
Assert.assertNull(map);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void accessThemePropertyWithValidURL() {
|
||||
String content = GithubUtils.accessThemeProperty("https://github.com/halo-dev/halo-theme-hux", "master");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void accessThemePropertyWithInvalidURL() {
|
||||
String content = GithubUtils.accessThemeProperty("https://github.com/halo-dev/halo-theme-hu", "master");
|
||||
Assert.assertNull(content);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getReleasesTest() {
|
||||
List<String> list = GithubUtils.getReleases("https://github.com/halo-dev/halo-theme-hux");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue