keyfil/文档/技术实现/20250613-Java使用Maven及Gitlab.md
liuxiaohua 26a842b04f
All checks were successful
Publish to Confluence / confluence (push) Successful in 52s
[2025-06-17] 完善文档
2025-06-17 18:12:00 +08:00

18 KiB
Raw Permalink Blame History

Java使用Maven及Gitlab

Java使用Maven

所需依赖


<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生成项目

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) {
        // 构建 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

依赖引入


<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调用工具类

package org.jeecg.modules.devops.archetype;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
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;
import java.util.Objects;

/**
 * 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 (Objects.isNull(groupInfo) || Objects.isNull(groupInfo.getId())) {
            throw new RuntimeException("获取分组信息失败");
        }
        Project gitLabRepository = createRepository(groupInfo, 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().getOptionalGroup(groupPath).orElse(null);
    }

    /**
     * 在 GitLab 创建新仓库
     */
    private static Project createRepository(Group group,
                                            String projectName,
                                            String desc) {
        Project exists = gitLabApi.getProjectApi().getOptionalProject(group.getName(), projectName).orElse(null);
        if (Objects.nonNull(exists)) {
            throw new RuntimeException("项目已经存在");
        }

        try {
            Project project = new Project()
                    .withName(projectName)
                    .withDefaultBranch("master")
                    .withDescription(desc)
                    .withNamespaceId(group.getId())
                    .withVisibility(Visibility.PRIVATE); // 私有库用 PRIVATE

            return gitLabApi.getProjectApi().createProject(project);  // 返回 HTTPS 仓库地址
        } catch (Exception e) {
            log.warn("create project error: {}", e.getMessage());
        }
        return null;
    }

    public static ProjectHook addWebhook(Long projectId, String webhookUrl, Integer retryTimes) {
        try {
            // 配置 webhook
            ProjectHook projectHook = gitLabApi.getProjectApi().addHook(
                    projectId, webhookUrl, Boolean.FALSE, Boolean.FALSE, Boolean.TRUE);
            log.info("add project hook: {}", JSONObject.toJSONString(projectHook));
            return projectHook;
        } catch (GitLabApiException e) {
            log.warn("add project hook failed: {}", e.getMessage());
        }

        if (retryTimes < 3) {
            return addWebhook(projectId, webhookUrl, ++retryTimes);
        }
        log.warn("add project hook failure: {}", retryTimes);
        throw new RuntimeException(ArchetypeUtils.WARN_MESSAGE_2);
    }

    public static Member addMembers(Long projectId, String email, Integer retryTimes) {
        try {
            if (StrUtil.isBlank(email)) {
                email = "liuxiaohua@keyfil.com";
            }
            if (!email.endsWith("@keyfil.com")) {
                email += "@keyfil.com";
            }
            User userByEmail = gitLabApi.getUserApi().getOptionalUserByEmail(email).orElse(null);
            if (userByEmail == null) {
                log.warn("get user by email error");
                throw new RuntimeException(ArchetypeUtils.WARN_MESSAGE_3);
            }
            log.info("add members user: {}", JSONObject.toJSONString(userByEmail));

            Member member = gitLabApi.getProjectApi().getOptionalMember(projectId, userByEmail.getId()).orElse(null);
            log.info("member exists: {}", JSONObject.toJSONString(member));
            if (Objects.nonNull(member)) {
                if (member.getAccessLevel().value < AccessLevel.MAINTAINER.value) {
                    member = gitLabApi.getProjectApi().updateMember(projectId, userByEmail.getId(), AccessLevel.MAINTAINER);
                }
            } else {
                member = gitLabApi.getProjectApi()
                        .getOptionalMember(projectId, userByEmail.getId(), Boolean.TRUE)
                        .orElse(null);
                if (Objects.isNull(member) || member.getAccessLevel().value < AccessLevel.MAINTAINER.value) {
                    member = gitLabApi.getProjectApi().addMember(projectId, userByEmail.getId(), AccessLevel.MAINTAINER);
                }
            }
            return member;
        } catch (GitLabApiException e) {
            log.warn("add members user: failed: {}", e.getMessage());
        }

        if (retryTimes < 3) {
            return addMembers(projectId, email, ++retryTimes);
        }

        log.warn("add members user failure: {}", retryTimes);
        throw new RuntimeException(ArchetypeUtils.WARN_MESSAGE_3);
    }

}


Git调用工具类

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);
        }
    }

}