From 90fdcc9276c680d20762fbec94ce1989a04afed3 Mon Sep 17 00:00:00 2001 From: sorie Date: Mon, 28 Feb 2022 22:48:23 +0800 Subject: [PATCH] =?UTF-8?q?git=E6=8F=92=E4=BB=B6=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../magic-api-plugin-git-store/pom.xml | 30 ++++ .../org/ssssssss/magicapi/git/GitRepo.java | 160 ++++++++++++++++++ .../ssssssss/magicapi/git/GitResource.java | 124 ++++++++++++++ .../magicapi/git/GitStoreProperties.java | 68 ++++++++ .../git/MagicGitStoreConfiguration.java | 48 ++++++ .../main/resources/META-INF/spring.factories | 2 + magic-api-plugins/pom.xml | 1 + 7 files changed, 433 insertions(+) create mode 100644 magic-api-plugins/magic-api-plugin-git-store/pom.xml create mode 100644 magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/GitRepo.java create mode 100644 magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/GitResource.java create mode 100644 magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/GitStoreProperties.java create mode 100644 magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/MagicGitStoreConfiguration.java create mode 100644 magic-api-plugins/magic-api-plugin-git-store/src/main/resources/META-INF/spring.factories diff --git a/magic-api-plugins/magic-api-plugin-git-store/pom.xml b/magic-api-plugins/magic-api-plugin-git-store/pom.xml new file mode 100644 index 00000000..6579153c --- /dev/null +++ b/magic-api-plugins/magic-api-plugin-git-store/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + org.ssssssss + magic-api-plugins + 2.0.0-alpha.4 + + magic-api-plugin-git-store + jar + magic-api-plugin-git-store + magic-api-plugin-git-store + + 5.13.0.202109080827-r + + + + org.eclipse.jgit + org.eclipse.jgit + ${jgit.version} + + + org.eclipse.jgit + org.eclipse.jgit.ssh.jsch + ${jgit.version} + + + diff --git a/magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/GitRepo.java b/magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/GitRepo.java new file mode 100644 index 00000000..72b9156d --- /dev/null +++ b/magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/GitRepo.java @@ -0,0 +1,160 @@ +package org.ssssssss.magicapi.git; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jgit.api.*; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; +import org.eclipse.jgit.transport.*; +import org.eclipse.jgit.util.FS; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.ssssssss.magicapi.core.exception.MagicAPIException; + +import java.io.File; +import java.io.IOException; + +/** + * git仓库 + * + * @author soriee + * @date 2022/2/20 22:48 + */ +public class GitRepo { + private static final Logger logger = LoggerFactory.getLogger(GitRepo.class); + /** + * 文件路径地址 + */ + private String rootPath; + private String gitFilePath; + private GitStoreProperties properties; + private Git git; + + public GitRepo(String rootPath, GitStoreProperties properties) { + this.rootPath = rootPath; + this.gitFilePath = rootPath + File.separator + ".git"; + this.properties = properties; + } + + private void valid() { + File repoDir = new File(rootPath); + File gitFile = new File(gitFilePath); + // 如果文件夹不存在 则创建文件夹 + if (!repoDir.exists()) { + repoDir.mkdirs(); + } + if (!gitFile.exists() && repoDir.list().length > 0) { + throw new MagicAPIException("初次项目启动时,请保持文件夹为空。"); + } + } + + /** + * 设置ssh秘钥或者账号密码 + * + * @param transportCommand + * @return + * @author soriee + * @date 2022/2/28 20:06 + */ + private void setSshOrCredentials(TransportCommand transportCommand) { + if (this.getProperties().getPrivateKey() != null) { + // ssh + final SshSessionFactory sshSessionFactory = new JschConfigSessionFactory() { + @Override + protected void configure(OpenSshConfig.Host host, Session session) { + } + + @Override + protected JSch createDefaultJSch(FS fs) throws JSchException { + JSch defaultJSch = super.createDefaultJSch(fs); + defaultJSch.addIdentity(GitRepo.this.getProperties().getPrivateKey()); + return defaultJSch; + } + }; + transportCommand.setTransportConfigCallback(new TransportConfigCallback() { + @Override + public void configure(Transport transport) { + SshTransport sshTransport = (SshTransport) transport; + sshTransport.setSshSessionFactory(sshSessionFactory); + } + }); + } else if (StringUtils.isNotBlank(properties.getUsername()) + && StringUtils.isNotBlank(properties.getPassword())) { + // 账号密码 + transportCommand.setCredentialsProvider(new UsernamePasswordCredentialsProvider( + properties.getUsername(), + properties.getPassword())); + } + } + + /** + * 项目设置仓库 + * + * @return + * @author soriee + * @date 2022/2/24 20:43 + */ + public void setupRepo() throws IOException, GitAPIException { + this.valid(); + File gitFile = new File(gitFilePath); + try { + if (gitFile.exists()) { + // 项目存在,则打开为仓库, 并且强制更新一次 + FileRepositoryBuilder builder = new FileRepositoryBuilder(); + Repository repository = builder.create(gitFile); + git = new Git(repository); + // 更新两次,避免删除文件未更新 + this.update(false); + this.update(true); + } else { + CloneCommand cloneCommand = Git.cloneRepository() + .setURI(properties.getUrl()) + .setDirectory(new File(rootPath)) + .setBranch(properties.getBranch()); + this.setSshOrCredentials(cloneCommand); + git = cloneCommand.call(); + } + } catch (IOException | GitAPIException e) { + logger.error("初始化git仓库失败", e); + throw e; + } + } + + /** + * 更新 + * 1.git add . + * 2.git commit -m "同步数据" + * 3.git pull + * 4.git push + * @param update + * @return + * @author soriee + * @date 2022/2/20 22:54 + */ + public boolean update(boolean update) { + try { + git.add().setUpdate(update).addFilepattern(".").call(); + git.commit().setMessage("同步数据").call(); + PullCommand pull = git.pull(); + this.setSshOrCredentials(pull); + PullResult pullResult = pull.call(); + if (!pullResult.isSuccessful()) { + throw new MagicAPIException("git更新失败, 请重试或尝试手动更新"); + } + PushCommand pushCommand = git.push(); + this.setSshOrCredentials(pushCommand); + pushCommand.call(); + } catch (GitAPIException e) { + logger.error("git更新失败", e); + throw new MagicAPIException("git更新失败, 请重试或尝试手动更新"); + } + return true; + } + + public GitStoreProperties getProperties() { + return properties; + } +} diff --git a/magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/GitResource.java b/magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/GitResource.java new file mode 100644 index 00000000..ee975b85 --- /dev/null +++ b/magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/GitResource.java @@ -0,0 +1,124 @@ +package org.ssssssss.magicapi.git; + +import org.eclipse.jgit.api.errors.GitAPIException; +import org.ssssssss.magicapi.core.resource.FileResource; +import org.ssssssss.magicapi.core.resource.Resource; +import org.ssssssss.magicapi.core.resource.ResourceAdapter; +import org.ssssssss.magicapi.git.GitStoreProperties; +import org.ssssssss.magicapi.utils.IoUtils; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 文件存储实现 + * + * @author mxd + */ +public class GitResource extends FileResource { + private GitRepo gitRepo; + + public static GitResource of(org.ssssssss.magicapi.core.config.Resource config, GitStoreProperties properties) throws IOException, GitAPIException { + File file = new File(config.getLocation()); + GitRepo gitRepo = new GitRepo(file.getAbsolutePath(), properties); + GitResource gitResource = new GitResource(config.isReadonly(), file, + file.getAbsolutePath(), gitRepo); + // 初始化 + gitResource.setup(); + return gitResource; + } + + + public GitResource(boolean readonly, File file, String rootPath, GitRepo gitRepo) { + super(file, readonly, rootPath); + this.gitRepo = gitRepo; + } + /** + * 进行初始化操作, 仅仅在项目启动时进行初始化 + * @author soriee + * @date 2022/2/20 22:30 + * @return + */ + private void setup() throws IOException, GitAPIException { + synchronized(GitResource.class) { + gitRepo.setupRepo(); + } + } + private boolean update(boolean update) { + return gitRepo.update(update); + } + + + @Override + public boolean delete() { + return super.delete() && this.update(true); + } + + @Override + public boolean mkdir() { + return super.mkdir() && this.update(false); + } + @Override + public Resource getResource(String name) { + return new GitResource(super.readonly(), new File(super.file, name), super.rootPath, this.gitRepo); + } + + @Override + public Resource getDirectory(String name) { + return getResource(name); + } + @Override + public boolean write(byte[] bytes) { + return super.write(bytes) && this.update(false); + } + + @Override + public boolean write(String content) { + return super.write(content) && this.update(false); + } + + @Override + public List resources() { + File[] files = this.file.listFiles(); + return files == null ? Collections.emptyList() : Arrays.stream(files).map(it -> new GitResource(this.readonly(), + it, this.rootPath, this.gitRepo)).collect(Collectors.toList()); + } + @Override + public Resource parent() { + return this.rootPath.equals(this.file.getAbsolutePath()) ? null : new GitResource(this.readonly(), + this.file.getParentFile(), this.rootPath, this.gitRepo); + } + @Override + public List dirs() { + return IoUtils.dirs(this.file).stream().map(it -> new GitResource(this.readonly(), + it, this.rootPath, this.gitRepo)).collect(Collectors.toList()); + } + @Override + public List files(String suffix) { + return IoUtils.files(this.file, suffix).stream().map(it -> new GitResource(this.readonly(), + it, this.rootPath, this.gitRepo)).collect(Collectors.toList()); + } + @Override + public boolean renameTo(Resource resource) { + if (!this.readonly()) { + File target = ((GitResource) resource).file; + if (this.file.renameTo(target)) { + this.file = target; + // 更新两次,新增文件和删除文件都要更新 + this.update(false); + this.update(true); + return true; + } + } + return false; + } + + @Override + public String toString() { + return this.gitRepo.getProperties().getUrl(); + } +} diff --git a/magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/GitStoreProperties.java b/magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/GitStoreProperties.java new file mode 100644 index 00000000..ff7db46a --- /dev/null +++ b/magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/GitStoreProperties.java @@ -0,0 +1,68 @@ +package org.ssssssss.magicapi.git; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "magic-api.resource.git") +public class GitStoreProperties { + /** + * git仓库地址 + */ + private String url; + /** + * git分支 + */ + private String branch; + /** + * ssh 密钥地址 + * 仅支持-m PEM参数生产的ssh key + */ + private String privateKey; + /** + * git账号 + */ + private String username; + /** + * git密码 + */ + private String password; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getBranch() { + return branch; + } + + public void setBranch(String branch) { + this.branch = branch; + } + + public String getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/MagicGitStoreConfiguration.java b/magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/MagicGitStoreConfiguration.java new file mode 100644 index 00000000..d8aab900 --- /dev/null +++ b/magic-api-plugins/magic-api-plugin-git-store/src/main/java/org/ssssssss/magicapi/git/MagicGitStoreConfiguration.java @@ -0,0 +1,48 @@ +package org.ssssssss.magicapi.git; + +import org.eclipse.jgit.api.errors.GitAPIException; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.ssssssss.magicapi.core.config.MagicAPIProperties; +import org.ssssssss.magicapi.core.config.MagicPluginConfiguration; +import org.ssssssss.magicapi.core.config.Resource; +import org.ssssssss.magicapi.core.model.Plugin; +import org.ssssssss.magicapi.git.GitStoreProperties; + +import java.io.IOException; + +@Configuration +@EnableConfigurationProperties(GitStoreProperties.class) +public class MagicGitStoreConfiguration implements MagicPluginConfiguration { + + private final MagicAPIProperties properties; + private final GitStoreProperties gitStoreProperties; + + public MagicGitStoreConfiguration(MagicAPIProperties properties, GitStoreProperties gitStoreProperties) { + this.properties = properties; + this.gitStoreProperties = gitStoreProperties; + } + + /** + * git存储 + * @author soriee + * @date 2022/2/28 19:50 + * @return + */ + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(prefix = "magic-api", name = "resource.type", havingValue = "git") + public org.ssssssss.magicapi.core.resource.Resource magicGitResource() throws IOException, GitAPIException { + Resource resourceConfig = properties.getResource(); + return GitResource.of(resourceConfig, this.gitStoreProperties); + } + + + @Override + public Plugin plugin() { + return new Plugin("Git"); + } +} diff --git a/magic-api-plugins/magic-api-plugin-git-store/src/main/resources/META-INF/spring.factories b/magic-api-plugins/magic-api-plugin-git-store/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..83c6a893 --- /dev/null +++ b/magic-api-plugins/magic-api-plugin-git-store/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + org.ssssssss.magicapi.git.MagicGitStoreConfiguration diff --git a/magic-api-plugins/pom.xml b/magic-api-plugins/pom.xml index 241b3d96..11ee112b 100644 --- a/magic-api-plugins/pom.xml +++ b/magic-api-plugins/pom.xml @@ -20,6 +20,7 @@ magic-api-plugin-mongo magic-api-plugin-elasticsearch magic-api-plugin-cluster + magic-api-plugin-git-store