https://www.bilibili.com/video/BV1WaNbzQEoZ
gitlab是一个常用的代码存储仓库,通常都是使用各种工具(IDEA等)创建分支,再去gitlab页面去创建/合并MR。
开发流程分支几乎都是固定的,如 feature > dev > master
。现在开发几乎都是微服务,比如我们两周发一次版本,一次10-40个服务左右,在文档里面已经写好了每个MR的地址,但依旧需要每个人去合并MR,再创建 dev > master
的MR,再合一遍,这里面的步骤是很长的。
基于此做了一个功能,开发填写 feature > dev
的MR,到了合适的时间,1、一键合并(dev),2、一键创建dev > master的mr,3、一键合并(master)。 原本复杂的流程,只需要三个按钮就解决了。
一、系统设计
无非是对接 gitlab的Api,这并不重要,先来看看如何设计的,后面再看怎么写代码。
1、合并的要求
代码合并流程 feature > dev > master
代码合并要求,(防止一些没有准备好的项目被误合并)
- 被打上了 confirmed 标签
- 目标分支正确(前端可以做多个按钮,一键合并dev、一键合并master)
- pom 里面不能有快照版
- 代码没有冲突
- 其它符合的业务的限制
2、合并的流程
其实我们更愿意先解决问题,再一起合并。所以在合并之前需要先有校验功能,当这一批次的项目都校验通过了,再去合并。(无需考虑校验到合并这期间发生的变换,内部人操作大概率不会出现这样的问题)
整个流程如下:
- 填写项目的MR分支(可对接查询接口,模糊搜索MR的分支)
- 点击校验,通过后展示「合并按钮」,不通过,弹窗提示原因 (feature > dev)
- 一键创建MR(功能分支不一样,但 dev > master 的源分支和目标分支都是确定的,可通过Api快速创建)
- 点击校验,通过后展示「合并按钮」,不通过,弹窗提示原因 (dev > master)
二、代码开发
1、gitlab4j-api
虽然gitlab有RestApi,自己封装还是过于麻烦,可以使用 gitlab4j-api
上面是原生Api和封装的Sdk,在开发的时候,可以先去原生Api里面找找看是否有符合的接口,再用gitlab4j-api调用
- rootUrl 就是gitlab请求的地址前缀
- token,可以在gitlab上创建一个拥有所有权限的帐号,然后基于这个帐号生成一个token
导入pom,版本号需要对应当前的gitlab
<dependency>
<groupId>org.gitlab4j</groupId>
<artifactId>gitlab4j-api</artifactId>
<version>5.0.0</version>
</dependency>
2、查询MR
必须要先得到MR,才能去合并MR
try(GitLabApi gitLabApi = new GitLabApi(rootUrl, token)) {
MergeRequestFilter mergeRequestFilter = new MergeRequestFilter();
//默认只返回自己创建的,所以需要指定all
mergeRequestFilter.setScope(Constants.MergeRequestScope.ALL);
mergeRequestFilter.setSearch("模糊搜索key");
List<MergeRequest> mergeRequests = gitLabApi.getMergeRequestApi().getMergeRequests(mergeRequestFilter, 1, 100);
return mergeRequests;
} catch (GitLabApiException e) {
log.error("查询MergeRequest失败", e);
}
MergeRequest 里面的字段很多,后续流程也都是基于MR来做的,这里说一下核心的参数,把 MergeRequest 转成我们需要的参数
buildGitlabMergeRequest
public static GitlabMergeRequest buildGitlabMergeRequest(MergeRequest mr) {
if (mr == null) {
return null;
}
GitlabMergeRequest gitlabMergeRequest = new GitlabMergeRequest();
gitlabMergeRequest.setMergeRequestId(mr.getId().intValue());
gitlabMergeRequest.setMergeRequestIid(mr.getIid().intValue());
gitlabMergeRequest.setGitProjectId(mr.getProjectId().intValue());
gitlabMergeRequest.setTitle(mr.getTitle());
gitlabMergeRequest.setSourceBranch(mr.getSourceBranch());
gitlabMergeRequest.setTargetBranch(mr.getTargetBranch());
gitlabMergeRequest.setLabels(mr.getLabels());
gitlabMergeRequest.setState(mr.getState());
gitlabMergeRequest.setMergeStatus(mr.getMergeStatus());
return gitlabMergeRequest;
}
GitlabMergeRequest 结构
public class GitlabMergeRequest {
/**
* git项目ID
*/
private Integer gitProjectId;
/**
* merge request id
*/
private Integer mergeRequestId;
/**
* merge request iid(项目内部ID,查询MR详情需要用这个ID)
*/
private Integer mergeRequestIid;
/**
* merge request title
*/
private String title;
/**
* source branch
*/
private String sourceBranch;
/**
* target branch
*/
private String targetBranch;
/**
* 标签
*/
private List<String> labels;
/**
* 状态
* opened=打开, closed=关闭, locked=锁定, merged=已合并
*/
private String state;
/**
* 合并状态
* unchecked=Git 还未测试是否可以进行有效合并。
* checking=Git 正在测试是否可以进行有效合并。
* can_be_merged=可以合并。
* cannot_be_merged=不能合并。
* cannot_be_merged_recheck=不能合并,需要重新检查。
*/
private String mergeStatus;
}
2、获取单个MR详情
try(GitLabApi gitLabApi = new GitLabApi(rootUrl, token)) {
MergeRequest mergeRequest = gitLabApi.getMergeRequestApi().getMergeRequest(gitlabMergeRequest.getGitProjectId().longValue(), gitlabMergeRequest.getMergeRequestIid().longValue());
return GitlabFormatUtils.buildGitlabMergeRequest(mergeRequest);
} catch (GitLabApiException e) {
log.error("查询MergeRequest失败", e);
}
GitlabMergeRequest 结构参考查询MR中的,自定义封装的参数
3、合并MR
try(GitLabApi gitLabApi = new GitLabApi(rootUrl, token)) {
MergeRequest mergeRequest = gitLabApi.getMergeRequestApi().acceptMergeRequest(gitlabMergeRequest.getGitProjectId().longValue(), gitlabMergeRequest.getMergeRequestIid().longValue());
return GitlabFormatUtils.buildGitlabMergeRequest(mergeRequest);
} catch (GitLabApiException e) {
log.error("合并MergeRequest失败,gitlabMergeRequest:{}", gitlabMergeRequest, e);
}
- GitlabMergeRequest 结构参考查询MR中的,自定义封装的参数
- MR合并只能一个个来,没有批量的
4、创建MR
try(GitLabApi gitLabApi = new GitLabApi(rootUrl, token)) {
MergeRequestParams mrParams = new MergeRequestParams()
.withSourceBranch(param.getSourceBranch())
.withTargetBranch(param.getTargetBranch())
.withTitle(param.getTitle())
.withDescription(param.getDescription())
.withLabels(param.getLabels());
MergeRequest mergeRequest = gitLabApi.getMergeRequestApi().createMergeRequest(param.getGitProjectId().longValue(), mrParams);
return GitlabFormatUtils.buildGitlabMergeRequest(mergeRequest);
} catch (GitLabApiException e) {
log.error("创建MergeRequest失败", e);
}
5、分支对比
try (GitLabApi gitLabApi = new GitLabApi(rootUrl, token)) {
CompareResults compareResults = gitLabApi.getRepositoryApi().compare(param.getGitProjectId().longValue(), param.getSourceBranch(), param.getTargetBranch(), true);
return compareResults.getDiffs();
} catch (GitLabApiException e) {
log.error("对比分支失败", e);
}
创建MR是为了代码合并,如果dev > master 并没有任何改变,创建一个无法合并的MR也是不好的,在创建MR之前可以获取Diff来判断是否有必要创建
6、获取MR的Diff
MR合并的时候一般不允许有 SNAPSHOT版本,可以通过Diff来判断字符串里面是否有 SNAPSHOT
当把 SNAPSHOT删掉的时候在Diff里面也会存在,但 dev/master里面存在 SNAPSHOT本身也是不合理的
try(GitLabApi gitLabApi = new GitLabApi(rootUrl, token)) {
MergeRequest mergeRequest = gitLabApi.getMergeRequestApi().getMergeRequestChanges(gitlabMergeRequest.getGitProjectId().longValue(), gitlabMergeRequest.getMergeRequestIid().longValue());
return mergeRequest.getChanges();
} catch (GitLabApiException e) {
log.error("查询MergeRequest changes失败", e);
}