Complete theme updating

pull/172/head
johnniang 2019-05-30 23:06:59 +08:00
parent bf6b11eb2e
commit 0c8955dfcf
10 changed files with 190 additions and 13 deletions

View File

@ -117,6 +117,13 @@ public class ThemeController {
themeSettingService.save(settings, themeId);
}
@PutMapping("{themeId}")
public ThemeProperty updateTheme(@PathVariable("themeId") String themeId,
@RequestPart(name = "file", required = false) MultipartFile file) {
return themeService.update(themeId);
}
@DeleteMapping("{themeId}")
@ApiOperation("Deletes a theme")
public void deleteBy(@PathVariable("themeId") String themeId) {
@ -145,4 +152,5 @@ public class ThemeController {
public BaseResponse exists(@RequestParam(value = "template") String template) {
return BaseResponse.ok(themeService.templateExists(template));
}
}

View File

@ -0,0 +1,18 @@
package run.halo.app.exception;
/**
* Theme update exception.
*
* @author johnniang
* @date 19-5-30
*/
public class ThemeUpdateException extends ServiceException {
public ThemeUpdateException(String message) {
super(message);
}
public ThemeUpdateException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -1,6 +1,8 @@
package run.halo.app.handler.theme.config.impl;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
@ -18,7 +20,12 @@ import java.io.IOException;
@Service
public class YamlThemePropertyResolver implements ThemePropertyResolver {
private final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
private final ObjectMapper yamlMapper;
public YamlThemePropertyResolver() {
yamlMapper = new ObjectMapper(new YAMLFactory());
yamlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
@Override
public ThemeProperty resolve(String content) throws IOException {

View File

@ -28,6 +28,14 @@ public class ThemeProperty {
*/
private String website;
/**
* Theme remote branch.(default is master)
*/
private String branch;
/**
* Theme repo url.
*/
private String repo;
/**
@ -51,7 +59,7 @@ public class ThemeProperty {
private Author author;
/**
* Theme path.
* Theme full path.
*/
private String themePath;

View File

@ -71,6 +71,16 @@ public interface ThemeService {
*/
String CUSTOM_SHEET_PREFIX = "sheet_";
/**
* Theme provider remote name.
*/
String THEME_PROVIDER_REMOTE_NAME = "theme-provider";
/**
* Default remote branch name.
*/
String DEFAULT_REMOTE_BRANCH = "master";
/**
* Get theme property by theme id.
*
@ -241,4 +251,13 @@ public interface ThemeService {
* Reloads themes
*/
void reload();
/**
* Updates theme by theme id.
*
* @param themeId theme id must not be blank
* @return theme property
*/
@NonNull
ThemeProperty update(@NonNull String themeId);
}

View File

@ -3,7 +3,14 @@ package run.halo.app.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.MergeCommand;
import org.eclipse.jgit.api.PullResult;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.URIish;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.NonNull;
@ -32,7 +39,9 @@ import run.halo.app.utils.FilenameUtils;
import run.halo.app.utils.HaloUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@ -460,6 +469,87 @@ public class ThemeServiceImpl implements ThemeService {
eventPublisher.publishEvent(new ThemeUpdatedEvent(this));
}
@Override
public ThemeProperty update(String themeId) {
Assert.hasText(themeId, "Theme id must not be blank");
ThemeProperty updatingTheme = getThemeOfNonNullBy(themeId);
try {
pullFromGit(updatingTheme);
} catch (Exception e) {
throw new ThemeUpdateException("主题更新失败!您与主题作者可能同时更改了同一个文件,您也可以尝试删除主题并重新拉取最新的主题", e).setErrorData(themeId);
}
eventPublisher.publishEvent(new ThemeUpdatedEvent(this));
return getThemeOfNonNullBy(themeId);
}
private void pullFromGit(@NonNull ThemeProperty themeProperty) throws IOException, GitAPIException, URISyntaxException {
Assert.notNull(themeProperty, "Theme property must not be null");
// Get branch
String branch = StringUtils.isBlank(themeProperty.getBranch()) ?
DEFAULT_REMOTE_BRANCH : themeProperty.getBranch();
File themeFolder = Paths.get(themeProperty.getThemePath()).toFile();
// Open the theme path
Git git;
try {
git = Git.open(themeFolder);
} catch (RepositoryNotFoundException e) {
// Repository is not initialized
git = Git.init().setDirectory(themeFolder).call();
}
// 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();
// 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));
git.checkout()
.setCreateBranch(true)
.setForced(!present)
.setName(branch)
.call();
}
// Pull with rebasing
PullResult pullResult = git.pull()
.setRemote(remoteConfig.getName())
.setRemoteBranchName(branch)
.setRebase(true)
.call();
if (!pullResult.isSuccessful()) {
log.debug("Rebase result: [{}]", pullResult.getRebaseResult());
log.debug("Merge result: [{}]", pullResult.getMergeResult());
throw new ThemeUpdateException("拉取失败!您与主题作者可能同时更改了同一个文件");
}
// Close git
git.close();
}
/**
* Clones theme from git.
*

View File

@ -64,7 +64,7 @@ public class FileUtils {
*
* @param deletingPath deleting path must not be null
*/
public static void deleteFolder(Path deletingPath) throws IOException {
public static void deleteFolder(@NonNull Path deletingPath) throws IOException {
Assert.notNull(deletingPath, "Deleting path must not be null");
log.debug("Deleting [{}]", deletingPath);
@ -249,9 +249,9 @@ public class FileUtils {
/**
* Deletes folder quietly.
*
* @param deletingPath deleting path must not be null
* @param deletingPath deleting path
*/
public static void deleteFolderQuietly(@NonNull Path deletingPath) {
public static void deleteFolderQuietly(@Nullable Path deletingPath) {
try {
if (deletingPath != null) {
FileUtils.deleteFolder(deletingPath);

View File

@ -44,8 +44,8 @@ spring:
open-in-view: false
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
max-file-size: 10240MB
max-request-size: 10240MB
cache:
type: none

View File

@ -5,5 +5,6 @@ author:
website: https://www.caicai.me
description: A other Halo theme
logo: https://avatars1.githubusercontent.com/u/1811819?s=460&v=4
website: https://github.com/halo-dev/halo-theme-anatole
repo: https://github.com/halo-dev/halo-theme-anatole
version: 1.0

View File

@ -1,15 +1,23 @@
package run.halo.app.utils;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.Status;
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.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Set;
/**
* Git test.
@ -17,17 +25,23 @@ import java.nio.file.Path;
* @author johnniang
* @date 19-5-21
*/
@Ignore
@Slf4j
public class GitTest {
private final Path tempPath;
private static Path tempPath;
public GitTest() throws IOException {
@BeforeClass
public static void setUp() throws IOException {
tempPath = Files.createTempDirectory("git-test");
}
@AfterClass
public static void destroy() throws IOException {
FileUtils.deleteFolder(tempPath);
}
@Test(expected = RepositoryNotFoundException.class)
public void openTest() throws IOException {
public void openFailureTest() throws IOException {
Git.open(tempPath.toFile());
}
@ -37,17 +51,29 @@ public class GitTest {
}
@Test
public void statusTest() throws GitAPIException {
public void statusSuccessfulTest() throws GitAPIException {
Git git = Git.init().setDirectory(tempPath.toFile()).call();
git.status().call();
Status status = git.status().call();
log.debug("Status missing: [{}]", status.getMissing());
}
@Test
public void remoteAddTest() throws GitAPIException, URISyntaxException {
Git git = Git.init().setDirectory(tempPath.toFile()).call();
git.remoteRemove().setRemoteName("theme-provider").call();
git.remoteAdd().setName("theme-provider").setUri(new URIish("https://github.com/halo-dev/halo-theme-pinghsu.git")).call();
List<RemoteConfig> remoteConfigs = git.remoteList().call();
remoteConfigs.forEach(remoteConfig -> log.debug("name: [{}], url: [{}]", remoteConfig.getName(), remoteConfig.getURIs()));
}
@Test
@Ignore
public void cloneTest() throws GitAPIException {
cloneRepository();
}
@Test
@Ignore
public void pullTest() throws GitAPIException {
Git git = cloneRepository();
git.pull().call();