diff --git a/README.md b/README.md index 12ba941..d4b2175 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,31 @@ # code-diff -**用于java代码的code-dif && 覆盖数据收集** +**用于java代码的code-diff && 覆盖数据合并收集** +* 支持git仓库获取代码code-diff +* 支持多个实例服务合并收集 +* 支持同分支不同commit代码覆盖率合并收集 +* 支持页面配置管理实例服务:配置jacoco端口;配置includes/excludes表达式 +* 支持实时生成查看报告:报告生成支持使用includes && excldes表达式过滤 +* 支持提供api配合jenkins pipeline集成使用 +* 支持收集函数调用链 -## 部署流程 +## linux部署 +> 需要部署server && web 2个服务 +> FBI warning!!! +> 这2个服务都需要和jenkins部署在同一台linux上 +### server部署 +- 先执行sql文件:[ code-diff/sql/coverage.sql](https://github.com/alwans/code-diff/blob/main/sql/coverage.sql) +- 修改数据库配置:[diff-services/src/main/resources/application.properties](https://github.com/alwans/code-diff/blob/main/diff-services/src/main/resources/application.properties) +- 构建jar:mvn clean install -Dmaven.test.skip=true +- 启动jar服务:java -jar diff-services-1.0.0.jar +### web部署 +- nginx部署web服务,web文件:[code-diff/dist.zip](https://github.com/alwans/code-diff/tree/main/dist) +- 访问wen站点 -> *1.通过nginx部署 /dist/dist.zip 站点* +## docker部署 + + + +待更新... + + +## 示图 + +### 1. 集成到jenkins的pipeline脚本中 +![image](https://user-images.githubusercontent.com/13557160/148341521-fed3ef16-2131-4522-8081-98f3d14cbe39.png) + +### 2. 页面配置应用的jacoco信息 +![image](https://user-images.githubusercontent.com/13557160/148341886-ed653598-b6dc-410e-91c8-08af1cbc6070.png) + +### 3. 收集状态控制 +#### 3.1 支持多台应用同时合并收集 +#### 3.2 支持不同commitId 合并收集 +![image](https://user-images.githubusercontent.com/13557160/148342008-71fa3f30-8c84-48f1-8b74-9b631e12bbe0.png) + +### 4. 实时查看报告 +#### 4.1 支持增量 | 全量报告 +#### 4.2 支持分支增量 | commitId增量 +![image](https://user-images.githubusercontent.com/13557160/148342243-1f83629c-7447-45af-a842-25691e7df1bc.png) +![image](https://user-images.githubusercontent.com/13557160/148342472-f9dff777-3ebe-490b-ba16-5095dcabd8c8.png) +![image](https://user-images.githubusercontent.com/13557160/148342492-de5f0fc0-e0c2-42ed-8934-711cbdb8f87d.png) + + diff --git a/diff-common/pom.xml b/diff-common/pom.xml index 6071ddd..9aa64e4 100644 --- a/diff-common/pom.xml +++ b/diff-common/pom.xml @@ -51,6 +51,10 @@ junit test + + com.alibaba.fastjson2 + fastjson2 + diff --git a/diff-common/src/main/java/com/test/diff/common/util/FastJsonUtil.java b/diff-common/src/main/java/com/test/diff/common/util/FastJsonUtil.java new file mode 100644 index 0000000..180fd6d --- /dev/null +++ b/diff-common/src/main/java/com/test/diff/common/util/FastJsonUtil.java @@ -0,0 +1,19 @@ +package com.test.diff.common.util; + +import com.alibaba.fastjson2.JSON; + +/** + * @author wl + * @date 2022/8/2 + */ +public class FastJsonUtil { + + + public static String serialize (Object object) { + return JSON.toJSONString(object); + } + + public static T deserialize (String str, Class type) { + return JSON.parseObject(str, type); + } +} diff --git a/diff-common/src/test/java/com/test/diff/common/JacksonTest.java b/diff-common/src/test/java/com/test/diff/common/JacksonTest.java index e4e6cfd..3705e03 100644 --- a/diff-common/src/test/java/com/test/diff/common/JacksonTest.java +++ b/diff-common/src/test/java/com/test/diff/common/JacksonTest.java @@ -1,5 +1,9 @@ package com.test.diff.common; +import com.alibaba.fastjson2.JSON; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.test.diff.common.util.JacksonUtil; +import lombok.Data; import org.junit.Test; import java.io.DataOutputStream; @@ -9,8 +13,43 @@ public class JacksonTest { + int deep = 50; + int count = 1; + Node tailNode = null; @Test - public void testOutputStreamSerialize() throws FileNotFoundException { + public void testOutputStreamSerialize() throws FileNotFoundException, JsonProcessingException { + Node head = new Node(); + head.setUri("com/icai/common/redis/RedisUtils.setRedisProperties(Lcom/icai/common/redis/RedisProperties;)V--" +count); + count++; + getNode(head); +// System.out.println(tailNode); +// String str = JacksonUtil.serialize(tailNode); + String str = JSON.toJSONString(tailNode); + System.out.println(str.length()); + } + private void getNode(Node preNode) { + Node node = new Node(); + node.setPreNode(preNode); + node.setCalledNode(preNode); + node.setUri("com/beitai/common/redis/RedisUtils.setRedisProperties(Lcom/beitai/common/redis/RedisProperties;)V--" +count); + count++; + if(count<=deep){ + deep--; + getNode(node); + } + else { + tailNode = node; + } } } + +@Data +class Node { + + private Node preNode; + + private Node calledNode; + + private String uri; +} diff --git a/diff-services/src/main/java/com/test/diff/services/controller/FileController.java b/diff-services/src/main/java/com/test/diff/services/controller/FileController.java index cdd557e..4cd2a9d 100644 --- a/diff-services/src/main/java/com/test/diff/services/controller/FileController.java +++ b/diff-services/src/main/java/com/test/diff/services/controller/FileController.java @@ -5,8 +5,10 @@ import com.test.diff.services.entity.ProjectInfo; import com.test.diff.services.enums.StatusCode; import com.test.diff.services.internal.source.SourceFileHandleFactory; +import com.test.diff.services.params.CloneParamDto; import com.test.diff.services.params.FileParams; import com.test.diff.services.service.ProjectInfoService; +import com.test.diff.services.task.CloneProjectTask; import com.test.diff.services.utils.FileUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -34,31 +36,48 @@ public class FileController { @Resource private FileUtil fileUtil; + @Resource + private CloneProjectTask cloneProjectTask; + @PostMapping(value = "/class/path", produces = "application/json;charset=UTF-8") public BaseResult getFileClassPath(@RequestBody @Validated FileParams fileParams){ ProjectInfo projectInfo = projectInfoService.getById(fileParams.getId()); if(Objects.isNull(projectInfo)){ return BaseResult.error(StatusCode.PROJECT_INFO_NOT_EXISTS); } + String repoPath; if(StringUtils.isNotBlank(fileParams.getCommitId())){ - fileParams.setBranch(fileParams.getBranch()+"_"+fileParams.getCommitId()); - } - String repoPath = fileUtil.getRepoPath(projectInfo, fileParams.getBranch()); - String path = fileUtil.addPath(repoPath, - FileConst.JAVA_CLASS_FILE_PARENT_DIR_NAME, - FileConst.JAVA_CLASS_FILE_DIR_NAME); - if(fileParams.getClassName().contains("/")){ - String[] arr = fileParams.getClassName().split("/"); - path = fileUtil.addPaths(path, arr); + repoPath = fileUtil.getRepoCommitPath(projectInfo, fileParams.getBranch(), fileParams.getCommitId()); + }else{ + repoPath = fileUtil.getRepoPath(projectInfo, fileParams.getBranch()); } - path = path + GitConst.CLASS_FILE_SUFFIX; - if(fileUtil.isExist(path)){ + String classSubPath = fileParams.getClassName() + GitConst.CLASS_FILE_SUFFIX; + String path = fileUtil.getClassFilePath(repoPath, classSubPath); + if(!Objects.isNull(path) && fileUtil.isExist(path)){ return BaseResult.success(path); } + log.error("未找到{} class文件路径,工程id: {}, branch: {}, commitId: {}", fileParams.getId(), fileParams.getClassName(), + fileParams.getBranch(), fileParams.getCommitId()); return BaseResult.error(StatusCode.OTHER_ERROR, "class文件不存在"); } - @GetMapping(value = "copy/project") + /** + * 克隆指定工程的指定分支或者指定commit id代码 + * @param cloneParamDto 克隆参数 + * @return + */ + @GetMapping("/clone/") + public BaseResult cloneProject(@Validated CloneParamDto cloneParamDto){ + ProjectInfo projectInfo = projectInfoService.getById(cloneParamDto.getId()); + if (projectInfo == null){ + return BaseResult.error(StatusCode.OTHER_ERROR, "id="+cloneParamDto.getId().toString()+"的工程不存在, 请检查参数"); + } + cloneProjectTask.checkClone(projectInfo, cloneParamDto.getBranchName(), cloneParamDto.getCommitId()); + cloneProjectTask.cloneProjectTask(projectInfo, cloneParamDto.getBranchName(), cloneParamDto.getCommitId()); + return BaseResult.success(null); + } + + @GetMapping( "copy/project") public BaseResult copyProjectFile(@RequestParam("path") String path, @RequestParam("branch") String branch, @RequestParam("id") int projectId){ diff --git a/diff-services/src/main/java/com/test/diff/services/controller/WebSiteController.java b/diff-services/src/main/java/com/test/diff/services/controller/WebSiteController.java index f288442..ec7c13a 100644 --- a/diff-services/src/main/java/com/test/diff/services/controller/WebSiteController.java +++ b/diff-services/src/main/java/com/test/diff/services/controller/WebSiteController.java @@ -123,6 +123,15 @@ public BaseResult generateReport(@Validated @RequestBody ReportParams params){ return coverageReportService.report(params); } + @GetMapping( "/getReportUri") + public BaseResult getReportUrl(@RequestParam("id") int projectId){ + if(projectId == 0){ + return BaseResult.error(StatusCode.PARAMS_ERROR, "项目id不允许为空"); + } + return coverageReportService.getReportURI(projectId); + } + + /** * 开始或继续收集 * @param apps 应用id列表 diff --git a/diff-services/src/main/java/com/test/diff/services/entity/CoverageApp.java b/diff-services/src/main/java/com/test/diff/services/entity/CoverageApp.java index 5e479be..116d743 100644 --- a/diff-services/src/main/java/com/test/diff/services/entity/CoverageApp.java +++ b/diff-services/src/main/java/com/test/diff/services/entity/CoverageApp.java @@ -51,6 +51,18 @@ public class CoverageApp implements Serializable { @TableField(value = "port") private String port; + /** + * 匹配通配符 + */ + @TableField(value = "includes") + private String includes; + + /** + * 排除通配符 + */ + @TableField(value = "excludes") + private String excludes; + /** * 是否禁用 */ @@ -96,6 +108,8 @@ public boolean equals(Object that) { && (this.getStatus() == null ? other.getStatus() == null : this.getStatus().equals(other.getStatus())) && (this.getHost() == null ? other.getHost() == null : this.getHost().equals(other.getHost())) && (this.getPort() == null ? other.getPort() == null : this.getPort().equals(other.getPort())) + && (this.getIncludes() == null ? other.getIncludes() == null : this.getIncludes().equals(other.getIncludes())) + && (this.getExcludes() == null ? other.getExcludes() == null : this.getExcludes().equals(other.getExcludes())) && (this.getIsDisable() == null ? other.getIsDisable() == null : this.getIsDisable().equals(other.getIsDisable())) && (this.getIsDelete() == null ? other.getIsDelete() == null : this.getIsDelete().equals(other.getIsDelete())) && (this.getAddTime() == null ? other.getAddTime() == null : this.getAddTime().equals(other.getAddTime())) @@ -112,6 +126,8 @@ public int hashCode() { result = prime * result + ((getStatus() == null) ? 0 : getStatus().hashCode()); result = prime * result + ((getHost() == null) ? 0 : getHost().hashCode()); result = prime * result + ((getPort() == null) ? 0 : getPort().hashCode()); + result = prime * result + ((getIncludes() == null) ? 0 : getIncludes().hashCode()); + result = prime * result + ((getExcludes() == null) ? 0 : getExcludes().hashCode()); result = prime * result + ((getIsDisable() == null) ? 0 : getIsDisable().hashCode()); result = prime * result + ((getIsDelete() == null) ? 0 : getIsDelete().hashCode()); result = prime * result + ((getAddTime() == null) ? 0 : getAddTime().hashCode()); @@ -131,6 +147,8 @@ public String toString() { sb.append(", status=").append(status); sb.append(", host=").append(host); sb.append(", port=").append(port); + sb.append(", includes=").append(includes); + sb.append(", excludes=").append(excludes); sb.append(", isDisable=").append(isDisable); sb.append(", isDelete=").append(isDelete); sb.append(", addTime=").append(addTime); diff --git a/diff-services/src/main/java/com/test/diff/services/entity/CoverageReport.java b/diff-services/src/main/java/com/test/diff/services/entity/CoverageReport.java index 739018f..0086fee 100644 --- a/diff-services/src/main/java/com/test/diff/services/entity/CoverageReport.java +++ b/diff-services/src/main/java/com/test/diff/services/entity/CoverageReport.java @@ -35,7 +35,6 @@ public class CoverageReport implements Serializable { /** * 报告类型:0:全量,1:增量 - * {@link com.test.diff.services.enums.ReportTypeEnum} */ @TableField(value = "report_type") private Integer reportType; @@ -64,6 +63,12 @@ public class CoverageReport implements Serializable { @TableField(value = "new_branch") private String newBranch; + /** + * 报告绝对路径 + */ + @TableField(value = "report_uri") + private String reportUri; + /** * 是否删除 */ @@ -101,9 +106,11 @@ public boolean equals(Object that) { && (this.getProjectId() == null ? other.getProjectId() == null : this.getProjectId().equals(other.getProjectId())) && (this.getUuid() == null ? other.getUuid() == null : this.getUuid().equals(other.getUuid())) && (this.getReportType() == null ? other.getReportType() == null : this.getReportType().equals(other.getReportType())) + && (this.getDiffType() == null ? other.getDiffType() == null : this.getDiffType().equals(other.getDiffType())) && (this.getIsUsed() == null ? other.getIsUsed() == null : this.getIsUsed().equals(other.getIsUsed())) && (this.getOldBranch() == null ? other.getOldBranch() == null : this.getOldBranch().equals(other.getOldBranch())) && (this.getNewBranch() == null ? other.getNewBranch() == null : this.getNewBranch().equals(other.getNewBranch())) + && (this.getReportUri() == null ? other.getReportUri() == null : this.getReportUri().equals(other.getReportUri())) && (this.getIsDelete() == null ? other.getIsDelete() == null : this.getIsDelete().equals(other.getIsDelete())) && (this.getAddTime() == null ? other.getAddTime() == null : this.getAddTime().equals(other.getAddTime())) && (this.getLastTime() == null ? other.getLastTime() == null : this.getLastTime().equals(other.getLastTime())); @@ -117,9 +124,11 @@ public int hashCode() { result = prime * result + ((getProjectId() == null) ? 0 : getProjectId().hashCode()); result = prime * result + ((getUuid() == null) ? 0 : getUuid().hashCode()); result = prime * result + ((getReportType() == null) ? 0 : getReportType().hashCode()); + result = prime * result + ((getDiffType() == null) ? 0 : getDiffType().hashCode()); result = prime * result + ((getIsUsed() == null) ? 0 : getIsUsed().hashCode()); result = prime * result + ((getOldBranch() == null) ? 0 : getOldBranch().hashCode()); result = prime * result + ((getNewBranch() == null) ? 0 : getNewBranch().hashCode()); + result = prime * result + ((getReportUri() == null) ? 0 : getReportUri().hashCode()); result = prime * result + ((getIsDelete() == null) ? 0 : getIsDelete().hashCode()); result = prime * result + ((getAddTime() == null) ? 0 : getAddTime().hashCode()); result = prime * result + ((getLastTime() == null) ? 0 : getLastTime().hashCode()); @@ -136,9 +145,11 @@ public String toString() { sb.append(", projectId=").append(projectId); sb.append(", uuid=").append(uuid); sb.append(", reportType=").append(reportType); + sb.append(", diffType=").append(diffType); sb.append(", isUsed=").append(isUsed); sb.append(", oldBranch=").append(oldBranch); sb.append(", newBranch=").append(newBranch); + sb.append(", reportUri=").append(reportUri); sb.append(", isDelete=").append(isDelete); sb.append(", addTime=").append(addTime); sb.append(", lastTime=").append(lastTime); diff --git a/diff-services/src/main/java/com/test/diff/services/enums/StatusCode.java b/diff-services/src/main/java/com/test/diff/services/enums/StatusCode.java index c90865c..2bb6292 100644 --- a/diff-services/src/main/java/com/test/diff/services/enums/StatusCode.java +++ b/diff-services/src/main/java/com/test/diff/services/enums/StatusCode.java @@ -35,6 +35,8 @@ public enum StatusCode { //数据库信息相关 PROJECT_INFO_NOT_EXISTS(30010, "项目信息不存在"), REPO_INFO_NOT_EXITS(30020, "项目对应的仓库信息不存在"), + APP_INFO_NOT_EXISTS(30030, "应用信息不存在"), + REPORT_NOT_EXISTS(30040, "报告不存在"), //操作文件相关 File_ERROR(40010, "文件操作异常"), diff --git a/diff-services/src/main/java/com/test/diff/services/internal/DiffWorkFlow.java b/diff-services/src/main/java/com/test/diff/services/internal/DiffWorkFlow.java index f7789bd..5dc1bc0 100644 --- a/diff-services/src/main/java/com/test/diff/services/internal/DiffWorkFlow.java +++ b/diff-services/src/main/java/com/test/diff/services/internal/DiffWorkFlow.java @@ -156,18 +156,27 @@ private List getDiffCodeClasses(Git newGit,AbstractTreeIterator baseT private Git syncCode(BaseRepository baseRepository, ProjectInfo projectInfo, - String branch,String commitId, boolean isBranchDiff) throws IOException { - return updateCode(baseRepository, FileConst.DEFAULT_EMPTY_PATH, + String branch, String commitId, boolean isBranchDiff) throws IOException { + //这里直接生成路径 + String branch_path = fileUtil.getRepoPath(projectInfo, branch); + if(!isBranchDiff){ + branch_path = branch_path + "_" + commitId; + } + return updateCode(baseRepository, branch_path, projectInfo.getProjectName(),projectInfo.getProjectUrl(), branch, commitId, isBranchDiff); } /** + * 暂时不用了,这里是直接从code-diff根目录开始建项目文件夹的 + * 后期都改成了由group/env/project,这里就不适用了,没有传group和env,所以直接不用 + * 改为上游方法中直接调用fileUtil类中方法直接生成path * 检查项目路径是否正确 * @param path 项目路径 * @param projectName 项目名 * @param branch 分支名 * @return */ + @Deprecated private String checkProjectPath(String path, String projectName, String branch, String commitId, boolean isBranchDiff){ if(!path.startsWith(FileConst.DIFF_ROOT_PATH)){ @@ -194,7 +203,7 @@ private String checkProjectPath(String path, String projectName, String branch, private Git updateCode(BaseRepository repository, String path, String projectName, String projectUrl, String branch, String commitId, boolean isBranchDiff) throws IOException { - path = checkProjectPath(path, projectName, branch, commitId, isBranchDiff); +// path = checkProjectPath(path, projectName, branch, commitId, isBranchDiff); String gitPath = fileUtil.addPath(path, GitConst.GIT_FILE_SUFFIX); if(new File(path).exists()){ if(this.threadLocal.get().isUpdateCode() && isBranchDiff){ diff --git a/diff-services/src/main/java/com/test/diff/services/internal/JavaFileCodeComparator.java b/diff-services/src/main/java/com/test/diff/services/internal/JavaFileCodeComparator.java index eeac639..53aa18e 100644 --- a/diff-services/src/main/java/com/test/diff/services/internal/JavaFileCodeComparator.java +++ b/diff-services/src/main/java/com/test/diff/services/internal/JavaFileCodeComparator.java @@ -148,16 +148,14 @@ public void visit(MethodDeclaration n, List list) { StringBuilder params = new StringBuilder(); NodeList parameters = n.getParameters(); if(!CollectionUtils.isEmpty(parameters)){ - params.append("("); for (int i = 0; i < parameters.size(); i++) { String param = parameters.get(i).getType().toString(); params.append(param.replaceAll(" ", "")); - //和asm一致,每个参数都使用分号结尾 - params.append(";"); + //和asm一致,每个参数都使用分号结尾 + params.append(";"); } - params.append(")"); }else{ - params.append("()"); + params.append(""); } MethodInfo methodInfo = MethodInfo.builder() .md5(md5) diff --git a/diff-services/src/main/java/com/test/diff/services/internal/jacoco/JacocoHandle.java b/diff-services/src/main/java/com/test/diff/services/internal/jacoco/JacocoHandle.java index 6346836..95cd34f 100644 --- a/diff-services/src/main/java/com/test/diff/services/internal/jacoco/JacocoHandle.java +++ b/diff-services/src/main/java/com/test/diff/services/internal/jacoco/JacocoHandle.java @@ -9,6 +9,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -45,7 +46,7 @@ public static ExecFileLoader getLoader(String execFilePath){ * @return */ public static String getBranchName(ExecFileLoader loader){ - return loader.getExecutionDataStore().getBranchName(); + return loader.getBranchName(); } /** @@ -54,7 +55,7 @@ public static String getBranchName(ExecFileLoader loader){ * @return */ public static String getCommitId(ExecFileLoader loader){ - return loader.getExecutionDataStore().getCommitId(); + return loader.getCommitId(); } /** @@ -98,20 +99,24 @@ public static void merge(String newSaveFilePath, String... execFilesPath) throws /** * merge 合并多个exec数据文件, * 2中情况可合并:一:同分支同commit id ;二:同分支,不同commit id + * @param projectId 项目id 不同commit id合并时,需要知道项目id来找出差异类,针对差异类做合并; 同commit可传0 + * @param localPort 本服务对应的端口 --> @Value("${server.port}") * @param newSaveFilePath 合并后保存的文件绝对路径 * @param execFiles exec文件数组 * @throws Exception */ - public static void merge(String newSaveFilePath, File... execFiles) throws Exception { + public static void merge(long projectId, String localPort, String newSaveFilePath, File... execFiles) throws Exception { List params = new ArrayList<>(); params.add("merge"); + params.add("--id"); + params.add(projectId+""); params.add("--destfile"); params.add(newSaveFilePath); for(File file : execFiles){ params.add(file.getAbsolutePath()); } params.add("--diffPort"); - params.add("1990"); + params.add(localPort); int result = JacocoApi.execute(params.toArray(new String[params.size()])); if(result == -1){ throw new BizException("merge 失败"); @@ -131,7 +136,8 @@ public static void report(String execFilePath, List classFilePaths, List sourceFilePaths, String reportPath, - String diffResult) throws Exception { + String diffResult, + String filterRules) throws Exception { List params = new ArrayList<>(); params.add("report"); params.add(execFilePath); @@ -149,7 +155,12 @@ public static void report(String execFilePath, params.add("--diffFiles"); params.add(diffResult); } - int result = JacocoApi.execute(params.toArray(new String[params.size()])); + if(StringUtils.isNotBlank(filterRules)){ + params.add("--filterRules"); + params.add(filterRules); + } + log.info("jacoco report 参数:"+params); + int result = JacocoApi.execute(params.toArray(new String[0])); if(result == -1){ throw new BizException("报告生成失败"); } diff --git a/diff-services/src/main/java/com/test/diff/services/openapi/controller/AppInfoController.java b/diff-services/src/main/java/com/test/diff/services/openapi/controller/AppInfoController.java new file mode 100644 index 0000000..bc0b001 --- /dev/null +++ b/diff-services/src/main/java/com/test/diff/services/openapi/controller/AppInfoController.java @@ -0,0 +1,57 @@ +package com.test.diff.services.openapi.controller; + +import com.test.diff.services.entity.CoverageApp; +import com.test.diff.services.entity.ProjectInfo; +import com.test.diff.services.enums.StatusCode; +import com.test.diff.services.service.CoverageAppService; +import com.test.diff.services.service.ProjectInfoService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.CollectionUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.test.diff.services.base.controller.result.BaseResult; + +import javax.annotation.Resource; +import java.util.Collections; +import java.util.List; + +@RestController +@RequestMapping("api/app") +public class AppInfoController { + + + @Resource + private ProjectInfoService projectInfoService; + + @Resource + private CoverageAppService coverageAppService; + + @GetMapping(value = "/info") + public BaseResult getInfo(@RequestParam("groupName") String groupName, + @RequestParam("projectName") String projectName){ + if(StringUtils.isEmpty(groupName) + || StringUtils.isEmpty(projectName) ){ + return BaseResult.error(StatusCode.PARAMS_ERROR, "参数不允许为空"); + } + List list = projectInfoService.getInfoByName(groupName); + if(CollectionUtils.isEmpty(list)){ + return BaseResult.error(StatusCode.PROJECT_INFO_NOT_EXISTS); + } + for (ProjectInfo projectInfo : list) { + List apps = coverageAppService.getListByProjectId(projectInfo.getId()); +// if(CollectionUtils.isEmpty(apps)){ +// return BaseResult.error(StatusCode.APP_INFO_NOT_EXISTS); +// } + for(CoverageApp app : apps){ + if(app.getAppName().equalsIgnoreCase(projectName)){ + return BaseResult.success(app); + } + } + } + return BaseResult.error(StatusCode.APP_INFO_NOT_EXISTS); + } + +} diff --git a/diff-services/src/main/java/com/test/diff/services/params/CloneParamDto.java b/diff-services/src/main/java/com/test/diff/services/params/CloneParamDto.java new file mode 100644 index 0000000..20fc598 --- /dev/null +++ b/diff-services/src/main/java/com/test/diff/services/params/CloneParamDto.java @@ -0,0 +1,39 @@ +package com.test.diff.services.params; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * @author wl + * @date 2022/10/1 + * @Description 克隆项目请求参数 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class CloneParamDto extends BaseParams { + + @NotNull(message = "工程id不可为空") + private Integer id; + + @NotEmpty(message = "分支名不可为空") + private String branchName; + + private String commitId; + +// @ApiModelProperty(value = "编译命令") +// private String compileInstruction; +// +// /** +// * {@link BuildToolTypeEnum#getValue()} +// */ +// @ApiModelProperty(value = "编译工具类型") +// private Integer buildToolType; +// +// @ApiModelProperty("是否需要编译") +// private Boolean isNeedCompile; +} diff --git a/diff-services/src/main/java/com/test/diff/services/params/CompileParamDto.java b/diff-services/src/main/java/com/test/diff/services/params/CompileParamDto.java new file mode 100644 index 0000000..b0b51fe --- /dev/null +++ b/diff-services/src/main/java/com/test/diff/services/params/CompileParamDto.java @@ -0,0 +1,35 @@ +package com.test.diff.services.params; +; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * @author wl + * @date 2022/9/30 + * @Description 编译参数 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class CompileParamDto extends BaseParams { + + @NotNull(message = "工程id不可为空") + private Integer id; + + @NotEmpty(message = "分支名不可为空") + private String branchName; + + private String commitId; + + private String compileInstruction; + + + private Integer buildToolType; + + private Boolean isRecompile; + +} diff --git a/diff-services/src/main/java/com/test/diff/services/service/CoverageReportService.java b/diff-services/src/main/java/com/test/diff/services/service/CoverageReportService.java index e1bfadf..b53f37c 100644 --- a/diff-services/src/main/java/com/test/diff/services/service/CoverageReportService.java +++ b/diff-services/src/main/java/com/test/diff/services/service/CoverageReportService.java @@ -6,6 +6,8 @@ import com.test.diff.services.params.ProjectDiffParams; import com.test.diff.services.params.ReportParams; +import java.util.List; + /** * * @author wl @@ -26,10 +28,27 @@ public interface CoverageReportService extends IService { */ CoverageReport selectUsedByProjectId(long projectId); + /** + * 正序| 倒序 返回指定项目的所有未删除报告记录列表 + * @param projectId 项目id + * @param isDesc 是否倒序 + * @return + */ + List selectListByProjectId(long projectId, boolean isDesc); + /** * 生成全量/增量报告 * @param params * @return */ BaseResult report(ReportParams params); + + /** + * 获取指定项目的报告地址 + * 如果该项目有收集中的记录,且该记录有生成过报告,那么就返回这条收集中的报告记录;如果收集中且未生成过报告,即返回为空 + * 如果该项目没有收集中的记录,那么返回最近一条报告,状态成功:返回报告地址,状态失败:返回空 + * @param projectId + * @return + */ + BaseResult getReportURI(long projectId); } diff --git a/diff-services/src/main/java/com/test/diff/services/service/impl/CoverageReportServiceImpl.java b/diff-services/src/main/java/com/test/diff/services/service/impl/CoverageReportServiceImpl.java index 70cf66d..15b2f9b 100644 --- a/diff-services/src/main/java/com/test/diff/services/service/impl/CoverageReportServiceImpl.java +++ b/diff-services/src/main/java/com/test/diff/services/service/impl/CoverageReportServiceImpl.java @@ -2,12 +2,14 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.test.diff.common.util.CollectionUtil; import com.test.diff.common.util.JacksonUtil; import com.test.diff.services.base.controller.result.BaseResult; import com.test.diff.services.consts.FileConst; import com.test.diff.services.consts.GitConst; import com.test.diff.services.consts.JacocoConst; import com.test.diff.services.convert.ModelConvert; +import com.test.diff.services.entity.CoverageApp; import com.test.diff.services.entity.CoverageReport; import com.test.diff.services.entity.ProjectInfo; import com.test.diff.services.enums.DiffTypeEnum; @@ -18,31 +20,37 @@ import com.test.diff.services.internal.jacoco.JacocoHandle; import com.test.diff.services.params.ProjectDiffParams; import com.test.diff.services.params.ReportParams; +import com.test.diff.services.service.CoverageAppService; import com.test.diff.services.service.CoverageReportService; import com.test.diff.services.mapper.CoverageReportMapper; import com.test.diff.services.service.ProjectInfoService; import com.test.diff.services.utils.CommonUtil; import com.test.diff.services.utils.FileUtil; +import com.test.diff.services.utils.WildcardMatcher; import com.test.diff.services.vo.ProjectVo; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.apache.ibatis.annotations.Result; import org.jacoco.cli.internal.JacocoApi; import org.jacoco.cli.internal.core.tools.ExecFileLoader; import org.slf4j.Logger; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.io.File; -import java.util.Date; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; @Service @Slf4j public class CoverageReportServiceImpl extends ServiceImpl implements CoverageReportService{ + @Value("${report.domain}") + private String reportDomain; @Resource private CoverageReportMapper coverageReportMapper; @@ -50,6 +58,9 @@ public class CoverageReportServiceImpl extends ServiceImpl selectListByProjectId(long projectId, boolean isDesc) { + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + query.eq(CoverageReport::getProjectId, projectId); + query.eq(CoverageReport::getIsDelete, false); + if(isDesc){ + query.orderByDesc(CoverageReport::getId); + } + return coverageReportMapper.selectList(query); + } + /** * 这个方法要重新写 * @param params @@ -94,7 +116,7 @@ public BaseResult report(ReportParams params) { projectInfoService.updateById(projectInfo); CoverageReport report = selectUsedByProjectId(params.getProjectId()); - //没有使用中的记录,说明没有点开始收集,而是手机点击生成报告。所以先创建一条使用的report记录 + //没有使用中的记录,说明没有点开始收集,而是直接点击生成报告。所以先创建一条使用的report记录 if(Objects.isNull(report)){ create(params.getProjectId()); } @@ -148,7 +170,11 @@ public BaseResult report(ReportParams params) { report.setNewBranch(branch); report.setReportType(ReportTypeEnum.FULL.getCode()); } - JacocoHandle.report(execFilePath, classFilePaths, sourcePaths, uuidDir, diffResult); + //获取匹配规则 + List filters = getAppFilterRules(projectInfo.getId()); + String filterRules = JacksonUtil.serialize(filters); + + JacocoHandle.report(execFilePath, classFilePaths, sourcePaths, uuidDir, diffResult, filterRules); log.info("报告生成完成!"); projectInfo.setReportStatus(ReportStatusEnum.REPORT_SUCCESS.getCode()); projectInfoService.updateById(projectInfo); @@ -160,10 +186,76 @@ public BaseResult report(ReportParams params) { return BaseResult.error(StatusCode.OTHER_ERROR, "报告生成失败"); } report.setLastTime(new Date()); + report.setReportUri(uuidDir); coverageReportMapper.updateById(report); log.info("{}项目报告生成成功", projectInfo.getProjectName()); return BaseResult.success("操作成功"); } + + @Override + public BaseResult getReportURI(long projectId) { + ProjectInfo projectInfo = projectInfoService.getById(projectId); + if(Objects.isNull(projectInfo)){ + return BaseResult.error(StatusCode.PARAMS_ERROR, "id= "+projectId+" 的项目不存在"); + } + CoverageReport report = selectUsedByProjectId(projectId); + if(Objects.isNull(report)){ + List historyReports = selectListByProjectId(projectId, true); + if(CollectionUtils.isEmpty(historyReports)){ + return BaseResult.error(StatusCode.REPORT_NOT_EXISTS); + } + CoverageReport lastReport = historyReports.get(historyReports.size()-1); + if(StringUtils.isEmpty(lastReport.getReportUri())){ + return BaseResult.error(StatusCode.REPORT_NOT_EXISTS, "最新报告记录未生成或生成失败"); + } + return BaseResult.success(reportDomain + lastReport.getReportUri() + "/index.html"); + } + if(StringUtils.isEmpty(report.getReportUri())){ + return BaseResult.error(StatusCode.REPORT_NOT_EXISTS, "请先点击生成报告再查看"); + } + //返回路径不考虑是windows路径 + return BaseResult.success(reportDomain + report.getReportUri() + "/index.html"); + } + + /** + * 获取指定工程:收集中的应用匹配类表达式 + * @param projectId + * @return 返回list格式:[app1-includes, app1-excludes, app2-includes, app2-excludes, ...] + */ + private List getAppFilterRules(long projectId){ + List apps = coverageAppService.getListByProjectId(projectId).stream() + .filter(app -> app.getStatus()) + .collect(Collectors.toList()); + List list = new ArrayList(); + for(CoverageApp app: apps){ + list.add(app.getIncludes()); + list.add(app.getExcludes()==null? "" : app.getExcludes()); + } + return list; + } + + + @Deprecated //(files是文件路径,匹配规则是针对全限定类名的,所以没法用) = 白写 + private List filter(List files, ProjectInfo projectInfo){ + //找出对应项目,收集中的所有应用 + List apps = coverageAppService.getListByProjectId(projectInfo.getId()).stream() + .filter(app -> app.getStatus()).collect(Collectors.toList()); + List list = new ArrayList(); + //过滤无效文件 + for(String file: files){ + //匹配任一一个应用中的includes && excludes 表达式,即为有效文件 + boolean flag = apps.stream().anyMatch(app -> { + WildcardMatcher includes = new WildcardMatcher(app.getIncludes()); + WildcardMatcher excludes = new WildcardMatcher(app.getExcludes()); + return includes.matches(file) && + !excludes.matches(file); + }); + if(flag){ + list.add(file); + } + } + return list; + } } diff --git a/diff-services/src/main/java/com/test/diff/services/service/impl/ProjectInfoServiceImpl.java b/diff-services/src/main/java/com/test/diff/services/service/impl/ProjectInfoServiceImpl.java index 2d9401d..21b315b 100644 --- a/diff-services/src/main/java/com/test/diff/services/service/impl/ProjectInfoServiceImpl.java +++ b/diff-services/src/main/java/com/test/diff/services/service/impl/ProjectInfoServiceImpl.java @@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.jacoco.cli.internal.core.tools.ExecFileLoader; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -42,6 +43,9 @@ public class ProjectInfoServiceImpl extends ServiceImpl implements ProjectInfoService { + @Value("${server.port}") + private String localPort; + @Resource private FileUtil fileUtil; @@ -68,6 +72,7 @@ public Long create(ProjectInfo projectInfo) { public List getInfoByName(String projectName) { LambdaQueryWrapper query = new LambdaQueryWrapper(); query.eq(ProjectInfo::getProjectName, projectName); + query.eq(ProjectInfo::getIsDelete, false); return projectInfoMapper.selectList(query); } @@ -106,7 +111,7 @@ private void pullSingleExec(ProjectInfo projectInfo, CoverageApp coverageApp, C //1.获取数据,先存放在uuid目录中 String execUuidPath = dumpData(coverageApp, projectInfo, report.getUuid(), execName); //2.合并uuid目录中的exec文件 - mergeExec(execUuidPath); + mergeExec(execUuidPath, projectInfo.getId()); //3.把第一步dump下来的数据移至对应分支文件夹中 && 合并分支目录中的exec文件,并删除老数据 String oriExecPath = fileUtil.addPath(execUuidPath, execName); String branchDir = getBranchPath(projectInfo, @@ -118,7 +123,7 @@ private void pullSingleExec(ProjectInfo projectInfo, CoverageApp coverageApp, C log.error("{}文件移动至{}目录失败, 停止合并操作分支目录中相关exec数据", oriExecPath, branchDir, e); return; } - mergeExec(branchDir); + mergeExec(branchDir, projectInfo.getId()); deleteOldExec(branchDir); log.info("{}应用探针数据拉取合并完成...", coverageApp.getAppName()); } @@ -138,7 +143,7 @@ public boolean accept(File pathname) { } } - private void mergeExec(String dirPath){ + private void mergeExec(String dirPath, long projectId){ File dir = new File(dirPath); if(!dir.exists()){ log.error("合并数据的{}目录不存在", dir.getAbsolutePath()); @@ -156,7 +161,7 @@ public boolean accept(File pathname) { } String newFilePath = fileUtil.addPath(dirPath, JacocoConst.EXEC_MERGE_FILE_NAME); try { - JacocoHandle.merge(newFilePath, execFiles); + JacocoHandle.merge(projectId, localPort, newFilePath, execFiles); } catch (Exception e) { e.printStackTrace(); log.error("合并{}目录中的exec文件失败", dirPath, e); diff --git a/diff-services/src/main/java/com/test/diff/services/task/CloneProjectTask.java b/diff-services/src/main/java/com/test/diff/services/task/CloneProjectTask.java new file mode 100644 index 0000000..d12fdb3 --- /dev/null +++ b/diff-services/src/main/java/com/test/diff/services/task/CloneProjectTask.java @@ -0,0 +1,213 @@ +package com.test.diff.services.task; + + +import com.sun.prism.impl.BaseResourceFactory; +import com.test.diff.services.entity.ProjectInfo; +import com.test.diff.services.entity.RepoInfo; +import com.test.diff.services.exceptions.BizException; +import com.test.diff.services.internal.BaseRepository; +import com.test.diff.services.internal.RepositoryFactory; +import com.test.diff.services.params.CompileParamDto; +import com.test.diff.services.service.ProjectInfoService; +import com.test.diff.services.service.RepoInfoService; +import com.test.diff.services.utils.CMDUtils; +import com.test.diff.services.utils.FileUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jgit.api.Git; +import org.jetbrains.annotations.NotNull; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.util.StopWatch; + +import javax.annotation.Resource; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +/** + * @author wl + * @date 2022/9/30 + */ +@Slf4j +@Component +public class CloneProjectTask { + + /** + * clone失败的历史任务 + */ + public static final List failedTasks = new ArrayList<>(); + + /** + * 克隆中的项目 + */ + public static final Map taskMap = new ConcurrentHashMap<>(); + + /** + * 仓库实例 + */ + private static final Map repoInstances = new ConcurrentHashMap<>(); + + @Resource + private RepoInfoService repoInfoService; + + + + /** + * 异步克隆工程代码 + * @param projectInfo 覆盖工程信息 + * @param branch 分支信息 + */ + @Async("compileExecutor") + public void cloneProjectTask(ProjectInfo projectInfo, String branch){ + cloneProject(projectInfo, branch, null, true); + } + + @Async("compileExecutor") + public void cloneProjectTask(ProjectInfo projectInfo, String branch, String commitId){ + cloneProject(projectInfo, branch, commitId, true); + } + + /** + * 检查是否可以克隆 + * @param projectInfo + */ + public void checkClone(ProjectInfo projectInfo, + String branch, String commitId){ + String compileKey = projectInfo.getProjectName() + projectInfo.getEnv() + + branch + commitId; + if (failedTasks.contains(projectInfo.getProjectName())){ + new BizException(projectInfo.getProjectName()+"工程克隆异常, 不再继续尝试"); + } + synchronized (this){ + if (taskMap.get(compileKey) != null){ + throw new BizException(projectInfo.getProjectName()+"项目正在clone中..., 请稍后再试"); + } + taskMap.put(compileKey, projectInfo.getProjectName()); + if (repoInstances.get(projectInfo.getRepoId()) == null){ + RepoInfo repoInfo = repoInfoService.getById(projectInfo.getRepoId()); + if (repoInfo == null){ + throw new BizException(projectInfo.getProjectName() + "项目的仓库信息不存在,克隆失败"); + } + repoInstances.put(projectInfo.getRepoId(), repoInfo); + } + } + } + + /** + * 克隆工程 + * @param projectInfo + * @param branch + * @param commitId + * @param isNeedCompile 是否需要编译 + */ + public void cloneProject(ProjectInfo projectInfo, String branch, String commitId, Boolean isNeedCompile){ + // 手动调用clone检查 +// checkClone(coverageProject, branch, commitId); + RepoInfo repoDto = repoInstances.get(projectInfo.getRepoId()); + String new_branch = ""; + if (StringUtils.isEmpty(commitId)){ + new_branch = branch; + }else { + new_branch = fixBranch(branch, commitId); + } + StopWatch stopWatch = new StopWatch(); + try { + String repoPath = FileUtil.getRepoPath(projectInfo, new_branch); + log.info("开始克隆"+projectInfo.getProjectName()+"工程,仓库路径: "+repoPath); + stopWatch.start(); + BaseRepository repository = RepositoryFactory.create(repoDto); + Git git = repository.clone(projectInfo.getProjectUrl(), repoPath, branch); + git.getRepository().close(); + git.close(); + if (StringUtils.isNotEmpty(commitId)){ + repository.pullByCommitId(repoPath, branch, commitId); + } + stopWatch.stop(); + log.info("{}工程clone完成, 耗时: {}秒", projectInfo.getProjectName(), stopWatch.getTotalTimeSeconds()); + } catch (Exception e) { + stopWatch.stop(); + failedTasks.add(projectInfo.getProjectName()); + log.error("克隆{}项目异常, 耗时: {}秒, 异常原因: {}", projectInfo.getProjectName(), + stopWatch.getTotalTimeSeconds(), + e.getMessage()); + } finally { + String compileKey = projectInfo.getProjectName() + projectInfo.getEnv() + + branch + commitId; + taskMap.remove(compileKey); + } + if (isNeedCompile){ + // 克隆完,顺便编译一下 + CompileParamDto compileParamDto = new CompileParamDto(); + compileParamDto.setId(projectInfo.getId().intValue()); + compileParamDto.setBranchName(branch); + compileParamDto.setCommitId(commitId); + compileSource(projectInfo, compileParamDto); + } + } + + public void compileSource(ProjectInfo projectInfo, CompileParamDto compileParamDto){ + // 计时 + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + String branchPath = compileParamDto.getBranchName(); + if (StringUtils.isNotEmpty(compileParamDto.getCommitId())){ + branchPath = fixBranch(compileParamDto.getBranchName(), compileParamDto.getCommitId()); + } + String repoPath = FileUtil.getRepoPath(projectInfo, branchPath); + log.info("{}工程开始编译...., 分支: {}, commit id: {}", projectInfo.getProjectName(), + compileParamDto.getBranchName(), + compileParamDto.getCommitId()); + // 判断代码是不是存在 + if (!new File(repoPath).exists()){ + log.info("{}项目的{}分支代码不存在,开始clone项目...", projectInfo.getProjectName(), repoPath); + checkClone(projectInfo, compileParamDto.getBranchName(), compileParamDto.getCommitId()); + log.info("{}项目的{}克隆结束,开始编译项目...", projectInfo.getProjectName(), repoPath); + } + try { + // 开始编译 + String execCommand = "cd "+repoPath+" && mvn clean package -Dmaven.test.skip=true"; + CMDUtils.CmdResult result = CMDUtils.exec(execCommand); + if (result.getIsSuccess()) { + log.info("{}工程编译成功", projectInfo.getProjectName()); + } else { + log.error("{}工程编译失败, 编译信息: {}", projectInfo.getProjectName(), result.getContext()); + } + stopWatch.stop(); + log.info("{}工程编译完成, 耗时:{}秒", projectInfo.getProjectName(), stopWatch.getTotalTimeSeconds()); + }finally { + String compileKey = projectInfo.getProjectName() + projectInfo.getEnv() + + compileParamDto.getBranchName() + compileParamDto.getCommitId(); +// compileMap.remove(compileKey); + } + + } + + @Bean("compileExecutor") + public Executor compileExecutor(){ + return Executors.newFixedThreadPool(2, new ThreadFactory() { + @Override + public Thread newThread(@NotNull Runnable r) { + Thread thread = new Thread(r); + thread.setName("compile async thread --" + thread.getId()); + return thread; + } + }); + } + + + public String fixBranch(String branch, String commitId){ + if (branch.contains("/")){ + branch = branch.replaceAll("/", "_" ); + } + return branch + "_" + commitId; + } + +} diff --git a/diff-services/src/main/java/com/test/diff/services/utils/AsmUtil.java b/diff-services/src/main/java/com/test/diff/services/utils/AsmUtil.java new file mode 100644 index 0000000..0fc31b0 --- /dev/null +++ b/diff-services/src/main/java/com/test/diff/services/utils/AsmUtil.java @@ -0,0 +1,11 @@ +package com.test.diff.services.utils; + + +import org.springframework.stereotype.Component; + +@Component +public class AsmUtil { + +// public String getC + +} diff --git a/diff-services/src/main/java/com/test/diff/services/utils/CMDUtils.java b/diff-services/src/main/java/com/test/diff/services/utils/CMDUtils.java new file mode 100644 index 0000000..b9c8c06 --- /dev/null +++ b/diff-services/src/main/java/com/test/diff/services/utils/CMDUtils.java @@ -0,0 +1,196 @@ +package com.test.diff.services.utils; + +import com.test.diff.services.exceptions.BizException; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * @author wl + * @date 2022/8/17 + */ +@Slf4j +public class CMDUtils { + + public static CmdResult exec (String cmdCommand) { + cmdCommand = fixWinCmd(cmdCommand); + StringBuilder stringBuilder = new StringBuilder(); + Process process = null; + try { + process = Runtime.getRuntime().exec(getOsCmd() + cmdCommand ); + BufferedReader bufferedReader = new BufferedReader( + new InputStreamReader(process.getInputStream(), "GBK")); + String line = null; + while((line=bufferedReader.readLine()) != null) + { + stringBuilder.append(line+"\n"); + } + return CmdResult.builder() + .isSuccess(process.exitValue() == 0 ? true : false ) + .status(process.exitValue()) + .context(stringBuilder.toString()) + .build(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static String execShell(String scriptPath, String... params) { + String[] cmd = new String[]{scriptPath}; + cmd = ArrayUtils.addAll(cmd, params); + //解决脚本没有执行权限 + ProcessBuilder builder = new ProcessBuilder("/bin/chmod", "777", scriptPath); + Process process = null; + int runningStatus = 0; + try { + process = builder.start(); + process.waitFor(); + Process ps = Runtime.getRuntime().exec(cmd); + runningStatus = ps.waitFor(); + BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream())); + BufferedReader err = new BufferedReader(new InputStreamReader(ps.getErrorStream())); + StringBuffer sb = new StringBuffer(); + String line; + while ((line = br.readLine()) != null) { + sb.append(line).append("\n"); + } + while ((line = err.readLine()) != null) { + log.error("shell log error...." + line); + sb.append(line).append("\n"); + } + if (runningStatus != 0){ + throw new BizException("exec shell script failed, result: " + sb.toString()); + } + //执行结果 + return sb.toString(); + } catch (IOException e) { + log.error("Failed to exec {}, sandbox load failed", scriptPath); + e.printStackTrace(); + throw new RuntimeException("shell script exec failed, sandbox load failed"); + } catch (InterruptedException e) { + log.error("Failed to exec {}, sandbox load failed", scriptPath); + e.printStackTrace(); + throw new RuntimeException("shell script exec failed, sandbox load failed"); + } + } + + /** + * windows系统,如果指令是cd, 需要先D: ,然后再执行cd D:\home 才能正确进入到home目录下 + * 所以这里特殊处理下 + * @param cmdCommand + * @return + */ + private static String fixWinCmd(String cmdCommand){ + if (OsUtils.isWindows() && cmdCommand.contains("cd")){ + String driveLetter = ""; + // 1.先把多条指令拆分 + String[] commands = cmdCommand.split("&&"); + List commandList = new ArrayList<>(); + for (int i = 0; i < commands.length; i++) { + // 2.找出cd操作的指令 + if (commands[i].trim().startsWith("cd")){ + driveLetter = getDriveLetter(commands[i]); + if (StringUtils.isNotEmpty(driveLetter)){ + commandList.add( driveLetter + ":"); // D: + } + + } + commandList.add(commands[i]); + } + return String.join(" && ", commandList.toArray(new String[0])); + } + return cmdCommand; + } + + /** + * 获取windows系统下,cd命令中的盘符 + * @param command 指令 + * @return + */ + private static String getDriveLetter(String command){ + String driveLetter = ""; + String[] arr = command.split(" "); + for (String s: arr){ + if (s.trim().contains(":\\")){ + driveLetter = s.split(":")[0]; + } + } + return driveLetter.toUpperCase(Locale.ROOT).trim(); + } + + /** + * 获取根指令信息 + * @return + */ + private static String getOsCmd(){ + if (OsUtils.isLinux()){ + return "/bin/sh -c "; + } + else if(OsUtils.isWindows()){ + return "cmd /c "; + } + throw new BizException("系统不支持"); + } + + /** + * 执行结果 + */ + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class CmdResult{ + + /** + * 是否成功 + */ + private Boolean isSuccess; + + /** + * 执行返回状态码 + */ + private Integer status; + + /** + * 执行返回内容 + */ + private String context; + + } + + class inputStreamThread implements Runnable{ + private InputStream ins = null; + private BufferedReader bfr = null; + public inputStreamThread(InputStream ins){ + this.ins = ins; + this.bfr = new BufferedReader(new InputStreamReader(ins)); + } + @Override + public void run() { + String line = null; + byte[] b = new byte[100]; + int num = 0; + try { + while((num=ins.read(b))!=-1){ + System.out.println(new String(b,"gb2312")); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + +} diff --git a/diff-services/src/main/java/com/test/diff/services/utils/FileUtil.java b/diff-services/src/main/java/com/test/diff/services/utils/FileUtil.java index 81a1005..c57c08c 100644 --- a/diff-services/src/main/java/com/test/diff/services/utils/FileUtil.java +++ b/diff-services/src/main/java/com/test/diff/services/utils/FileUtil.java @@ -44,7 +44,7 @@ public void createDir(String path) throws FileException { * @param subPath * @return */ - public String addPath(String... subPath){ + public static String addPath(String... subPath){ StringBuilder sb = new StringBuilder(); for(String path: subPath){ if(StringUtils.isBlank(path)){ @@ -59,9 +59,9 @@ public String addPath(String... subPath){ return sb.toString().substring(0, sb.lastIndexOf(File.separator)); } - public String addPaths(String root, String... sunPath){ + public String addPaths(String root, String... subPath){ StringBuilder path = new StringBuilder(root); - for(String p: sunPath){ + for(String p: subPath){ p = p.replaceAll("/", "_"); path.append(File.separator) .append(p); @@ -79,19 +79,71 @@ public String addPaths(String root, String... sunPath){ * @param branch * @return */ - public String getRepoPath(String group, String env, String projectName, String branch){ + public static String getRepoPath(String group, String env, String projectName, String branch){ if(branch.contains("/")){ branch = branch.replaceAll("/", "_"); } return addPath(FileConst.DIFF_ROOT_PATH, group, env, projectName, branch); } - public String getRepoPath(ProjectInfo projectInfo, String branch){ + public static String getRepoPath(ProjectInfo projectInfo, String branch){ return getRepoPath(projectInfo.getProjectGroup(), projectInfo.getEnv(), projectInfo.getProjectName(), branch); } + /** + * 返回指定commit id绝对路径 + * @param projectInfo + * @param branch + * @param commit + * @return + */ + public String getRepoCommitPath(ProjectInfo projectInfo, String branch, String commit){ + String path = getRepoPath(projectInfo, branch); + path = path + "_" + commit; + return path; + } + + /** + * 根据关键字获取单个指定class文件绝对路径 + * @param rootPath 搜索根目录 + * @param key 关键字符串 + * @return + */ + public String getClassFilePath(String rootPath, String key){ + //简单做下windows平台处理 + if(!key.contains(File.separator)){ + key = key.replace("/", File.separator); + } + String path = null; + List files = getAllClassFilePathsByProject(rootPath); + for(String file : files){ + path = findClassFileByKey(new File(file), key); + } + return path; + } + + private String findClassFileByKey(File file, String key){ + String path = null; + if(file.isDirectory()){ + for(File f: file.listFiles()){ + path = findClassFileByKey(f, key); + if(path != null){ + return path; + } + } + }else{ + if(file.getAbsolutePath().contains(key)){ + return file.getAbsolutePath(); + } + } + if(path!= null){ + return path; + } + return null; + } + public boolean isExist(String path){ return new File(path).exists() ? true : false; } @@ -109,9 +161,17 @@ public List getAllSourcePathsByProject(String branchDirPath){ } public List getAllClassFilePathsByProject(String branchDirPath){ + /** + * gradle编译的路径是build/classes/ + * maven编译的路径是target/classes/ + * 之前没考虑gradle这种,写死了target/classes + * 现在改为只检查是否包含classes + */ +// return getListDirByKey(branchDirPath, +// addPath(FileConst.JAVA_CLASS_FILE_PARENT_DIR_NAME, +// FileConst.JAVA_CLASS_FILE_DIR_NAME)); return getListDirByKey(branchDirPath, - addPath(FileConst.JAVA_CLASS_FILE_PARENT_DIR_NAME, - FileConst.JAVA_CLASS_FILE_DIR_NAME)); + FileConst.JAVA_CLASS_FILE_DIR_NAME); } public List getListDirByKey(String dirPath, String key){ diff --git a/diff-services/src/main/java/com/test/diff/services/utils/OsUtils.java b/diff-services/src/main/java/com/test/diff/services/utils/OsUtils.java new file mode 100644 index 0000000..1f23621 --- /dev/null +++ b/diff-services/src/main/java/com/test/diff/services/utils/OsUtils.java @@ -0,0 +1,20 @@ +package com.test.diff.services.utils; + +/** + * @author wl + * @date 2022/9/29 + */ +public class OsUtils { + + public static final String OS_NAME = System.getProperty("os.name").toLowerCase(); + public static final String WINDOWS_PREFIX = "window"; + public static final String LINUX_PREFIX = "linux"; + + public static Boolean isWindows() { + return OS_NAME.contains(WINDOWS_PREFIX); + } + + public static Boolean isLinux() { + return OS_NAME.contains(LINUX_PREFIX); + } +} diff --git a/diff-services/src/main/java/com/test/diff/services/utils/WildcardMatcher.java b/diff-services/src/main/java/com/test/diff/services/utils/WildcardMatcher.java new file mode 100644 index 0000000..e94a911 --- /dev/null +++ b/diff-services/src/main/java/com/test/diff/services/utils/WildcardMatcher.java @@ -0,0 +1,57 @@ +package com.test.diff.services.utils; + +import java.util.regex.Pattern; + +public class WildcardMatcher { + + private final Pattern pattern; + + /** + * Creates a new matcher with the given expression. + * + * @param expression + * wildcard expressions + */ + public WildcardMatcher(final String expression) { + final String[] parts = expression.split("\\:"); + final StringBuilder regex = new StringBuilder(expression.length() * 2); + boolean next = false; + for (final String part : parts) { + if (next) { + regex.append('|'); + } + regex.append('(').append(toRegex(part)).append(')'); + next = true; + } + pattern = Pattern.compile(regex.toString()); + } + + private static CharSequence toRegex(final String expression) { + final StringBuilder regex = new StringBuilder(expression.length() * 2); + for (final char c : expression.toCharArray()) { + switch (c) { + case '?': + regex.append("."); + break; + case '*': + regex.append(".*"); + break; + default: + regex.append(Pattern.quote(String.valueOf(c))); + break; + } + } + return regex; + } + + /** + * Matches the given string against the expressions of this matcher. + * + * @param s + * string to test + * @return true, if the expression matches + */ + public boolean matches(final String s) { + return pattern.matcher(s).matches(); + } +} diff --git a/diff-services/src/main/resources/application.properties b/diff-services/src/main/resources/application.properties index 58fbbe8..45f5a0d 100644 --- a/diff-services/src/main/resources/application.properties +++ b/diff-services/src/main/resources/application.properties @@ -1,7 +1,8 @@ server.port=1990 -spring.datasource.url= -spring.datasource.username= -spring.datasource.password= +spring.datasource.url=jdbc:mysql://127.0.0.1.158:3306/test_platform?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false +spring.datasource.username=root +spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.jdbc.Driver +#logging.level.com.test.diff.services=trace diff --git a/diff-services/src/main/resources/application.yml b/diff-services/src/main/resources/application.yml new file mode 100644 index 0000000..b5425a8 --- /dev/null +++ b/diff-services/src/main/resources/application.yml @@ -0,0 +1,2 @@ +report: + domain: "http://192.168.100.25:1991" \ No newline at end of file diff --git a/diff-services/src/main/resources/mapper/CoverageAppMapper.xml b/diff-services/src/main/resources/mapper/CoverageAppMapper.xml index e1b8da2..c865fce 100644 --- a/diff-services/src/main/resources/mapper/CoverageAppMapper.xml +++ b/diff-services/src/main/resources/mapper/CoverageAppMapper.xml @@ -11,6 +11,8 @@ + + @@ -20,7 +22,7 @@ id,project_id,app_name, status,host,port, - is_disable,is_delete,add_time, - last_time + includes,excludes,is_disable, + is_delete,add_time,last_time diff --git a/diff-services/src/main/resources/mapper/CoverageReportMapper.xml b/diff-services/src/main/resources/mapper/CoverageReportMapper.xml index d474161..c617554 100644 --- a/diff-services/src/main/resources/mapper/CoverageReportMapper.xml +++ b/diff-services/src/main/resources/mapper/CoverageReportMapper.xml @@ -8,11 +8,12 @@ - + + @@ -20,8 +21,8 @@ id,project_id,uuid, - report_type,diff_type,is_used,old_branch, - new_branch,is_delete,add_time, - last_time + report_type,diff_type,is_used, + old_branch,new_branch,report_uri, + is_delete,add_time,last_time diff --git a/diff-services/src/test/java/com/test/diff/services/CollectionTest.java b/diff-services/src/test/java/com/test/diff/services/CollectionTest.java index 0c6e4f7..3740097 100644 --- a/diff-services/src/test/java/com/test/diff/services/CollectionTest.java +++ b/diff-services/src/test/java/com/test/diff/services/CollectionTest.java @@ -1,23 +1,12 @@ package com.test.diff.services; -import org.jacoco.cli.internal.core.data.ChainNode; import org.junit.Test; import java.util.*; +import java.util.stream.Collectors; public class CollectionTest { - @Test - public void testSetClear(){ - Set sets = new HashSet<>(); - ChainNode node = new ChainNode(); - node.setUri("test"); - sets.add(node); - System.out.println(sets.size()); - sets.clear(); - System.out.println(sets.size()); - } - @Test public void testSetArrayClear(){ Set[] sets = new Set[10]; @@ -40,11 +29,45 @@ public void testStream(){ .forEach(item -> System.out.println(item)); } + @Test + public void testFilter(){ + List list = new ArrayList<>(); + list.add(false); + list.add(true); + List new_list = list.stream().filter(item -> item).collect(Collectors.toList()); + System.out.println(list); + System.out.println(new_list); + } + + @Test + public void testAnyMatch(){ + List list = new ArrayList<>(); + list.add(1); + list.add(2); +// boolean flag = list.stream().anyMatch(item -> item==1); + boolean flag = list.stream().anyMatch(item -> { return item==1;} ); //2中写法 + System.out.println(list); + System.out.println(flag); + } + @Test public void testArraySplit(){ -// String str = "String;String;Integer;"; - String str = "Ljava/lang/String;Lcom/beitai/iot/data/enums/SourceType;"; + String str = "String;String;Integer;"; String[] arr = str.split(";"); System.out.println(arr); } + + @Test + public void testRepeatFilter(){ + HashSet set1 = new HashSet<>(); + HashSet set2 = new HashSet<>(); + set1.add("a"); + set1.add("b"); + set1.add("c"); + //------------ + set2.add("a"); + set2.add("d"); + set1.addAll(set2); + System.out.println(set1); + } } diff --git a/diff-services/src/test/java/com/test/diff/services/ConfigTest.java b/diff-services/src/test/java/com/test/diff/services/ConfigTest.java new file mode 100644 index 0000000..3b41840 --- /dev/null +++ b/diff-services/src/test/java/com/test/diff/services/ConfigTest.java @@ -0,0 +1,29 @@ +package com.test.diff.services; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@SpringBootTest +@RunWith(SpringRunner.class) +public class ConfigTest { + + @Value("${report.domain}") + private String reportDomain; + + @Value("${server.port}") + private String port; + + @Test + public void testReportDomain(){ + System.out.println(reportDomain); + } + + @Test + public void testServerPort(){ + System.out.println(port); + } +} diff --git a/diff-services/src/test/java/com/test/diff/services/CoverageReportTest.java b/diff-services/src/test/java/com/test/diff/services/CoverageReportTest.java index 57f33d5..430ebe5 100644 --- a/diff-services/src/test/java/com/test/diff/services/CoverageReportTest.java +++ b/diff-services/src/test/java/com/test/diff/services/CoverageReportTest.java @@ -2,6 +2,7 @@ import com.test.diff.services.entity.CoverageReport; import com.test.diff.services.service.CoverageReportService; +import com.test.diff.services.service.impl.CoverageReportServiceImpl; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; @@ -17,7 +18,7 @@ public class CoverageReportTest { @Resource - private CoverageReportService coverageReportService; + private CoverageReportServiceImpl coverageReportService; @Test public void testInsert(){ diff --git a/diff-services/src/test/java/com/test/diff/services/FileTest.java b/diff-services/src/test/java/com/test/diff/services/FileTest.java index cb1be88..ecaa694 100644 --- a/diff-services/src/test/java/com/test/diff/services/FileTest.java +++ b/diff-services/src/test/java/com/test/diff/services/FileTest.java @@ -22,6 +22,8 @@ import java.util.concurrent.locks.ReentrantLock; public class FileTest { + + private static final String USER_HOME = System.getProperty("user.home"); private FileUtil fileUtil; @@ -75,20 +77,20 @@ public void testFilePathTest(){ @Test public void testRename(){ - String newPath = "C:\\Users\\wl\\code-diff\\dump.txt"; - String oldPath = "C:\\Users\\wl\\code-diff\\merge.txt"; + String newPath = USER_HOME + "\\code-diff\\dump.txt"; + String oldPath = USER_HOME + "\\code-diff\\merge.txt"; fileUtil.reName(newPath, oldPath); } @Test public void testDelDir() throws IOException { - FileUtils.deleteDirectory(new File("C:\\Users\\wl\\code-diff\\jvmTest\\master_35c27bd393e1f92efb757dfff8d2029bd3163849")); + FileUtils.deleteDirectory(new File(USER_HOME + "\\code-diff\\jvmTest\\master_35c27bd393e1f92efb757dfff8d2029bd3163849")); } @Test public void testAsyncWrite() { ReentrantLock lock = new ReentrantLock(); - File file = new File("C:\\Users\\wl\\code-diff\\12.txt"); + File file = new File(USER_HOME + "\\code-diff\\12.txt"); Thread t1 = new Thread(){ @SneakyThrows @Override @@ -154,7 +156,7 @@ public void testSourcePaths(){ @Test public void testClassPaths(){ - String branPath = "C:\\Users\\wl\\code-diff\\11\\yami-server"; + String branPath = USER_HOME + "\\code-diff\\11\\yami-server"; List list = fileUtil.getAllClassFilePathsByProject(branPath); System.out.println(list.size()); list.stream().forEach(path -> System.out.println(path)); @@ -162,8 +164,8 @@ public void testClassPaths(){ @Test public void testCopyProject() throws IOException { - String oriPath = "C:\\Users\\wl\\code-diff\\yami-server"; - String newPath = "C:\\Users\\wl\\code-diff\\122"; + String oriPath = USER_HOME + "\\code-diff\\yami-server"; + String newPath = USER_HOME + "\\code-diff\\122"; //拷贝.git目录 String oriGitPath = fileUtil.addPath(oriPath, ".git"); String newGitPath = fileUtil.addPath(newPath, ".git"); @@ -194,4 +196,16 @@ public void testCopyProject() throws IOException { }); } + @Test + public void testFindSelectedClassFilePath(){ + String key = "CoverageAppServiceImpl.class"; + if(!key.contains(File.separator)){ + key = key.replace("/", File.separator); + } + System.out.println(key); + String path = fileUtil.getClassFilePath(USER_HOME + "\\code-diff", + key); + System.out.println(path); + } + } diff --git a/diff-services/src/test/java/com/test/diff/services/GitFileHandleTest.java b/diff-services/src/test/java/com/test/diff/services/GitFileHandleTest.java index 9fe11f9..276f276 100644 --- a/diff-services/src/test/java/com/test/diff/services/GitFileHandleTest.java +++ b/diff-services/src/test/java/com/test/diff/services/GitFileHandleTest.java @@ -14,6 +14,8 @@ @RunWith(SpringRunner.class) public class GitFileHandleTest { + private static final String USER_HOME = System.getProperty("user.home"); + @Autowired protected ApplicationContext ctx; @@ -21,7 +23,7 @@ public class GitFileHandleTest { public void testCopyProject(){ SpringUtil.setContext(ctx); ISourceFileHandle handle = SourceFileHandleFactory.build(); - String commitId = handle.copyProjectFile(null, "master", "C:\\Users\\wl\\code-diff\\jvmTest", 1); + String commitId = handle.copyProjectFile(null, "master", USER_HOME + "\\code-diff\\jvmTest", 1); System.out.println(commitId); } } diff --git a/diff-services/src/test/java/com/test/diff/services/GitWorkFlowTest.java b/diff-services/src/test/java/com/test/diff/services/GitWorkFlowTest.java index 90b1ac5..273dc76 100644 --- a/diff-services/src/test/java/com/test/diff/services/GitWorkFlowTest.java +++ b/diff-services/src/test/java/com/test/diff/services/GitWorkFlowTest.java @@ -107,7 +107,7 @@ public void testGetCommitList() throws IOException, GitAPIException { ProjectInfo projectInfo = projectInfoService.getById(1); String brandDir = fileUtil.getRepoPath(projectInfo, branch); // String gitPath = fileUtil.addPath(brandDir, ".git"); - String gitPath = "C:\\Users\\wl\\code-diff\\11\\yami-server\\.git"; + String gitPath = System.getProperty("user.home")+"\\code-diff\\11\\test-server\\.git"; Repository repo = new FileRepository(gitPath); Git git = new Git(repo); RevWalk walk = new RevWalk(repo); diff --git a/diff-services/src/test/java/com/test/diff/services/JacksonTest.java b/diff-services/src/test/java/com/test/diff/services/JacksonTest.java deleted file mode 100644 index aba0e12..0000000 --- a/diff-services/src/test/java/com/test/diff/services/JacksonTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.test.diff.services; - -import com.test.diff.common.util.JacksonUtil; -import org.jacoco.cli.internal.core.data.ChainNode; -import org.junit.Test; - -import java.io.*; -import java.nio.charset.StandardCharsets; - -public class JacksonTest { - - @Test - public void testStreamSerialize() throws IOException { - ChainNode node = new ChainNode(); - node.setUri("12dfj"); - String str = JacksonUtil.serialize(node); - byte[] bytes = str.getBytes(StandardCharsets.UTF_8); - String ss = new String(bytes, StandardCharsets.UTF_8); - ChainNode n = JacksonUtil.deserialize(ss, ChainNode.class); -// ChainNode n = JacksonUtil.deserialize(bytes, ChainNode.class); -// ChainNode n = JacksonUtil.deserialize(str, ChainNode.class); - System.out.println(n.toString()); - } -} diff --git a/diff-services/src/test/java/com/test/diff/services/JacocoHandleTest.java b/diff-services/src/test/java/com/test/diff/services/JacocoHandleTest.java index a827f54..2d8ecab 100644 --- a/diff-services/src/test/java/com/test/diff/services/JacocoHandleTest.java +++ b/diff-services/src/test/java/com/test/diff/services/JacocoHandleTest.java @@ -8,12 +8,12 @@ import com.test.diff.services.params.ProjectDiffParams; import com.test.diff.services.utils.FileUtil; -import org.jacoco.cli.internal.core.data.ChainNode; import org.jacoco.cli.internal.core.data.ExecutionData; import org.jacoco.cli.internal.core.data.ExecutionDataStore; import org.jacoco.cli.internal.core.tools.ExecFileLoader; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @@ -27,50 +27,47 @@ @RunWith(SpringRunner.class) public class JacocoHandleTest { + private static final String USER_HOME = System.getProperty("user.home"); + @Resource private DiffWorkFlow diffWorkFlow; + @Value("${server.port}") + private String port; + @Test public void testDump() throws Exception { File execfile = new File( - "C:\\Users\\wl\\Desktop\\dump2.exec"); + USER_HOME+ "\\dump2.exec"); - JacocoHandle.dump("192.168.100.83", "6300", execfile.getAbsolutePath()); + JacocoHandle.dump("127.0.0.1", "6300", execfile.getAbsolutePath()); } @Test public void testMerge() throws Exception { - String newEexcFilePath = "C:\\Users\\wl\\Desktop\\merge.exec"; - File[] files = new File("C:\\Users\\wl\\Desktop").listFiles(new FileFilter() { + String newEexcFilePath = USER_HOME + "\\merge.exec"; + File[] files = new File("").listFiles(new FileFilter() { @Override public boolean accept(File pathname) { return pathname.getAbsolutePath().endsWith(".exec"); } }); - JacocoHandle.merge(newEexcFilePath, files); - } - - @Test - public void testChainNode() throws IOException, ClassNotFoundException { - File file = new File("C:\\Users\\wl\\Desktop\\obj.txt"); - InputStream is = new FileInputStream(file); - ObjectInputStream ois = new ObjectInputStream(is); - ChainNode node =(ChainNode) ois.readObject(); - System.out.println(node); + JacocoHandle.merge(0, port, newEexcFilePath, files); } @Test public void testLoadExecFile(){ - String execFilePath = "C:\\Users\\wl\\Desktop\\dump.exec"; + String execFilePath = USER_HOME + "\\dump2.exec"; ExecFileLoader loader = JacocoHandle.getLoader(execFilePath); ExecutionDataStore store = loader.getExecutionDataStore(); Collection data = store.getContents(); data.stream() .forEach(exec -> { - if(exec.getName().equals("com/beitai/inventory/managment/service/impl/ImmMerchandiseInventoryServiceImpl") - || exec.getName().equals("com/beitai/inventory/managment/service/impl/ImmSingleProductRecordServiceImpl")){ - System.out.println(exec); - } +// if(exec.getName().equals(" com/scm/financial/service/api/receipt/entity/FundCollectionRegisterImport") +// || exec.getName().equals("com/beitai/inventory/managment/service/impl/ImmSingleProductRecordServiceImpl")){ +// System.out.println(exec); +// } + System.out.println(exec.getName()); }); } @@ -90,12 +87,12 @@ public void testReport() throws Exception { List classInfos = diffWorkFlow.diff(params); - String execFilePath = "C:\\Users\\wl\\Desktop\\dump.exec"; - String projectDir = "C:\\Users\\wl\\Desktop\\feature_v1.1.1_17602254b3c491c2f4c3dcc693db6f41ec5af8e7"; - String reportPath = "C:\\Users\\wl\\Desktop\\report"; + String execFilePath = USER_HOME + "\\dump.exec"; + String projectDir = USER_HOME + "\\feature_v1.1.1_17602254b3c491c2f4c3dcc693db6f41ec5af8e7"; + String reportPath = USER_HOME + "\\report"; FileUtil fileUtil = new FileUtil(); JacocoHandle.report(execFilePath, fileUtil.getAllClassFilePathsByProject(projectDir), - fileUtil.getAllSourcePathsByProject(projectDir),reportPath, JacksonUtil.serialize(classInfos)); + fileUtil.getAllSourcePathsByProject(projectDir),reportPath, JacksonUtil.serialize(classInfos), null); } } diff --git a/diff-services/src/test/java/com/test/diff/services/StringTest.java b/diff-services/src/test/java/com/test/diff/services/StringTest.java new file mode 100644 index 0000000..4ef7b1a --- /dev/null +++ b/diff-services/src/test/java/com/test/diff/services/StringTest.java @@ -0,0 +1,45 @@ +package com.test.diff.services; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; + +public class StringTest { + + @Test + public void testReplace(){ + String str = "/root/code-diff/test/test/test-server/feature_1.0_f7eb21efa925a7588ae424ea3033d3805936fb23/sc-test/sc-job/src/main/java"; + str = str.replaceAll("/", "."); + System.out.println(str); + + } + + @Test + public void testIterator(){ + List list = new ArrayList<>(); + list.add("a"); + list.add("b"); + list.add("c"); + list.add("d"); + for(int i=0; i> local = new ThreadLocal<>(); + System.out.println(local.get()==null); + local.set(new ArrayList<>()); + System.out.println(local.get()); + } + + @Test + public void testLen(){ + System.out.println("".length()); + } +} diff --git a/dist/dist.zip b/dist/dist.zip index b942499..5ea62a2 100644 Binary files a/dist/dist.zip and b/dist/dist.zip differ diff --git a/lib/jacoco-cli.jar b/lib/jacoco-cli.jar index 452fcfb..72cda10 100644 Binary files a/lib/jacoco-cli.jar and b/lib/jacoco-cli.jar differ diff --git a/lib/jacocoagent.jar b/lib/jacocoagent.jar index 8a74553..c4ee486 100644 Binary files a/lib/jacocoagent.jar and b/lib/jacocoagent.jar differ diff --git a/pom.xml b/pom.xml index e0c7ce1..428e535 100644 --- a/pom.xml +++ b/pom.xml @@ -35,9 +35,10 @@ 2.0.2 3.18.0 5.6.3 - 2.12.4 + 2.13.3 4.9.0 2.8.6 + 2.0.10 @@ -128,7 +129,11 @@ gson ${gson.version} - + + com.alibaba.fastjson2 + fastjson2 + ${fastjson2.version} + diff --git a/sql/coverage.sql b/sql/coverage.sql index 7f7342c..a61e952 100644 --- a/sql/coverage.sql +++ b/sql/coverage.sql @@ -1,113 +1,112 @@ /* - Navicat Premium Data Transfer +Navicat MySQL Data Transfer - Source Server : 158 - Source Server Type : MySQL - Source Server Version : 50730 - Source Host : 127.0.0.1:3306 - Source Schema : test_platform +Source Server : 158 +Source Server Version : 50730 +Source Host : 192.168.100.158:3306 +Source Database : test_platform - Target Server Type : MySQL - Target Server Version : 50730 - File Encoding : 65001 +Target Server Type : MYSQL +Target Server Version : 50730 +File Encoding : 65001 - Date: 24/09/2021 10:56:21 +Date: 2022-01-07 09:47:43 */ -SET NAMES utf8mb4; -SET FOREIGN_KEY_CHECKS = 0; +SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for coverage_app -- ---------------------------- DROP TABLE IF EXISTS `coverage_app`; -CREATE TABLE `coverage_app` ( - `id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '自增id', - `project_id` int(10) NOT NULL COMMENT '项目id', - `app_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '应用名', - `status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '收集状态:收集中:未收集', - `host` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '服务器ip', - `port` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'jacoco端口', - `is_disable` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否禁用', - `is_delete` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否删除', - `add_time` datetime(0) NULL DEFAULT NULL COMMENT '添加时间', - `last_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间', - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; +CREATE TABLE `coverage_app` ( + `id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '自增id', + `project_id` int(10) NOT NULL COMMENT '项目id', + `app_name` varchar(255) DEFAULT NULL COMMENT '应用名', + `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '收集状态:收集中:未收集', + `host` varchar(20) NOT NULL COMMENT '服务器ip', + `port` varchar(10) NOT NULL COMMENT 'jacoco端口', + `includes` varchar(255) DEFAULT NULL COMMENT '匹配通配符', + `excludes` varchar(255) DEFAULT NULL COMMENT '排除通配符', + `is_disable` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否禁用', + `is_delete` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除', + `add_time` datetime DEFAULT NULL COMMENT '添加时间', + `last_time` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Table structure for coverage_report -- ---------------------------- DROP TABLE IF EXISTS `coverage_report`; -CREATE TABLE `coverage_report` ( - `id` bigint(10) NOT NULL AUTO_INCREMENT, - `project_id` int(5) NOT NULL COMMENT '项目id', - `uuid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '自动生成的uuid', - `report_type` tinyint(1) NULL DEFAULT NULL COMMENT '报告类型:0:全量,1:增量', - `diff_type` int(11) NULL DEFAULT NULL COMMENT 'diff类型:0:分支diff 1:commit diff', - `is_used` bit(1) NOT NULL DEFAULT b'1' COMMENT '是否正被启用:0:未被使用,1:被使用', - `old_branch` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '基线分支', - `new_branch` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '当前分支', - `is_delete` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', - `add_time` datetime(0) NOT NULL COMMENT '创建时间', - `last_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间', - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; +CREATE TABLE `coverage_report` ( + `id` bigint(10) NOT NULL AUTO_INCREMENT, + `project_id` int(5) NOT NULL COMMENT '项目id', + `uuid` varchar(255) NOT NULL COMMENT '自动生成的uuid', + `report_type` tinyint(1) DEFAULT NULL COMMENT '报告类型:0:全量,1:增量', + `diff_type` int(11) DEFAULT NULL COMMENT 'diff类型:0:分支diff 1:commit diff', + `is_used` bit(1) NOT NULL DEFAULT b'1' COMMENT '是否正被启用:0:未被使用,1:被使用', + `old_branch` varchar(255) DEFAULT NULL COMMENT '基线分支', + `new_branch` varchar(255) DEFAULT NULL COMMENT '当前分支', + `report_uri` varchar(255) DEFAULT NULL COMMENT '报告绝对路径', + `is_delete` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `add_time` datetime NOT NULL COMMENT '创建时间', + `last_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Table structure for diff_project_info -- ---------------------------- DROP TABLE IF EXISTS `diff_project_info`; -CREATE TABLE `diff_project_info` ( - `id` bigint(5) NOT NULL AUTO_INCREMENT COMMENT '自增id', - `repo_id` int(5) NOT NULL COMMENT '仓库id', - `env` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '环境名', - `project_group` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '所属项目组名', - `project_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '项目名', - `project_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '项目仓库地址', - `collect_status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '收集状态:0:未收集,1:收集中,2:暂停收集', - `report_status` tinyint(1) NOT NULL COMMENT '报告状态:0:未生成,1:生成中,2:生成成功,3:生成失败', - `is_disable` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否启用', - `is_delete` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', - `add_time` datetime(0) NOT NULL COMMENT '添加时间', - `last_time` datetime(0) NOT NULL COMMENT '最后修改时间', - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; +CREATE TABLE `diff_project_info` ( + `id` bigint(5) NOT NULL AUTO_INCREMENT COMMENT '自增id', + `repo_id` int(5) NOT NULL COMMENT '仓库id', + `env` varchar(255) NOT NULL COMMENT '环境名', + `project_group` varchar(255) NOT NULL COMMENT '所属项目组名', + `project_name` varchar(255) NOT NULL COMMENT '项目名', + `project_url` varchar(255) NOT NULL COMMENT '项目仓库地址', + `collect_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '收集状态:0:未收集,1:收集中,2:暂停收集', + `report_status` tinyint(1) NOT NULL COMMENT '报告状态:0:未生成,1:生成中,2:生成成功,3:生成失败', + `is_disable` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否启用', + `is_delete` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `add_time` datetime NOT NULL COMMENT '添加时间', + `last_time` datetime NOT NULL COMMENT '最后修改时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; -- ---------------------------- -- Table structure for diff_repo_info -- ---------------------------- DROP TABLE IF EXISTS `diff_repo_info`; -CREATE TABLE `diff_repo_info` ( - `id` bigint(5) NOT NULL AUTO_INCREMENT COMMENT '自增id', - `depot_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '仓库名', - `depot_type` int(2) NOT NULL COMMENT '仓库类型:0:SVN, 1:GIT', - `depot_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '仓库地址:例127.0.0.1:888', - `user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录账号', - `passwd` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录密码', - `is_disable` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否启用', - `is_delete` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', - `add_time` datetime(0) NOT NULL COMMENT '创建时间', - `last_time` datetime(0) NOT NULL COMMENT '最后修改时间', - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; +CREATE TABLE `diff_repo_info` ( + `id` bigint(5) NOT NULL AUTO_INCREMENT COMMENT '自增id', + `depot_name` varchar(100) NOT NULL COMMENT '仓库名', + `depot_type` int(2) NOT NULL COMMENT '仓库类型:0:SVN, 1:GIT', + `depot_url` varchar(255) NOT NULL COMMENT '仓库地址:例127.0.0.1:888', + `user_name` varchar(255) NOT NULL COMMENT '登录账号', + `passwd` varchar(255) NOT NULL COMMENT '登录密码', + `is_disable` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否启用', + `is_delete` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `add_time` datetime NOT NULL COMMENT '创建时间', + `last_time` datetime NOT NULL COMMENT '最后修改时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Table structure for diff_result_info -- ---------------------------- DROP TABLE IF EXISTS `diff_result_info`; -CREATE TABLE `diff_result_info` ( - `id` bigint(100) NOT NULL AUTO_INCREMENT COMMENT '自增id', - `project_id` int(5) NOT NULL COMMENT '项目id', - `diff_type` int(1) NOT NULL COMMENT 'diff类型:0:分支diff,1:commit diff', - `commit_branch` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'commit对应的分支名', - `old_branch` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '对比分支名', - `new_branch` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '目标分支名', - `old_commit_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '对比commitId', - `new_commit_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '目标commitId', - `diff_result` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'diff结果', - `add_time` datetime(0) NOT NULL COMMENT '创建时间', - PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; - -SET FOREIGN_KEY_CHECKS = 1; +CREATE TABLE `diff_result_info` ( + `id` bigint(100) NOT NULL AUTO_INCREMENT COMMENT '自增id', + `project_id` int(5) NOT NULL COMMENT '项目id', + `diff_type` int(1) NOT NULL COMMENT 'diff类型:0:分支diff,1:commit diff', + `commit_branch` varchar(100) DEFAULT NULL COMMENT 'commit对应的分支名', + `old_branch` varchar(100) DEFAULT NULL COMMENT '对比分支名', + `new_branch` varchar(100) DEFAULT NULL COMMENT '目标分支名', + `old_commit_id` varchar(255) DEFAULT NULL COMMENT '对比commitId', + `new_commit_id` varchar(255) DEFAULT NULL COMMENT '目标commitId', + `diff_result` longtext NOT NULL COMMENT 'diff结果', + `add_time` datetime NOT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;