feat: ASW-88 本地 GIT 仓库管理 application 文件
1. 使用 JGIT 完成对 application 文件的操作
This commit is contained in:
@@ -57,6 +57,10 @@ public enum RCode {
|
||||
PACKAGE_DESCRIPTION_CANNOT_EMPTY(202002, "package description cannot be empty"),
|
||||
|
||||
|
||||
// GIT
|
||||
GIT_COMMIT_CONFLICT_ERROR(203001, "Commit failed; fix conflicts and then commit the result"),
|
||||
|
||||
|
||||
// Runner
|
||||
RUNNER_ID_CANNOT_EMPTY(301001, "runner id cannot be empty"),
|
||||
|
||||
|
||||
@@ -49,4 +49,88 @@ public class GitController {
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/{workspaceId}/branch/{branchName}/application")
|
||||
public R listApplication(@PathVariable("workspaceId") String workspaceId,
|
||||
@PathVariable("branchName") String branchName,
|
||||
@RequestParam(value = "q", required = false) String q) {
|
||||
List<Map<Object, Object>> records = gitService.listApplication(workspaceId, branchName, q);
|
||||
return R.ok().putData("records", records);
|
||||
}
|
||||
|
||||
@GetMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}")
|
||||
public R infoApplication(@PathVariable("workspaceId") String workspaceId,
|
||||
@PathVariable("branchName") String branchName,
|
||||
@PathVariable("applicationName") String applicationName) {
|
||||
Map<Object, Object> record = gitService.infoApplication(workspaceId, branchName, applicationName);
|
||||
return R.ok().putData("record", record);
|
||||
}
|
||||
|
||||
@PostMapping("/{workspaceId}/branch/{branchName}/application")
|
||||
public synchronized R newApplication(@PathVariable("workspaceId") String workspaceId,
|
||||
@PathVariable("branchName") String branchName,
|
||||
@RequestBody Map<String, String> body) {
|
||||
String applicationName = T.MapUtil.getStr(body, "name");
|
||||
T.VerifyUtil.is(applicationName).notEmpty(RCode.PARAM_CANNOT_EMPTY);
|
||||
|
||||
gitService.newApplication(workspaceId, branchName, applicationName);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/{workspaceId}/branch/{branchName}/application/commit")
|
||||
public synchronized R updateApplication(@PathVariable("workspaceId") String workspaceId,
|
||||
@PathVariable("branchName") String branchName,
|
||||
@RequestBody Map<String, Object> body) {
|
||||
String lastCommitId = T.MapUtil.getStr(body, "lastCommitId");
|
||||
String message = T.MapUtil.getStr(body, "message");
|
||||
|
||||
T.VerifyUtil.is(lastCommitId).notEmpty(RCode.PARAM_CANNOT_EMPTY)
|
||||
.and(message).notEmpty(RCode.PARAM_CANNOT_EMPTY);
|
||||
|
||||
List<Map<String, String>> files = T.MapUtil.get(body, "files", List.class, T.ListUtil.list(true));
|
||||
if (T.CollUtil.isEmpty(files)) {
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
for (Map<String, String> file : files) {
|
||||
String action = T.MapUtil.getStr(file, "action");
|
||||
String path = T.MapUtil.getStr(file, "path");
|
||||
String content = T.MapUtil.getStr(file, "content");
|
||||
if (T.StrUtil.hasEmpty(action, path, content)) {
|
||||
return R.error(RCode.PARAM_CANNOT_EMPTY);
|
||||
}
|
||||
}
|
||||
gitService.updateApplication(workspaceId, branchName, lastCommitId, message, files);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}")
|
||||
public synchronized R deleteApplication(@PathVariable("workspaceId") String workspaceId,
|
||||
@PathVariable("branchName") String branchName,
|
||||
@PathVariable("applicationName") String applicationName) {
|
||||
gitService.deleteApplication(workspaceId, branchName, applicationName);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}/commit")
|
||||
public R listApplicationCommit(@PathVariable("workspaceId") String workspaceId,
|
||||
@PathVariable("branchName") String branchName,
|
||||
@PathVariable("applicationName") String applicationName,
|
||||
@RequestParam(required = false) String file) {
|
||||
List<Map<Object, Object>> records = gitService.listApplicationCommit(workspaceId, branchName, applicationName, file);
|
||||
return R.ok().putData("records", records);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}/commit/{commitId}/content")
|
||||
public R infoApplicationFileContent(@PathVariable("workspaceId") String workspaceId,
|
||||
@PathVariable("branchName") String branchName,
|
||||
@PathVariable("applicationName") String applicationName,
|
||||
@PathVariable("commitId") String commitId,
|
||||
@RequestParam(required = true) String file) {
|
||||
Map<Object, Object> record = gitService.infoApplicationFileContent(workspaceId, branchName, applicationName, commitId, file);
|
||||
return R.ok().putData("record", record);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,4 +13,18 @@ public interface IGitService {
|
||||
|
||||
void deleteBranch(String workspaceId, String branchName);
|
||||
|
||||
List<Map<Object, Object>> listApplication(String workspaceId, String branch, String q);
|
||||
|
||||
Map<Object, Object> infoApplication(String workspaceId, String branch, String applicationName);
|
||||
|
||||
void newApplication(String workspaceId, String branch, String applicationName);
|
||||
|
||||
void deleteApplication(String workspaceId, String branch, String applicationName);
|
||||
|
||||
void updateApplication(String workspaceId, String branch, String lastCommitId, String message, List<Map<String, String>> updateContent);
|
||||
|
||||
List<Map<Object, Object>> listApplicationCommit(String workspaceId, String branch, String applicationName, String file);
|
||||
|
||||
Map<Object, Object> infoApplicationFileContent(String workspaceId, String branch, String applicationName, String commitId, String file);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,30 +1,37 @@
|
||||
package net.geedge.asw.module.app.service.impl;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.log.Log;
|
||||
import net.geedge.asw.common.util.ASWException;
|
||||
import net.geedge.asw.common.util.RCode;
|
||||
import net.geedge.asw.common.util.T;
|
||||
import net.geedge.asw.module.app.service.IGitService;
|
||||
import net.geedge.asw.module.sys.entity.SysUserEntity;
|
||||
import net.geedge.asw.module.sys.service.ISysUserService;
|
||||
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
|
||||
import net.geedge.asw.module.workspace.service.IWorkspaceService;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.StoredConfig;
|
||||
import org.eclipse.jgit.diff.DiffEntry;
|
||||
import org.eclipse.jgit.dircache.DirCache;
|
||||
import org.eclipse.jgit.dircache.DirCacheBuilder;
|
||||
import org.eclipse.jgit.dircache.DirCacheEntry;
|
||||
import org.eclipse.jgit.lib.*;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
|
||||
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.eclipse.jgit.treewalk.filter.PathFilter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@@ -32,14 +39,32 @@ public class GitServiceImpl implements IGitService {
|
||||
|
||||
private final static Log log = Log.get();
|
||||
|
||||
@Value("${file.extensions.text:txt,csv,md,html,xml,json,log,bat,py,sh,ini,conf,yaml,yml,properties,toml,java,c,cpp,js,php,ts,go,rb,rtf,tex,rss,xhtml,sql}")
|
||||
private String textExtensions;
|
||||
|
||||
/**
|
||||
* 本地分支引用前缀
|
||||
*/
|
||||
public static final String LOCAL_BRANCH_PREFIX = "refs/heads/";
|
||||
|
||||
@Autowired
|
||||
private ISysUserService userService;
|
||||
|
||||
@Autowired
|
||||
private IWorkspaceService workspaceService;
|
||||
|
||||
/**
|
||||
* is binary file
|
||||
*
|
||||
* @param filename
|
||||
* @return
|
||||
*/
|
||||
private boolean isBinary(String filename) {
|
||||
String extension = FilenameUtils.getExtension(filename);
|
||||
List<String> split = T.StrUtil.split(this.textExtensions, ",");
|
||||
return !split.contains(extension.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* get repository path
|
||||
* path= {webRootPath}/workspeace/{workspace.name}
|
||||
@@ -53,29 +78,34 @@ public class GitServiceImpl implements IGitService {
|
||||
/**
|
||||
* get git instance
|
||||
*/
|
||||
private Git getGitInstance(File repoDir) {
|
||||
public Git getGitInstance(File repoDir) {
|
||||
try {
|
||||
if (T.FileUtil.exist(repoDir) && T.FileUtil.file(repoDir, ".git").exists()) {
|
||||
log.info("[getGitInstance] [open exist repository] [path: {}]", repoDir);
|
||||
// 目录不存在,初始化裸仓库
|
||||
if (!repoDir.exists()) {
|
||||
log.info("[getGitInstance] [dir not exist] [init new repository] [path: {}]", repoDir);
|
||||
return Git.init()
|
||||
.setBare(true)
|
||||
.setDirectory(repoDir)
|
||||
.call();
|
||||
}
|
||||
|
||||
FileRepositoryBuilder builder = new FileRepositoryBuilder();
|
||||
builder.setGitDir(T.FileUtil.file(repoDir, ".git"));
|
||||
builder.readEnvironment();
|
||||
builder.findGitDir();
|
||||
FileRepositoryBuilder builder = new FileRepositoryBuilder();
|
||||
Repository repository = builder.setGitDir(repoDir)
|
||||
.readEnvironment()
|
||||
.findGitDir()
|
||||
.build();
|
||||
|
||||
return new Git(builder.build());
|
||||
// 是否为 Git 仓库
|
||||
if (repository.getObjectDatabase().exists()) {
|
||||
return new Git(repository);
|
||||
} else {
|
||||
log.info("[getGitInstance] [init new repository] [path: {}]", repoDir);
|
||||
// init
|
||||
Git git = Git.init().setDirectory(repoDir).call();
|
||||
// config
|
||||
StoredConfig config = git.getRepository().getConfig();
|
||||
config.setString("user", null, "name", "asw");
|
||||
config.setString("user", null, "email", "asw@geedgenetworks.com");
|
||||
config.save();
|
||||
return git;
|
||||
return Git.init()
|
||||
.setBare(true)
|
||||
.setDirectory(repoDir)
|
||||
.call();
|
||||
}
|
||||
} catch (IOException | GitAPIException | IllegalStateException e) {
|
||||
} catch (GitAPIException | IOException e) {
|
||||
log.error(e, "[getGitInstance] [error] [path: {}]", repoDir);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -108,28 +138,8 @@ public class GitServiceImpl implements IGitService {
|
||||
.build();
|
||||
|
||||
RevCommit commit = revCommits.parseCommit(ref.getObjectId());
|
||||
List<String> parentIds = Arrays.stream(commit.getParents()).map(RevCommit::getName).collect(Collectors.toList());
|
||||
m.put("commit", this.buildAswCommitInfo(commit));
|
||||
|
||||
Map<Object, Object> m1 = new LinkedHashMap<>();
|
||||
m1.put("id", commit.getName());
|
||||
m1.put("shortId", T.StrUtil.subPre(commit.getName(), 8));
|
||||
m1.put("createdAt", commit.getCommitTime());
|
||||
|
||||
m1.put("title", commit.getShortMessage());
|
||||
m1.put("message", commit.getFullMessage());
|
||||
m1.put("parentIds", parentIds);
|
||||
|
||||
PersonIdent authorIdent = commit.getAuthorIdent();
|
||||
m1.put("authorName", authorIdent.getName());
|
||||
m1.put("authorEmail", authorIdent.getEmailAddress());
|
||||
m1.put("authoredDate", authorIdent.getWhen().getTime());
|
||||
|
||||
PersonIdent committerIdent = commit.getCommitterIdent();
|
||||
m1.put("committerName", committerIdent.getName());
|
||||
m1.put("committerEmail", committerIdent.getEmailAddress());
|
||||
m1.put("committedDate", committerIdent.getWhen().getTime());
|
||||
|
||||
m.put("commit", m1);
|
||||
resultList.add(m);
|
||||
}
|
||||
} catch (GitAPIException | IOException e) {
|
||||
@@ -179,4 +189,468 @@ public class GitServiceImpl implements IGitService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<Object, Object>> listApplication(String workspaceId, String branch, String q) {
|
||||
List<Map<Object, Object>> resultList = T.ListUtil.list(true);
|
||||
|
||||
File repoDir = this.getRepoDirPath(workspaceId);
|
||||
try (Git git = this.getGitInstance(repoDir)) {
|
||||
Repository repository = git.getRepository();
|
||||
|
||||
try (TreeWalk treeWalk = new TreeWalk(repository);
|
||||
RevWalk revWalk = new RevWalk(repository)) {
|
||||
|
||||
ObjectId branchRef = repository.resolve(branch);
|
||||
treeWalk.addTree(revWalk.parseTree(branchRef));
|
||||
treeWalk.setFilter(PathFilter.create("applications/"));
|
||||
treeWalk.setRecursive(true);
|
||||
|
||||
while (treeWalk.next()) {
|
||||
String fileName = treeWalk.getNameString();
|
||||
if (T.StrUtil.equals("basic.json", fileName)) {
|
||||
ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
|
||||
String basicJsonStr = T.StrUtil.utf8Str(loader.getBytes());
|
||||
basicJsonStr = T.StrUtil.emptyToDefault(basicJsonStr, T.StrUtil.EMPTY_JSON);
|
||||
Map basicJsonMap = T.JSONUtil.toBean(basicJsonStr, Map.class);
|
||||
|
||||
Map<Object, Object> m = T.MapUtil.newHashMap(true);
|
||||
m.putAll(basicJsonMap);
|
||||
|
||||
Iterable<RevCommit> iterable = git.log()
|
||||
.add(branchRef)
|
||||
.addPath(treeWalk.getPathString().replaceAll(fileName, ""))
|
||||
.call();
|
||||
Iterator<RevCommit> iterator = iterable.iterator();
|
||||
RevCommit commit = iterator.hasNext() ? iterator.next() : null;
|
||||
m.put("commit", this.buildAswCommitInfo(commit));
|
||||
|
||||
resultList.add(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException | GitAPIException e) {
|
||||
log.error(e, "[listApplication] [error] [workspaceId: {}] [branch: {}]", workspaceId, branch);
|
||||
throw new ASWException(RCode.ERROR);
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Object, Object> infoApplication(String workspaceId, String branch, String applicationName) {
|
||||
Map<Object, Object> result = T.MapUtil.builder()
|
||||
.put("branch", branch)
|
||||
.build();
|
||||
|
||||
File repoDir = this.getRepoDirPath(workspaceId);
|
||||
try (Git git = this.getGitInstance(repoDir)) {
|
||||
Repository repository = git.getRepository();
|
||||
|
||||
// 获取指定版本的最新ID
|
||||
ObjectId branchRef = repository.resolve(branch);
|
||||
result.put("commitId", branchRef.getName());
|
||||
|
||||
List<Object> files = T.ListUtil.list(true);
|
||||
try (TreeWalk treeWalk = new TreeWalk(repository);
|
||||
RevWalk revWalk = new RevWalk(repository)) {
|
||||
|
||||
treeWalk.addTree(revWalk.parseTree(branchRef));
|
||||
treeWalk.setFilter(PathFilter.create("applications/" + applicationName + "/"));
|
||||
treeWalk.setRecursive(true);
|
||||
|
||||
while (treeWalk.next()) {
|
||||
Map<Object, Object> m = T.MapUtil.builder()
|
||||
.put("path", treeWalk.getPathString())
|
||||
.build();
|
||||
|
||||
ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
|
||||
if (this.isBinary(treeWalk.getNameString())) {
|
||||
String content = Base64.getEncoder().encodeToString(loader.getBytes());
|
||||
m.put("encoding", "base64");
|
||||
m.put("content", content);
|
||||
} else {
|
||||
m.put("encoding", "");
|
||||
m.put("content", T.StrUtil.utf8Str(loader.getBytes()));
|
||||
}
|
||||
|
||||
// lastCommitId
|
||||
Iterable<RevCommit> iterable = git.log()
|
||||
.add(branchRef)
|
||||
.addPath(treeWalk.getPathString())
|
||||
.call();
|
||||
Iterator<RevCommit> iterator = iterable.iterator();
|
||||
m.put("lastCommitId", iterator.hasNext() ? iterator.next().getName() : null);
|
||||
files.add(m);
|
||||
}
|
||||
}
|
||||
result.put("files", files);
|
||||
} catch (IOException | GitAPIException e) {
|
||||
log.error(e, "[infoApplication] [error] [workspaceId: {}] [branch: {}] [application: {}]", workspaceId, branch, applicationName);
|
||||
throw new ASWException(RCode.ERROR);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newApplication(String workspaceId, String branch, String applicationName) {
|
||||
File repoDir = this.getRepoDirPath(workspaceId);
|
||||
try (Git git = this.getGitInstance(repoDir)) {
|
||||
Repository repository = git.getRepository();
|
||||
|
||||
Map<String, ObjectId> filePathAndBlobIdMap = T.MapUtil.newHashMap(true);
|
||||
for (String str : T.ListUtil.of("README.md", "basic.json", "signature.json", "icon.png")) {
|
||||
String savePath = T.StrUtil.concat(true, "applications/", applicationName, "/", str);
|
||||
|
||||
ObjectId objectId = this.insertBlobFileToDatabase(repository, T.StrUtil.EMPTY.getBytes());
|
||||
filePathAndBlobIdMap.put(savePath, objectId);
|
||||
}
|
||||
|
||||
try (ObjectInserter inserter = repository.getObjectDatabase().newInserter();
|
||||
TreeWalk treeWalk = new TreeWalk(repository);
|
||||
RevWalk revWalk = new RevWalk(repository)) {
|
||||
|
||||
// branch tree
|
||||
ObjectId resolveId = repository.resolve(branch);
|
||||
if (null != resolveId) {
|
||||
treeWalk.addTree(revWalk.parseTree(resolveId));
|
||||
} else {
|
||||
treeWalk.addTree(new CanonicalTreeParser());
|
||||
}
|
||||
treeWalk.setRecursive(true);
|
||||
|
||||
DirCache newTree = DirCache.newInCore();
|
||||
DirCacheBuilder newTreeBuilder = newTree.builder();
|
||||
while (treeWalk.next()) {
|
||||
DirCacheEntry entry = new DirCacheEntry(treeWalk.getPathString());
|
||||
entry.setFileMode(treeWalk.getFileMode(0));
|
||||
entry.setObjectId(treeWalk.getObjectId(0));
|
||||
newTreeBuilder.add(entry);
|
||||
}
|
||||
|
||||
// update new tree
|
||||
for (Map.Entry<String, ObjectId> entry : filePathAndBlobIdMap.entrySet()) {
|
||||
String filePath = entry.getKey();
|
||||
ObjectId blobId = entry.getValue();
|
||||
|
||||
// add file ref
|
||||
DirCacheEntry dirCacheEntry = new DirCacheEntry(filePath);
|
||||
dirCacheEntry.setFileMode(FileMode.REGULAR_FILE);
|
||||
dirCacheEntry.setObjectId(blobId);
|
||||
newTreeBuilder.add(dirCacheEntry);
|
||||
}
|
||||
|
||||
newTreeBuilder.finish();
|
||||
ObjectId newTreeId = newTree.writeTree(inserter);
|
||||
|
||||
String message = String.format("feat: add %s application", applicationName);
|
||||
this.createCommit(repository, branch, newTreeId, message);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error(e, "[newApplication] [error] [workspaceId: {}] [branch: {}] [application: {}]", workspaceId, branch, applicationName);
|
||||
throw new ASWException(RCode.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteApplication(String workspaceId, String branch, String applicationName) {
|
||||
File repoDir = this.getRepoDirPath(workspaceId);
|
||||
try (Git git = this.getGitInstance(repoDir)) {
|
||||
Repository repository = git.getRepository();
|
||||
|
||||
try (ObjectInserter inserter = repository.getObjectDatabase().newInserter();
|
||||
TreeWalk treeWalk = new TreeWalk(repository);
|
||||
RevWalk revWalk = new RevWalk(repository)) {
|
||||
|
||||
ObjectId branchRef = repository.resolve(branch);
|
||||
treeWalk.addTree(revWalk.parseTree(branchRef));
|
||||
treeWalk.setRecursive(true);
|
||||
|
||||
DirCache newTree = DirCache.newInCore();
|
||||
DirCacheBuilder newTreeBuilder = newTree.builder();
|
||||
|
||||
String appFilePrefixStr = T.StrUtil.concat(true, "applications/", applicationName, "/");
|
||||
while (treeWalk.next()) {
|
||||
String pathString = treeWalk.getPathString();
|
||||
if (!pathString.startsWith(appFilePrefixStr)) {
|
||||
DirCacheEntry entry = new DirCacheEntry(pathString);
|
||||
entry.setFileMode(treeWalk.getFileMode(0));
|
||||
entry.setObjectId(treeWalk.getObjectId(0));
|
||||
newTreeBuilder.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
newTreeBuilder.finish();
|
||||
ObjectId newTreeId = newTree.writeTree(inserter);
|
||||
|
||||
String message = String.format("refactor: remove %s application", applicationName);
|
||||
this.createCommit(repository, branch, newTreeId, message);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error(e, "[deleteApplication] [error] [workspaceId: {}] [branch: {}] [application: {}]", workspaceId, branch, applicationName);
|
||||
throw new ASWException(RCode.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateApplication(String workspaceId, String branch, String lastCommitId, String message, List<Map<String, String>> updateContent) {
|
||||
File repoDir = this.getRepoDirPath(workspaceId);
|
||||
try (Git git = this.getGitInstance(repoDir)) {
|
||||
Repository repository = git.getRepository();
|
||||
|
||||
// 分支最新 commitId
|
||||
ObjectId branchRef = repository.resolve(branch);
|
||||
// 如果不相等,检查 param.lastCommitId --- branch.HEAD 期间是否更新了本次修改文件
|
||||
if (!T.StrUtil.equals(lastCommitId, branchRef.getName())) {
|
||||
List<String> updateFilePath = updateContent.stream()
|
||||
.filter(map -> {
|
||||
String action = T.MapUtil.getStr(map, "action");
|
||||
return T.StrUtil.equalsAny(action, "create", "update");
|
||||
})
|
||||
.map(map -> T.MapUtil.getStr(map, "path"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
ObjectId currentCommitId = repository.resolve(lastCommitId);
|
||||
try (RevWalk walk = new RevWalk(repository)) {
|
||||
CanonicalTreeParser c1 = new CanonicalTreeParser();
|
||||
c1.reset(repository.newObjectReader(), walk.parseCommit(currentCommitId).getTree());
|
||||
|
||||
CanonicalTreeParser c2 = new CanonicalTreeParser();
|
||||
c2.reset(repository.newObjectReader(), walk.parseCommit(branchRef).getTree());
|
||||
|
||||
List<DiffEntry> diffs = git.diff()
|
||||
.setOldTree(c1)
|
||||
.setNewTree(c2)
|
||||
.call();
|
||||
List<String> affectFilePathList = diffs.stream()
|
||||
.filter(entry -> T.StrUtil.equalsAnyIgnoreCase(entry.getChangeType().name(), DiffEntry.ChangeType.MODIFY.name(), DiffEntry.ChangeType.ADD.name()))
|
||||
.map(DiffEntry::getNewPath)
|
||||
.collect(Collectors.toList());
|
||||
List<String> intersection = T.CollUtil.intersection(updateFilePath, affectFilePathList).stream().toList();
|
||||
if (T.CollUtil.isEmpty(intersection)) {
|
||||
// 在 param.lastCommitId 不是当前分支最新提交时,本次提交文件没有冲突,更新 commit=branch.HEAD
|
||||
lastCommitId = branchRef.getName();
|
||||
} else {
|
||||
throw new ASWException(RCode.GIT_COMMIT_CONFLICT_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
try (ObjectInserter inserter = repository.getObjectDatabase().newInserter();
|
||||
TreeWalk treeWalk = new TreeWalk(repository);
|
||||
RevWalk revWalk = new RevWalk(repository)) {
|
||||
|
||||
// branch tree
|
||||
treeWalk.addTree(revWalk.parseTree(branchRef));
|
||||
treeWalk.setRecursive(true);
|
||||
|
||||
DirCache newTree = DirCache.newInCore();
|
||||
DirCacheBuilder newTreeBuilder = newTree.builder();
|
||||
|
||||
List<String> updateFilePath = updateContent.stream().map(map -> T.MapUtil.getStr(map, "path")).collect(Collectors.toList());
|
||||
while (treeWalk.next()) {
|
||||
String pathString = treeWalk.getPathString();
|
||||
// 先删
|
||||
if (!updateFilePath.contains(pathString)) {
|
||||
DirCacheEntry entry = new DirCacheEntry(pathString);
|
||||
entry.setFileMode(treeWalk.getFileMode(0));
|
||||
entry.setObjectId(treeWalk.getObjectId(0));
|
||||
newTreeBuilder.add(entry);
|
||||
}
|
||||
}
|
||||
// 后增
|
||||
for (Map<String, String> map : updateContent) {
|
||||
String action = T.MapUtil.getStr(map, "action");
|
||||
if (T.StrUtil.equalsAnyIgnoreCase(action, "create", "update")) {
|
||||
String path = T.MapUtil.getStr(map, "path");
|
||||
DirCacheEntry dirCacheEntry = new DirCacheEntry(path);
|
||||
dirCacheEntry.setFileMode(FileMode.REGULAR_FILE);
|
||||
|
||||
String content = T.MapUtil.getStr(map, "content");
|
||||
String encoding = T.MapUtil.getStr(map, "encoding");
|
||||
if ("base64".equals(encoding)) {
|
||||
// binary
|
||||
byte[] data = Base64.getDecoder().decode(content);
|
||||
ObjectId objectId = this.insertBlobFileToDatabase(repository, data);
|
||||
dirCacheEntry.setObjectId(objectId);
|
||||
} else {
|
||||
// text
|
||||
ObjectId objectId = this.insertBlobFileToDatabase(repository, content.getBytes());
|
||||
dirCacheEntry.setObjectId(objectId);
|
||||
}
|
||||
newTreeBuilder.add(dirCacheEntry);
|
||||
}
|
||||
}
|
||||
|
||||
newTreeBuilder.finish();
|
||||
ObjectId newTreeId = newTree.writeTree(inserter);
|
||||
this.createCommit(repository, branch, newTreeId, message);
|
||||
}
|
||||
} catch (IOException | GitAPIException e) {
|
||||
log.error(e, "[updateApplication] [error] [workspaceId: {}] [branch: {}] [lastCommitId: {}]", workspaceId, branch, lastCommitId);
|
||||
throw new ASWException(RCode.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<Object, Object>> listApplicationCommit(String workspaceId, String branch, String applicationName, String file) {
|
||||
List<Map<Object, Object>> resultList = T.ListUtil.list(true);
|
||||
|
||||
File repoDir = this.getRepoDirPath(workspaceId);
|
||||
try (Git git = this.getGitInstance(repoDir)) {
|
||||
String filterPath = T.StrUtil.concat(true, "applications/", applicationName);
|
||||
if (T.StrUtil.isNotEmpty(file)) {
|
||||
filterPath = T.StrUtil.concat(true, filterPath, "/", file);
|
||||
}
|
||||
|
||||
ObjectId branchRef = git.getRepository().resolve(branch);
|
||||
Iterable<RevCommit> iterable = git.log()
|
||||
.add(branchRef)
|
||||
.addPath(filterPath)
|
||||
.call();
|
||||
|
||||
for (RevCommit commit : iterable) {
|
||||
resultList.add(this.buildAswCommitInfo(commit));
|
||||
}
|
||||
} catch (IOException | GitAPIException e) {
|
||||
log.error(e, "[listApplicationCommit] [error] [workspaceId: {}] [branch: {}] [application: {}]", workspaceId, branch, applicationName);
|
||||
throw new ASWException(RCode.ERROR);
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Object, Object> infoApplicationFileContent(String workspaceId, String branch, String applicationName, String commitId1111, String file) {
|
||||
// applications/qq/basic.json
|
||||
String path = T.StrUtil.concat(true, "applications/", applicationName, "/", file);
|
||||
|
||||
Map<Object, Object> result = T.MapUtil.builder()
|
||||
.put("path", path)
|
||||
.build();
|
||||
|
||||
File repoDir = this.getRepoDirPath(workspaceId);
|
||||
try (Git git = getGitInstance(repoDir)) {
|
||||
Repository repository = git.getRepository();
|
||||
|
||||
try (TreeWalk treeWalk = new TreeWalk(repository);
|
||||
RevWalk revWalk = new RevWalk(repository);
|
||||
) {
|
||||
RevCommit revCommit = revWalk.parseCommit(ObjectId.fromString(commitId1111));
|
||||
ObjectId treeId = revCommit.getTree().getId();
|
||||
|
||||
treeWalk.addTree(treeId);
|
||||
treeWalk.setRecursive(true);
|
||||
|
||||
while (treeWalk.next()) {
|
||||
if (T.StrUtil.equals(path, treeWalk.getPathString())) {
|
||||
ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
|
||||
if (this.isBinary(treeWalk.getNameString())) {
|
||||
String content = Base64.getEncoder().encodeToString(loader.getBytes());
|
||||
result.put("encoding", "base64");
|
||||
result.put("content", content);
|
||||
} else {
|
||||
result.put("encoding", "");
|
||||
result.put("content", T.StrUtil.utf8Str(loader.getBytes()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error(e, "[infoApplicationFileContent] [error] [workspaceId: {}] [branch: {}] [application: {}]", workspaceId, branch, applicationName);
|
||||
throw new ASWException(RCode.ERROR);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* build asw commit info
|
||||
*
|
||||
* @param commit
|
||||
* @return
|
||||
*/
|
||||
private Map<Object, Object> buildAswCommitInfo(RevCommit commit) {
|
||||
if (null == commit) {
|
||||
return T.MapUtil.newHashMap();
|
||||
}
|
||||
|
||||
Map<Object, Object> m = new LinkedHashMap<>();
|
||||
m.put("id", commit.getName());
|
||||
m.put("shortId", T.StrUtil.subPre(commit.getName(), 8));
|
||||
m.put("createdAt", TimeUnit.SECONDS.toMillis(commit.getCommitTime()));
|
||||
|
||||
m.put("title", commit.getShortMessage());
|
||||
m.put("message", commit.getFullMessage());
|
||||
|
||||
List<String> parentIds = Arrays.stream(commit.getParents()).map(RevCommit::getName).collect(Collectors.toList());
|
||||
m.put("parentIds", parentIds);
|
||||
|
||||
PersonIdent authorIdent = commit.getAuthorIdent();
|
||||
m.put("authorName", authorIdent.getName());
|
||||
m.put("authorEmail", authorIdent.getEmailAddress());
|
||||
m.put("authoredDate", authorIdent.getWhen().getTime());
|
||||
|
||||
PersonIdent committerIdent = commit.getCommitterIdent();
|
||||
m.put("committerName", committerIdent.getName());
|
||||
m.put("committerEmail", committerIdent.getEmailAddress());
|
||||
m.put("committedDate", committerIdent.getWhen().getTime());
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 file object 添加到 objectDatabases,也就是 ./objects 目录下
|
||||
*
|
||||
* @param repository
|
||||
* @param data
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private ObjectId insertBlobFileToDatabase(Repository repository, byte[] data) throws IOException {
|
||||
try (ObjectInserter inserter = repository.getObjectDatabase().newInserter()) {
|
||||
ObjectId blobId = inserter.insert(Constants.OBJ_BLOB, data);
|
||||
inserter.flush();
|
||||
return blobId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* commit
|
||||
*
|
||||
* @param repo
|
||||
* @param branch
|
||||
* @param treeId
|
||||
* @param message
|
||||
* @throws IOException
|
||||
*/
|
||||
public void createCommit(Repository repo, String branch, ObjectId treeId, String message) throws IOException {
|
||||
try (ObjectInserter inserter = repo.getObjectDatabase().newInserter();
|
||||
RevWalk revWalk = new RevWalk(repo);) {
|
||||
|
||||
CommitBuilder builder = new CommitBuilder();
|
||||
builder.setTreeId(treeId);
|
||||
builder.setMessage(message);
|
||||
|
||||
ObjectId branchRef = repo.resolve(branch);
|
||||
if (null != branchRef) {
|
||||
RevCommit parentId = revWalk.parseCommit(branchRef);
|
||||
builder.setParentId(parentId);
|
||||
}
|
||||
|
||||
SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
|
||||
builder.setAuthor(new PersonIdent(loginUserEntity.getName(), "asw@geedgenetworks.com"));
|
||||
builder.setCommitter(new PersonIdent(loginUserEntity.getName(), "asw@geedgenetworks.com"));
|
||||
|
||||
// 插入新的提交对象
|
||||
ObjectId commitId = inserter.insert(builder);
|
||||
inserter.flush();
|
||||
|
||||
// 更新 branch 指向新的提交
|
||||
if (null != branchRef) {
|
||||
RefUpdate refUpdate = repo.updateRef(branch);
|
||||
refUpdate.setNewObjectId(commitId);
|
||||
refUpdate.update();
|
||||
} else {
|
||||
RefUpdate refUpdate = repo.updateRef(Constants.HEAD);
|
||||
refUpdate.setNewObjectId(commitId);
|
||||
refUpdate.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -139,5 +139,9 @@ INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_
|
||||
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (231, '302002', 'PLAYBOOK_NAME_DUPLICATE', '剧本名称重复', 'zh', '', 'admin', 1724030366000);
|
||||
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (232, '601005', 'ENVIRONMENT_ID_CANNOT_EMPTY', 'environment id cannot be empty', 'en', '', 'admin', 1724030366000);
|
||||
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (233, '601005', 'ENVIRONMENT_ID_CANNOT_EMPTY', '环境 id 不能为空', 'zh', '', 'admin', 1724030366000);
|
||||
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (234, '202003', 'PACKAGE_FILE_TYPE_ERROR', 'package invalid file', 'en', '', 'admin', 1724030366000);
|
||||
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (235, '202003', 'PACKAGE_FILE_TYPE_ERROR', '无效安装包文件', 'zh', '', 'admin', 1724030366000);
|
||||
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (236, '203001', 'GIT_COMMIT_CONFLICT_ERROR', 'Commit failed; fix conflicts and then commit the result', 'en', '', 'admin', 1724030366000);
|
||||
INSERT INTO `sys_i18n`(`id`, `name`, `code`, `value`, `lang`, `remark`, `update_user_id`, `update_timestamp`) VALUES (237, '203001', 'GIT_COMMIT_CONFLICT_ERROR', '提交失败;解决冲突,然后提交结果', 'zh', '', 'admin', 1724030366000);
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
Reference in New Issue
Block a user