mirror of https://github.com/halo-dev/halo
Complete theme updating
parent
bf6b11eb2e
commit
0c8955dfcf
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue