✨ [2025-06-13] 添加Java使用Maven及Gitlab教程
All checks were successful
Publish to Confluence / confluence (push) Successful in 1m0s
All checks were successful
Publish to Confluence / confluence (push) Successful in 1m0s
This commit is contained in:
parent
f96e972efe
commit
f69e5f7a4f
444
文档/技术实现/20250613-Java使用Maven及Gitlab.md
Normal file
444
文档/技术实现/20250613-Java使用Maven及Gitlab.md
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
<!-- Space: qifu -->
|
||||||
|
<!-- Parent: 后端技术&知识&规范 -->
|
||||||
|
<!-- Parent: 技术方案 -->
|
||||||
|
<!-- Parent: 基建 -->
|
||||||
|
<!-- Parent: 07-技术实现 -->
|
||||||
|
<!-- Title: 20250613-Java使用Maven及Gitlab -->
|
||||||
|
|
||||||
|
<!-- Macro: :anchor\((.*)\):
|
||||||
|
Template: ac:anchor
|
||||||
|
Anchor: ${1} -->
|
||||||
|
<!-- Macro: \!\[.*\]\((.+)\)\<\!\-\- width=(.*) \-\-\>
|
||||||
|
Template: ac:image
|
||||||
|
Url: ${1}
|
||||||
|
Width: ${2} -->
|
||||||
|
<!-- Macro: \<\!\-\- :toc: \-\-\>
|
||||||
|
Template: ac:toc
|
||||||
|
Printable: 'false'
|
||||||
|
MinLevel: 2
|
||||||
|
MaxLevel: 4 -->
|
||||||
|
<!-- Include: 杂项/声明文件.md -->
|
||||||
|
|
||||||
|
<!-- :toc: -->
|
||||||
|
|
||||||
|
# Java使用Maven及Gitlab
|
||||||
|
|
||||||
|
## Java使用Maven
|
||||||
|
### 所需依赖
|
||||||
|
```xml
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Maven Embedder 核心 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-embedder</artifactId>
|
||||||
|
<version>3.9.6</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.apache.maven/maven-compat -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-compat</artifactId>
|
||||||
|
<version>3.9.6</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- 网络传输支持 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven.resolver</groupId>
|
||||||
|
<artifactId>maven-resolver-transport-http</artifactId>
|
||||||
|
<version>1.9.18</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven.resolver</groupId>
|
||||||
|
<artifactId>maven-resolver-connector-basic</artifactId>
|
||||||
|
<version>1.9.18</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.apache.maven/maven-resolver-provider -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-resolver-provider</artifactId>
|
||||||
|
<version>3.9.6</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- 配置文件支持 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-settings-builder</artifactId>
|
||||||
|
<version>3.9.6</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
```
|
||||||
|
### 使用Archetype生成项目
|
||||||
|
```java
|
||||||
|
package org.jeecg.modules.devops.archetype;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.maven.cli.MavenCli;
|
||||||
|
import org.jeecg.modules.devops.model.YuanMengOnlineService;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class MavenUtils {
|
||||||
|
|
||||||
|
private static final String ARCHETYPE_GROUP_ID = "com.yuanmeng.engine";
|
||||||
|
private static final String ARCHETYPE_ARTIFACT_ID = "engine-archetype-web";
|
||||||
|
private static final String ARCHETYPE_VERSION = "2.0.2-SNAPSHOT";
|
||||||
|
|
||||||
|
public static Path createProject(YuanMengOnlineService archetypeInfo) {
|
||||||
|
try {
|
||||||
|
// 配置项目目录
|
||||||
|
Path projectDir = Files.createTempDirectory(archetypeInfo.getArchetypeArtifactId());
|
||||||
|
log.info("项目生成目录: {}", projectDir);
|
||||||
|
|
||||||
|
// 创建 settings.xml 文件
|
||||||
|
Path settingsFile = createTempSettingsFile();
|
||||||
|
|
||||||
|
// 下面这行代码不能缺少,否则无法执行
|
||||||
|
configureSystemProperties(settingsFile);
|
||||||
|
|
||||||
|
// 使用 Maven Embedder 生成项目
|
||||||
|
generateMavenProject(projectDir.toFile(), settingsFile, archetypeInfo);
|
||||||
|
|
||||||
|
Path path = projectDir.resolve(archetypeInfo.getArchetypeArtifactId());
|
||||||
|
log.info("✅ 项目成功创建于: {}", path);
|
||||||
|
return path;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 出现错误: {}, {}", e.getMessage(), archetypeInfo, e);
|
||||||
|
throw new RuntimeException("项目初始化失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void generateMavenProject(File baseDir, Path settingsFile, YuanMengOnlineService archetypeInfo) {
|
||||||
|
// String[] argsClean = new String[]{
|
||||||
|
// "-X", "dependency:resolve", "-s", settingsFile.getAbsolutePath()
|
||||||
|
// };
|
||||||
|
// int i = new MavenCli().doMain(argsClean, baseDir.getAbsolutePath(), System.out, System.err);
|
||||||
|
|
||||||
|
// boolean del = FileUtil.del("/tmp/repository");
|
||||||
|
// log.info("delete tmp directory: {}", del);
|
||||||
|
|
||||||
|
// 构建 Maven 参数
|
||||||
|
String[] mvnArgs = new String[]{
|
||||||
|
"archetype:generate",
|
||||||
|
"-DinteractiveMode=false",
|
||||||
|
"-DarchetypeGroupId=" + ARCHETYPE_GROUP_ID,
|
||||||
|
"-DarchetypeArtifactId=" + ARCHETYPE_ARTIFACT_ID,
|
||||||
|
"-DarchetypeVersion=" + ARCHETYPE_VERSION,
|
||||||
|
"-DgroupId=" + archetypeInfo.getArchetypeGroupId(),
|
||||||
|
"-DartifactId=" + archetypeInfo.getArchetypeArtifactId(),
|
||||||
|
"-Dversion=" + archetypeInfo.getArchetypeVersion(),
|
||||||
|
"-Dapp=" + archetypeInfo.getArchetypeApp(),
|
||||||
|
"-Dpackage=" + archetypeInfo.getArchetypePackage(),
|
||||||
|
"-B", // 开启批处理模式
|
||||||
|
"-s", settingsFile.toString()
|
||||||
|
};
|
||||||
|
|
||||||
|
log.info("Maven 项目生成指令:{}", String.join(" ", mvnArgs));
|
||||||
|
|
||||||
|
// 使用 Maven Embedder
|
||||||
|
MavenCli cli = new MavenCli();
|
||||||
|
int result = cli.doMain(mvnArgs, baseDir.getAbsolutePath(), System.out, System.err);
|
||||||
|
|
||||||
|
if (result != 0) {
|
||||||
|
throw new RuntimeException("Maven 项目生成失败 (退出码: " + result + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("✅ Maven 项目生成成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Path createTempSettingsFile() {
|
||||||
|
try {
|
||||||
|
// 创建临时 settings.xml 文件
|
||||||
|
Path settingsPath = Files.createTempFile("maven-settings", ".xml");
|
||||||
|
|
||||||
|
// 构建 settings.xml 内容
|
||||||
|
String settingsContent = buildSettingsXml();
|
||||||
|
|
||||||
|
// 写入文件
|
||||||
|
Files.write(settingsPath, settingsContent.getBytes());
|
||||||
|
|
||||||
|
// 设置文件删除钩子(程序退出时删除)
|
||||||
|
settingsPath.toFile().deleteOnExit();
|
||||||
|
|
||||||
|
return settingsPath;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Failed to create temp settings.xml", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildSettingsXml() {
|
||||||
|
return "<settings xmlns=\"http://maven.apache.org/SETTINGS/1.2.0\"\n" +
|
||||||
|
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
|
||||||
|
" xsi:schemaLocation=\"http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd\">\n" +
|
||||||
|
" <localRepository>/tmp/repository</localRepository>\n" +
|
||||||
|
" <servers>\n" +
|
||||||
|
" <server>\n" +
|
||||||
|
" <id>private-repo</id>\n" +
|
||||||
|
" <username>developer</username>\n" +
|
||||||
|
" <password>deve@123</password>\n" +
|
||||||
|
" </server>\n" +
|
||||||
|
" </servers>\n" +
|
||||||
|
" <mirrors>\n" +
|
||||||
|
" <mirror>\n" +
|
||||||
|
" <id>private-repo</id>\n" +
|
||||||
|
" <name>Private Repository Mirror</name>\n" +
|
||||||
|
" <url>http://120.79.68.191:8081/repository/maven-public/</url>\n" +
|
||||||
|
" <mirrorOf>*</mirrorOf>\n" +
|
||||||
|
" </mirror>\n" +
|
||||||
|
" </mirrors>\n" +
|
||||||
|
" <profiles>\n" +
|
||||||
|
" <profile>\n" +
|
||||||
|
" <id>default</id>\n" +
|
||||||
|
" <repositories>\n" +
|
||||||
|
" <repository>\n" +
|
||||||
|
" <id>private-repo</id>\n" +
|
||||||
|
" <url>http://120.79.68.191:8081/repository/maven-public/</url>\n" +
|
||||||
|
" <releases><enabled>true</enabled></releases>\n" +
|
||||||
|
" <snapshots><enabled>true</enabled></snapshots>\n" +
|
||||||
|
" </repository>\n" +
|
||||||
|
" </repositories>\n" +
|
||||||
|
" <pluginRepositories>\n" +
|
||||||
|
" <pluginRepository>\n" +
|
||||||
|
" <id>private-repo</id>\n" +
|
||||||
|
" <url>http://120.79.68.191:8081/repository/maven-public/</url>\n" +
|
||||||
|
" <releases><enabled>true</enabled></releases>\n" +
|
||||||
|
" <snapshots><enabled>true</enabled></snapshots>\n" +
|
||||||
|
" </pluginRepository>\n" +
|
||||||
|
" </pluginRepositories>\n" +
|
||||||
|
" </profile>\n" +
|
||||||
|
" </profiles>\n" +
|
||||||
|
" <activeProfiles>\n" +
|
||||||
|
" <activeProfile>default</activeProfile>\n" +
|
||||||
|
" </activeProfiles>\n" +
|
||||||
|
"</settings>";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void configureSystemProperties(Path settingsPath) {
|
||||||
|
System.setProperty("maven.multiModuleProjectDirectory", System.getProperty("user.dir"));
|
||||||
|
// 使用自定义 settings.xml
|
||||||
|
System.setProperty("maven.user.settings", settingsPath.toString());
|
||||||
|
|
||||||
|
// 配置超时(可选)
|
||||||
|
System.setProperty("maven.wagon.http.readTimeout", "300000"); // 5分钟
|
||||||
|
System.setProperty("maven.wagon.http.connectionTimeout", "120000");
|
||||||
|
|
||||||
|
// 禁用 SSL 验证(如果私服使用 HTTP)
|
||||||
|
System.setProperty("maven.wagon.http.ssl.insecure", "true");
|
||||||
|
System.setProperty("maven.wagon.http.ssl.allowall", "true");
|
||||||
|
|
||||||
|
// 启用调试日志(可选)
|
||||||
|
System.setProperty("org.slf4j.simpleLogger.log.org.apache.maven.cli", "info");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
----
|
||||||
|
## Java操作Git及Gitlab
|
||||||
|
### 依赖引入
|
||||||
|
```xml
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jgit</groupId>
|
||||||
|
<artifactId>org.eclipse.jgit</artifactId>
|
||||||
|
<version>6.10.1.202505221210-r</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/org.gitlab4j/gitlab4j-api -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gitlab4j</groupId>
|
||||||
|
<artifactId>gitlab4j-api</artifactId>
|
||||||
|
<version>5.3.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
```
|
||||||
|
### Java代码调用
|
||||||
|
#### Gitlab调用工具类
|
||||||
|
```java
|
||||||
|
package org.jeecg.modules.devops.archetype;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.gitlab4j.api.GitLabApi;
|
||||||
|
import org.gitlab4j.api.GitLabApiException;
|
||||||
|
import org.gitlab4j.api.models.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* insert description here
|
||||||
|
*
|
||||||
|
* @author liuxiaohua
|
||||||
|
* @since 2025-06-11
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class GitlabUtils {
|
||||||
|
|
||||||
|
private static volatile GitLabApi gitLabApi;
|
||||||
|
|
||||||
|
public static synchronized void init(ArchetypeProperties archetypeProperties) {
|
||||||
|
if (gitLabApi == null) {
|
||||||
|
synchronized (GitlabUtils.class) {
|
||||||
|
if (gitLabApi == null) {
|
||||||
|
gitLabApi = new GitLabApi(archetypeProperties.getGitlabUrl(), archetypeProperties.getAccessToken());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static void deleteProject(Long projectId) {
|
||||||
|
gitLabApi.getProjectApi().deleteProject(projectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static Project createProject(String group, String project, String desc) {
|
||||||
|
Group groupInfo = getGroupId(group);
|
||||||
|
if (groupInfo == null || groupInfo.getId() == null) {
|
||||||
|
throw new RuntimeException("获取分组信息失败");
|
||||||
|
}
|
||||||
|
Project gitLabRepository = createRepository(groupInfo.getId(), project, desc);
|
||||||
|
if (gitLabRepository == null || gitLabRepository.getId() == null) {
|
||||||
|
throw new RuntimeException("创建仓库失败");
|
||||||
|
}
|
||||||
|
return gitLabRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static ProtectedBranch protectedBranch(Long projectId, Integer retryTimes) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取已保护的分支
|
||||||
|
List<ProtectedBranch> protectedBranches = gitLabApi.getProtectedBranchesApi().getProtectedBranches(projectId);
|
||||||
|
log.info("protected branches: {}", JSONObject.toJSONString(protectedBranches));
|
||||||
|
// 保护分支
|
||||||
|
if (CollUtil.isNotEmpty(protectedBranches)
|
||||||
|
&& protectedBranches.stream().map(ProtectedBranch::getName).anyMatch("master"::equalsIgnoreCase)) {
|
||||||
|
// 取消旧的配置
|
||||||
|
unprotectedBranch(projectId);
|
||||||
|
}
|
||||||
|
// 添加分支保护
|
||||||
|
return gitLabApi.getProtectedBranchesApi()
|
||||||
|
.protectBranch(projectId, "master", AccessLevel.NONE, AccessLevel.MAINTAINER);
|
||||||
|
} catch (GitLabApiException e) {
|
||||||
|
log.warn("protected branch failed: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retryTimes < 3) {
|
||||||
|
return protectedBranch(projectId, ++retryTimes);
|
||||||
|
}
|
||||||
|
log.warn("protected branch failure: {}", retryTimes);
|
||||||
|
throw new RuntimeException(ArchetypeUtils.WARN_MESSAGE_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void unprotectedBranch(Long projectId) {
|
||||||
|
try {
|
||||||
|
gitLabApi.getProtectedBranchesApi().unprotectBranch(projectId, "master");
|
||||||
|
log.info("unprotected branch master success");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("unprotected branch error: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分组路径获取分组ID
|
||||||
|
*/
|
||||||
|
private static Group getGroupId(String groupPath) throws GitLabApiException {
|
||||||
|
return gitLabApi.getGroupApi().getGroup(groupPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在 GitLab 创建新仓库
|
||||||
|
*/
|
||||||
|
private static Project createRepository(Long groupId,
|
||||||
|
String projectName,
|
||||||
|
String desc) {
|
||||||
|
try {
|
||||||
|
Project project = new Project()
|
||||||
|
.withName(projectName)
|
||||||
|
.withDefaultBranch("master")
|
||||||
|
.withDescription(desc)
|
||||||
|
.withNamespaceId(groupId)
|
||||||
|
.withVisibility(Visibility.PRIVATE); // 私有库用 PRIVATE
|
||||||
|
|
||||||
|
return gitLabApi.getProjectApi().createProject(project); // 返回 HTTPS 仓库地址
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("create project error: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
#### Git调用工具类
|
||||||
|
```java
|
||||||
|
package org.jeecg.modules.devops.archetype;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.eclipse.jgit.api.Git;
|
||||||
|
import org.eclipse.jgit.transport.CredentialsProvider;
|
||||||
|
import org.eclipse.jgit.transport.URIish;
|
||||||
|
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* insert description here
|
||||||
|
*
|
||||||
|
* @author liuxiaohua
|
||||||
|
* @since 2025-06-11
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GitUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化本地仓库并推送到 GitLab
|
||||||
|
*/
|
||||||
|
public static void pushLocalCodeToGitLab(String remoteUrl,
|
||||||
|
String codePath,
|
||||||
|
ArchetypeProperties archetypeProperties) {
|
||||||
|
File localPath = new File(codePath);
|
||||||
|
CredentialsProvider credentials = new UsernamePasswordCredentialsProvider(
|
||||||
|
archetypeProperties.getUsername(), archetypeProperties.getAccessToken());
|
||||||
|
|
||||||
|
try (Git git = Git.init().setDirectory(localPath).call()) {
|
||||||
|
// 添加所有文件
|
||||||
|
git.add().addFilepattern(".").call();
|
||||||
|
|
||||||
|
// 提交更改
|
||||||
|
git.commit()
|
||||||
|
.setMessage(String.format("\uD83C\uDF89 [%s] 初始化提交",
|
||||||
|
new SimpleDateFormat("yyyy-MM-dd").format(new Date())))
|
||||||
|
.call();
|
||||||
|
|
||||||
|
// 添加远程仓库
|
||||||
|
git.remoteAdd()
|
||||||
|
.setName("origin")
|
||||||
|
.setUri(new URIish(remoteUrl))
|
||||||
|
.call();
|
||||||
|
|
||||||
|
// 推送到 GitLab
|
||||||
|
git.push()
|
||||||
|
.setRemote("origin")
|
||||||
|
.setCredentialsProvider(credentials)
|
||||||
|
.setForce(true)
|
||||||
|
.setPushAll()
|
||||||
|
.call();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
Loading…
x
Reference in New Issue
Block a user