org.dromara.jpom.build.ReleaseManage Maven / Gradle / Ivy
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.dromara.jpom.build;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.text.CharPool;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.extra.ssh.JschUtil;
import cn.keepbx.jpom.model.JsonMessage;
import cn.keepbx.jpom.plugins.IPlugin;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.Session;
import lombok.Builder;
import lombok.Lombok;
import lombok.extern.slf4j.Slf4j;
import org.dromara.jpom.JpomApplication;
import org.dromara.jpom.common.BaseServerController;
import org.dromara.jpom.common.forward.NodeForward;
import org.dromara.jpom.common.forward.NodeUrl;
import org.dromara.jpom.func.assets.model.MachineSshModel;
import org.dromara.jpom.func.assets.server.MachineDockerServer;
import org.dromara.jpom.func.files.service.FileStorageService;
import org.dromara.jpom.model.AfterOpt;
import org.dromara.jpom.model.BaseEnum;
import org.dromara.jpom.model.EnvironmentMapBuilder;
import org.dromara.jpom.model.data.BuildInfoModel;
import org.dromara.jpom.model.data.NodeModel;
import org.dromara.jpom.model.data.SshModel;
import org.dromara.jpom.model.docker.DockerInfoModel;
import org.dromara.jpom.model.enums.BuildReleaseMethod;
import org.dromara.jpom.model.enums.BuildStatus;
import org.dromara.jpom.model.outgiving.OutGivingModel;
import org.dromara.jpom.model.user.UserModel;
import org.dromara.jpom.outgiving.OutGivingRun;
import org.dromara.jpom.plugin.PluginFactory;
import org.dromara.jpom.plugins.JschUtils;
import org.dromara.jpom.service.docker.DockerInfoService;
import org.dromara.jpom.service.docker.DockerSwarmInfoService;
import org.dromara.jpom.service.node.NodeService;
import org.dromara.jpom.service.node.ssh.SshService;
import org.dromara.jpom.system.ExtConfigBean;
import org.dromara.jpom.system.JpomRuntimeException;
import org.dromara.jpom.system.extconf.BuildExtConfig;
import org.dromara.jpom.util.CommandUtil;
import org.dromara.jpom.util.LogRecorder;
import org.dromara.jpom.util.MySftp;
import org.dromara.jpom.util.StringUtil;
import org.springframework.util.Assert;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* 发布管理
*
* @author bwcx_jzy
* @since 2019/7/19
*/
@Builder
@Slf4j
public class ReleaseManage {
private final UserModel userModel;
private final Integer buildNumberId;
/**
* 回滚来源的构建 id
*/
private Integer fromBuildNumberId;
private final BuildExtraModule buildExtraModule;
private final String logId;
private final BuildExecuteService buildExecuteService;
private final DockerInfoService dockerInfoService;
private final MachineDockerServer machineDockerServer;
private final FileStorageService fileStorageService;
private final BuildExtConfig buildExtConfig;
private EnvironmentMapBuilder buildEnv;
private LogRecorder logRecorder;
private File resultFile;
private Integer getRealBuildNumberId() {
return ObjectUtil.defaultIfNull(this.fromBuildNumberId, this.buildNumberId);
}
private void init() {
if (this.logRecorder == null) {
// 回滚的时候需要重新创建对象
File logFile = BuildUtil.getLogFile(buildExtraModule.getId(), this.buildNumberId);
this.logRecorder = LogRecorder.builder().file(logFile).build();
}
Assert.notNull(buildEnv, "没有找到任何环境变量");
}
private void updateStatus(BuildStatus status, String msg) {
buildExecuteService.updateStatus(this.buildExtraModule.getId(), this.logId, this.buildNumberId, status, msg);
}
/**
* 不修改为发布中状态
*/
public String start(Consumer consumer, BuildInfoModel buildInfoModel) throws Exception {
this.init();
this.resultFile = buildExtraModule.resultDirFile(this.getRealBuildNumberId());
this.buildEnv.put("BUILD_RESULT_FILE", FileUtil.getAbsolutePath(this.resultFile));
this.buildEnv.put("BUILD_RESULT_DIR_FILE", buildExtraModule.getResultDirFile());
//
this.updateStatus(BuildStatus.PubIng, "开始发布中");
if (FileUtil.isEmpty(this.resultFile)) {
String info = "发布的文件或者文件夹为空,不能继续发布";
logRecorder.systemError(info);
return info;
}
long resultFileSize = FileUtil.size(this.resultFile);
logRecorder.system("开始执行发布,需要发布的文件大小:{}", FileUtil.readableFileSize(resultFileSize));
Optional.ofNullable(consumer).ifPresent(consumer1 -> consumer1.accept(resultFileSize));
// 先同步到文件管理中心
Boolean syncFileStorage = this.buildExtraModule.getSyncFileStorage();
if (syncFileStorage != null && syncFileStorage) {
// 处理保留天数
Integer fileStorageKeepDay =
Optional.ofNullable(this.buildExtraModule.getFileStorageKeepDay())
.map(integer -> Convert.toInt(buildExtraModule.getFileStorageKeepDay()))
.filter(integer -> integer > 0)
.orElse(null);
String keepMsg = fileStorageKeepDay == null ? StrUtil.EMPTY : StrUtil.format(",保留天数:{}", fileStorageKeepDay);
logRecorder.system("开始同步到文件管理中心{}", keepMsg);
boolean tarGz = this.buildEnv.getBool(BuildUtil.USE_TAR_GZ, false);
File dirPackage = BuildUtil.loadDirPackage(this.buildExtraModule.getId(), this.getRealBuildNumberId(), this.resultFile, tarGz, (unZip, file) -> file);
String successMd5 = fileStorageService.addFile(dirPackage, 1,
buildInfoModel.getWorkspaceId(),
"构建来源," + buildInfoModel.getName(),
// 默认的别名码为构建id
StrUtil.emptyToDefault(buildInfoModel.getAliasCode(), buildInfoModel.getId()),
fileStorageKeepDay);
if (successMd5 != null) {
logRecorder.system("构建产物文件成功同步到文件管理中心,{}", successMd5);
} else {
logRecorder.systemWarning("构建产物文件同步到文件管理中心失败,当前文件已经存文件管理中心存在啦");
}
}
//
int releaseMethod = this.buildExtraModule.getReleaseMethod();
logRecorder.system("发布的方式:{}", BaseEnum.getDescByCode(BuildReleaseMethod.class, releaseMethod));
if (releaseMethod == BuildReleaseMethod.Outgiving.getCode()) {
//
this.doOutGiving();
} else if (releaseMethod == BuildReleaseMethod.Project.getCode()) {
this.doProject();
} else if (releaseMethod == BuildReleaseMethod.Ssh.getCode()) {
this.doSsh();
} else if (releaseMethod == BuildReleaseMethod.LocalCommand.getCode()) {
return this.localCommand();
} else if (releaseMethod == BuildReleaseMethod.DockerImage.getCode()) {
return this.doDockerImage();
} else if (releaseMethod == BuildReleaseMethod.No.getCode()) {
return null;
} else {
String format = StrUtil.format("没有实现的发布分发:{}", releaseMethod);
logRecorder.systemError(format);
return format;
}
return null;
}
/**
* 版本号递增
*
* @param dockerTagIncrement 是否开启版本号递增
* @param dockerTag 当前版本号
* @return 递增后到版本号
*/
private String dockerTagIncrement(Boolean dockerTagIncrement, String dockerTag) {
if (dockerTagIncrement == null || !dockerTagIncrement) {
return dockerTag;
}
List list = StrUtil.splitTrim(dockerTag, StrUtil.COMMA);
return list.stream()
.map(s -> {
List tag = StrUtil.splitTrim(s, StrUtil.COLON);
String version = CollUtil.getLast(tag);
List versionList = StrUtil.splitTrim(version, StrUtil.DOT);
int tagSize = CollUtil.size(tag);
if (tagSize <= 1 || CollUtil.size(versionList) <= 1) {
logRecorder.systemWarning("version number incrementing error, no match for . or :");
return s;
}
boolean match = false;
for (int i = versionList.size() - 1; i >= 0; i--) {
String versionParting = versionList.get(i);
int versionPartingInt = Convert.toInt(versionParting, Integer.MIN_VALUE);
if (versionPartingInt != Integer.MIN_VALUE) {
versionList.set(i, this.buildNumberId + StrUtil.EMPTY);
match = true;
break;
}
}
tag.set(tagSize - 1, CollUtil.join(versionList, StrUtil.DOT));
String newVersion = CollUtil.join(tag, StrUtil.COLON);
if (match) {
logRecorder.system("dockerTag version number incrementing {} -> {}", s, newVersion);
} else {
logRecorder.systemWarning("version number incrementing error,No numeric version number {} ", s);
}
return newVersion;
})
.collect(Collectors.joining(StrUtil.COMMA));
}
private String doDockerImage() {
// 生成临时目录
File tempPath = FileUtil.file(JpomApplication.getInstance().getTempPath(), "build_temp", "docker_image", this.buildExtraModule.getId() + StrUtil.DASHED + this.buildNumberId);
try {
File sourceFile = BuildUtil.getSourceById(this.buildExtraModule.getId());
FileUtil.copyContent(sourceFile, tempPath, true);
// 将产物文件 copy 到本地仓库目录
File historyPackageFile = BuildUtil.getHistoryPackageFile(buildExtraModule.getId(), this.getRealBuildNumberId(), StrUtil.SLASH);
FileUtil.copyContent(historyPackageFile, tempPath, true);
// env file
Map envMap = buildEnv.environment();
//File envFile = FileUtil.file(tempPath, ".env");
String dockerTag = StringUtil.formatStrByMap(this.buildExtraModule.getDockerTag(), envMap);
//
dockerTag = this.dockerTagIncrement(this.buildExtraModule.getDockerTagIncrement(), dockerTag);
// docker file
String moduleDockerfile = this.buildExtraModule.getDockerfile();
List list = StrUtil.splitTrim(moduleDockerfile, StrUtil.COLON);
String dockerFile = CollUtil.getLast(list);
File dockerfile = FileUtil.file(tempPath, dockerFile);
if (!FileUtil.isFile(dockerfile)) {
String format = StrUtil.format("仓库目录下没有找到 Dockerfile 文件: {}", dockerFile);
logRecorder.systemError(format);
return format;
}
File baseDir = FileUtil.file(tempPath, list.size() == 1 ? StrUtil.SLASH : CollUtil.get(list, 0));
//
String fromTag = this.buildExtraModule.getFromTag();
// 根据 tag 查询
List dockerInfoModels = dockerInfoService
.queryByTag(this.buildExtraModule.getWorkspaceId(), fromTag);
Map map = machineDockerServer.dockerParameter(dockerInfoModels);
if (map == null) {
String format = StrUtil.format("{} 没有可用的 docker server", fromTag);
logRecorder.systemError(format);
return format;
}
//String dockerBuildArgs = this.buildExtraModule.getDockerBuildArgs();
for (DockerInfoModel infoModel : dockerInfoModels) {
boolean done = this.doDockerImage(infoModel, envMap, dockerfile, baseDir, dockerTag, this.buildExtraModule);
if (!done) {
logRecorder.systemWarning("容器构建异常:{} -> {}", infoModel.getName(), dockerTag);
if (buildExtraModule.strictlyEnforce()) {
return "严格模式下镜像构建失败,终止任务";
}
}
}
// 推送 - 只选择一个 docker 服务来推送到远程仓库
Boolean pushToRepository = this.buildExtraModule.getPushToRepository();
if (pushToRepository != null && pushToRepository) {
List repositoryList = StrUtil.splitTrim(dockerTag, StrUtil.COMMA);
for (String repositoryItem : repositoryList) {
logRecorder.system("start push to repository in({}),{} {}{}", map.get("name"), StrUtil.emptyToDefault((String) map.get("registryUrl"), StrUtil.EMPTY), repositoryItem, System.lineSeparator());
//
map.put("repository", repositoryItem);
Consumer logConsumer = s -> logRecorder.info(s);
map.put("logConsumer", logConsumer);
IPlugin plugin = PluginFactory.getPlugin(DockerInfoService.DOCKER_PLUGIN_NAME);
try {
plugin.execute("pushImage", map);
} catch (Exception e) {
logRecorder.error("推送镜像调用容器异常", e);
}
}
}
// 发布 docker 服务
this.updateSwarmService(dockerTag, this.buildExtraModule.getDockerSwarmId(), this.buildExtraModule.getDockerSwarmServiceName());
} finally {
CommandUtil.systemFastDel(tempPath);
}
return null;
}
private void updateSwarmService(String dockerTag, String swarmId, String serviceName) {
if (StrUtil.isEmpty(swarmId)) {
return;
}
List splitTrim = StrUtil.splitTrim(dockerTag, StrUtil.COMMA);
String first = CollUtil.getFirst(splitTrim);
logRecorder.system("start update swarm service: {} use image {}", serviceName, first);
Map pluginMap = machineDockerServer.dockerParameter(swarmId);
pluginMap.put("serviceId", serviceName);
pluginMap.put("image", first);
try {
IPlugin plugin = PluginFactory.getPlugin(DockerSwarmInfoService.DOCKER_PLUGIN_NAME);
plugin.execute("updateServiceImage", pluginMap);
} catch (Exception e) {
logRecorder.error("更新容器服务调用容器异常", e);
throw Lombok.sneakyThrow(e);
}
}
private boolean doDockerImage(DockerInfoModel dockerInfoModel, Map envMap, File dockerfile, File baseDir, String dockerTag, BuildExtraModule extraModule) {
logRecorder.system("{} start build image {}{}", dockerInfoModel.getName(), dockerTag, System.lineSeparator());
Map map = machineDockerServer.dockerParameter(dockerInfoModel);
//.toParameter();
map.put("Dockerfile", dockerfile);
map.put("baseDirectory", baseDir);
//
map.put("tags", dockerTag);
map.put("buildArgs", extraModule.getDockerBuildArgs());
map.put("pull", extraModule.getDockerBuildPull());
map.put("noCache", extraModule.getDockerNoCache());
map.put("labels", extraModule.getDockerImagesLabels());
map.put("env", envMap);
Consumer logConsumer = s -> logRecorder.append(s);
map.put("logConsumer", logConsumer);
IPlugin plugin = PluginFactory.getPlugin(DockerInfoService.DOCKER_PLUGIN_NAME);
try {
return (boolean) plugin.execute("buildImage", map);
} catch (Exception e) {
logRecorder.systemError("构建镜像调用容器异常", e);
return false;
}
}
/**
* 本地命令执行
*/
private String localCommand() {
// 执行命令
String releaseCommand = this.buildExtraModule.getReleaseCommand();
if (StrUtil.isEmpty(releaseCommand)) {
logRecorder.systemError("没有需要执行的命令");
return null;
}
logRecorder.system("{} start exec{}", DateUtil.now(), System.lineSeparator());
File sourceFile = BuildUtil.getSourceById(this.buildExtraModule.getId());
Map envFileMap = buildEnv.environment();
InputStream templateInputStream = ExtConfigBean.getConfigResourceInputStream("/exec/template." + CommandUtil.SUFFIX);
String s1 = IoUtil.readUtf8(templateInputStream);
int waitFor = JpomApplication.getInstance()
.execScript(s1 + releaseCommand, file -> {
try {
return CommandUtil.execWaitFor(file, sourceFile, envFileMap, StrUtil.EMPTY, (s, process) -> logRecorder.info(s));
} catch (IOException | InterruptedException e) {
throw Lombok.sneakyThrow(e);
}
});
logRecorder.system("执行发布脚本的退出码是:{}", waitFor);
// 判断是否为严格执行
if (buildExtraModule.strictlyEnforce()) {
return waitFor == 0 ? null : StrUtil.format("执行发布命令退出码非0,{}", waitFor);
}
return null;
}
/**
* ssh 发布
*/
private void doSsh() throws IOException {
String releaseMethodDataId = this.buildExtraModule.getReleaseMethodDataId();
SshService sshService = SpringUtil.getBean(SshService.class);
List strings = StrUtil.splitTrim(releaseMethodDataId, StrUtil.COMMA);
for (String releaseMethodDataIdItem : strings) {
SshModel item = sshService.getByKey(releaseMethodDataIdItem, false);
if (item == null) {
logRecorder.systemError("没有找到对应的ssh项:{}", releaseMethodDataIdItem);
continue;
}
this.doSsh(item, sshService);
}
}
private void doSsh(SshModel item, SshService sshService) throws IOException {
Map envFileMap = buildEnv.environment();
MachineSshModel machineSshModel = sshService.getMachineSshModel(item);
Session session = null;
ChannelSftp channelSftp = null;
try {
session = sshService.getSessionByModel(machineSshModel);
Charset charset = machineSshModel.charset();
int timeout = machineSshModel.timeout();
String releasePath = this.buildExtraModule.getReleasePath();
envFileMap.put("SSH_RELEASE_PATH", releasePath);
// 执行发布前命令
if (StrUtil.isNotEmpty(this.buildExtraModule.getReleaseBeforeCommand())) {
//
logRecorder.system("开始执行 {} 发布前命令", item.getName());
JschUtils.execCallbackLine(session, charset, timeout, this.buildExtraModule.getReleaseBeforeCommand(), StrUtil.EMPTY, envFileMap, logRecorder::info);
}
if (StrUtil.isEmpty(releasePath)) {
logRecorder.systemWarning("发布目录为空");
} else {
logRecorder.system("{} {} start ftp upload{}", DateUtil.now(), item.getName(), System.lineSeparator());
MySftp.ProgressMonitor sftpProgressMonitor = sshService.createProgressMonitor(logRecorder);
MySftp sftp = new MySftp(session, charset, timeout, sftpProgressMonitor);
channelSftp = sftp.getClient();
String prefix = "";
if (!StrUtil.startWith(releasePath, StrUtil.SLASH)) {
prefix = sftp.pwd();
}
String normalizePath = FileUtil.normalize(prefix + StrUtil.SLASH + releasePath);
if (this.buildExtraModule.isClearOld()) {
try {
if (sftp.exist(normalizePath)) {
sftp.delDir(normalizePath);
}
} catch (Exception e) {
if (!StrUtil.startWithIgnoreCase(e.getMessage(), "No such file")) {
logRecorder.error("清除构建产物失败", e);
}
}
}
sftp.syncUpload(this.resultFile, normalizePath);
logRecorder.system("{} ftp upload done", item.getName());
}
// 执行发布后命令
if (StrUtil.isEmpty(this.buildExtraModule.getReleaseCommand())) {
logRecorder.systemWarning("没有需要执行的ssh命令");
return;
}
//
logRecorder.system("开始执行 {} 发布后命令", item.getName());
JschUtils.execCallbackLine(session, charset, timeout, this.buildExtraModule.getReleaseCommand(), StrUtil.EMPTY, envFileMap, logRecorder::info);
} finally {
JschUtil.close(channelSftp);
JschUtil.close(session);
}
}
/**
* 差异上传发布
*
* @param nodeModel 节点
* @param projectId 项目ID
* @param afterOpt 发布后的操作
*/
private void diffSyncProject(NodeModel nodeModel, String projectId, AfterOpt afterOpt, boolean clearOld) {
File resultFile = this.resultFile;
String resultFileParent = resultFile.isFile() ?
FileUtil.getAbsolutePath(resultFile.getParent()) : FileUtil.getAbsolutePath(this.resultFile);
//
List files = FileUtil.loopFiles(resultFile);
List collect = files.stream().map(file -> {
//
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", StringUtil.delStartPath(file, resultFileParent, true));
jsonObject.put("sha1", SecureUtil.sha1(file));
return jsonObject;
}).collect(Collectors.toList());
//
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", projectId);
jsonObject.put("data", collect);
String directory = this.buildExtraModule.getProjectSecondaryDirectory();
directory = Opt.ofBlankAble(directory).orElse(StrUtil.SLASH);
jsonObject.put("dir", directory);
JsonMessage requestBody = NodeForward.requestBody(nodeModel, NodeUrl.MANAGE_FILE_DIFF_FILE, jsonObject);
Assert.state(requestBody.success(), "对比项目文件失败:" + requestBody);
JSONObject data = requestBody.getData();
JSONArray diff = data.getJSONArray("diff");
JSONArray del = data.getJSONArray("del");
int delSize = CollUtil.size(del);
int diffSize = CollUtil.size(diff);
if (clearOld) {
logRecorder.system("对比文件结果,产物文件 {} 个、需要上传 {} 个、需要删除 {} 个", CollUtil.size(collect), CollUtil.size(diff), delSize);
} else {
logRecorder.system("对比文件结果,产物文件 {} 个、需要上传 {} 个", CollUtil.size(collect), CollUtil.size(diff));
}
// 清空发布才先执行删除
if (delSize > 0 && clearOld) {
jsonObject.put("data", del);
requestBody = NodeForward.requestBody(nodeModel, NodeUrl.MANAGE_FILE_BATCH_DELETE, jsonObject);
Assert.state(requestBody.success(), "删除项目文件失败:" + requestBody);
}
for (int i = 0; i < diffSize; i++) {
boolean last = (i == diffSize - 1);
JSONObject diffData = (JSONObject) diff.get(i);
String name = diffData.getString("name");
File file = FileUtil.file(resultFileParent, name);
//
String startPath = StringUtil.delStartPath(file, resultFileParent, false);
startPath = FileUtil.normalize(startPath + StrUtil.SLASH + directory);
//
Set progressRangeList = ConcurrentHashMap.newKeySet((int) Math.floor((float) 100 / buildExtConfig.getLogReduceProgressRatio()));
int finalI = i;
JsonMessage jsonMessage = OutGivingRun.fileUpload(file, startPath,
projectId, false, last ? afterOpt : AfterOpt.No, nodeModel, false,
this.buildExtraModule.getProjectUploadCloseFirst(), (total, progressSize) -> {
double progressPercentage = Math.floor(((float) progressSize / total) * 100);
int progressRange = (int) Math.floor(progressPercentage / buildExtConfig.getLogReduceProgressRatio());
if (progressRangeList.add(progressRange)) {
// total, progressSize
logRecorder.system("上传文件进度:{}[{}/{}] {}/{} {} ", file.getName(),
(finalI + 1), diffSize,
FileUtil.readableFileSize(progressSize), FileUtil.readableFileSize(total),
NumberUtil.formatPercent(((float) progressSize / total), 0)
);
}
});
Assert.state(jsonMessage.success(), "同步项目文件失败:" + jsonMessage);
if (last) {
// 最后一个
logRecorder.system("发布项目包成功:{}", jsonMessage);
}
}
}
/**
* 发布项目
*/
private void doProject() {
//AfterOpt afterOpt, boolean clearOld, boolean diffSync
AfterOpt afterOpt = BaseEnum.getEnum(AfterOpt.class, this.buildExtraModule.getAfterOpt(), AfterOpt.No);
boolean clearOld = this.buildExtraModule.isClearOld();
boolean diffSync = this.buildExtraModule.isDiffSync();
String releaseMethodDataId = this.buildExtraModule.getReleaseMethodDataId();
String[] strings = StrUtil.splitToArray(releaseMethodDataId, CharPool.COLON);
if (ArrayUtil.length(strings) != 2) {
throw new IllegalArgumentException(releaseMethodDataId + " error");
}
NodeService nodeService = SpringUtil.getBean(NodeService.class);
NodeModel nodeModel = nodeService.getByKey(strings[0]);
Objects.requireNonNull(nodeModel, "节点不存在");
String projectId = strings[1];
if (diffSync) {
this.diffSyncProject(nodeModel, projectId, afterOpt, clearOld);
return;
}
boolean tarGz = this.buildEnv.getBool(BuildUtil.USE_TAR_GZ, false);
JsonMessage jsonMessage = BuildUtil.loadDirPackage(this.buildExtraModule.getId(), this.getRealBuildNumberId(), this.resultFile, tarGz, (unZip, zipFile) -> {
String name = zipFile.getName();
Set progressRangeList = ConcurrentHashMap.newKeySet((int) Math.floor((float) 100 / buildExtConfig.getLogReduceProgressRatio()));
return OutGivingRun.fileUpload(zipFile,
this.buildExtraModule.getProjectSecondaryDirectory(),
projectId,
unZip,
afterOpt,
nodeModel, clearOld, this.buildExtraModule.getProjectUploadCloseFirst(), (total, progressSize) -> {
double progressPercentage = Math.floor(((float) progressSize / total) * 100);
int progressRange = (int) Math.floor(progressPercentage / buildExtConfig.getLogReduceProgressRatio());
if (progressRangeList.add(progressRange)) {
logRecorder.system("上传文件进度:{} {}/{} {}", name,
FileUtil.readableFileSize(progressSize), FileUtil.readableFileSize(total),
NumberUtil.formatPercent(((float) progressSize / total), 0));
}
});
});
if (jsonMessage.success()) {
logRecorder.system("发布项目包成功:{}", jsonMessage);
} else {
throw new JpomRuntimeException("发布项目包失败:" + jsonMessage);
}
}
/**
* 分发包
*/
private void doOutGiving() throws ExecutionException, InterruptedException {
String releaseMethodDataId = this.buildExtraModule.getReleaseMethodDataId();
String projectSecondaryDirectory = this.buildExtraModule.getProjectSecondaryDirectory();
//
String selectProject = buildEnv.get("dispatchSelectProject");
boolean tarGz = buildEnv.getBool(BuildUtil.USE_TAR_GZ, false);
Future statusFuture = BuildUtil.loadDirPackage(this.buildExtraModule.getId(), this.getRealBuildNumberId(), this.resultFile, tarGz, (unZip, zipFile) -> {
OutGivingRun.OutGivingRunBuilder outGivingRunBuilder = OutGivingRun.builder()
.id(releaseMethodDataId)
.file(zipFile)
.logRecorder(logRecorder)
.userModel(userModel)
.unzip(unZip)
// 由构建配置决定是否删除
.doneDeleteFile(false)
.projectSecondaryDirectory(projectSecondaryDirectory)
.stripComponents(0);
return outGivingRunBuilder.build().startRun(selectProject);
});
//OutGivingRun.startRun(releaseMethodDataId, zipFile, userModel, unZip, 0);
logRecorder.system("开始执行分发包啦,请到分发中查看详情状态");
OutGivingModel.Status status = statusFuture.get();
logRecorder.system("分发结果:{}", status.getDesc());
}
/**
* 回滚
*
* @param item 构建对象
*/
public void rollback(BuildInfoModel item) {
try {
BaseServerController.resetInfo(userModel);
this.init();
//
buildEnv.eachStr(s -> logRecorder.system(s));
logRecorder.system("开始回滚:{}", DateTime.now());
//
String errorMsg = this.start(null, item);
logRecorder.system("执行回滚结束:{}", StrUtil.emptyToDefault(errorMsg, "ok"));
if (errorMsg == null) {
this.updateStatus(BuildStatus.PubSuccess, "发布成功");
} else {
this.updateStatus(BuildStatus.PubError, errorMsg);
}
} catch (Exception e) {
log.error("执行发布异常", e);
logRecorder.error("执行发布异常", e);
this.updateStatus(BuildStatus.PubError, e.getMessage());
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy