feat(fix) 在线升级执行脚本、脚本模版等兼容 debian

This commit is contained in:
bwcx_jzy 2022-02-23 10:31:31 +08:00
parent 37731c12c1
commit b2cba06872
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
10 changed files with 925 additions and 927 deletions

View File

@ -14,6 +14,7 @@
3. 【server】在线构建 ssh 发布选择授权目录切换不生效问题(感谢@天天)
4. 【server】在线构建本地构建命令不能换行问题感谢@华仔)
5. 【server】日志弹窗新增行号
6. 【server】在线升级执行脚本、脚本模版等兼容 `debian` (感谢@wxyShine [Gitee issues I4UQBD](https://gitee.com/dromara/Jpom/issues/I4UQBD)
------

View File

@ -29,7 +29,6 @@ import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.LineHandler;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.system.SystemUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import cn.jiangzeyin.common.spring.SpringUtil;
import io.jpom.model.data.DslYmlDto;
@ -75,9 +74,7 @@ public class DslScriptBuilder extends BaseRunScript implements Runnable {
ProcessBuilder processBuilder = new ProcessBuilder();
List<String> command = StrUtil.splitTrim(args, StrUtil.SPACE);
command.add(0, script);
if (SystemUtil.getOsInfo().isLinux() || SystemUtil.getOsInfo().isMac()) {
command.add(0, CommandUtil.SUFFIX);
}
command.add(0, CommandUtil.EXECUTE_PREFIX);
DefaultSystemLog.getLog().debug(CollUtil.join(command, StrUtil.SPACE));
if (environment != null) {
processBuilder.environment().putAll(environment);

View File

@ -30,7 +30,6 @@ import cn.hutool.core.io.LineHandler;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.system.SystemUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import cn.jiangzeyin.common.JsonMessage;
import com.alibaba.fastjson.JSONObject;
@ -53,171 +52,169 @@ import java.util.concurrent.ConcurrentHashMap;
* @date 2019/4/25
*/
public class ScriptProcessBuilder extends BaseRunScript implements Runnable {
/**
* 执行中的缓存
*/
private static final ConcurrentHashMap<String, ScriptProcessBuilder> FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP = new ConcurrentHashMap<>();
/**
* 执行中的缓存
*/
private static final ConcurrentHashMap<String, ScriptProcessBuilder> FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP = new ConcurrentHashMap<>();
private final ProcessBuilder processBuilder;
private final Set<Session> sessions = new HashSet<>();
private final String executeId;
private final ProcessBuilder processBuilder;
private final Set<Session> sessions = new HashSet<>();
private final String executeId;
private ScriptProcessBuilder(NodeScriptModel nodeScriptModel, String executeId, String args) {
super(nodeScriptModel.logFile(executeId));
this.executeId = executeId;
private ScriptProcessBuilder(NodeScriptModel nodeScriptModel, String executeId, String args) {
super(nodeScriptModel.logFile(executeId));
this.executeId = executeId;
File scriptFile = nodeScriptModel.getFile(true);
//
String script = FileUtil.getAbsolutePath(scriptFile);
processBuilder = new ProcessBuilder();
List<String> command = StrUtil.splitTrim(args, StrUtil.SPACE);
command.add(0, script);
if (SystemUtil.getOsInfo().isLinux() || SystemUtil.getOsInfo().isMac()) {
command.add(0, CommandUtil.SUFFIX);
}
DefaultSystemLog.getLog().debug(CollUtil.join(command, StrUtil.SPACE));
processBuilder.redirectErrorStream(true);
processBuilder.command(command);
processBuilder.directory(scriptFile.getParentFile());
}
File scriptFile = nodeScriptModel.getFile(true);
//
String script = FileUtil.getAbsolutePath(scriptFile);
processBuilder = new ProcessBuilder();
List<String> command = StrUtil.splitTrim(args, StrUtil.SPACE);
command.add(0, script);
command.add(0, CommandUtil.EXECUTE_PREFIX);
DefaultSystemLog.getLog().debug(CollUtil.join(command, StrUtil.SPACE));
processBuilder.redirectErrorStream(true);
processBuilder.command(command);
processBuilder.directory(scriptFile.getParentFile());
}
/**
* 创建执行 并监听
*
* @param nodeScriptModel 脚本模版
* @param executeId 执行ID
* @param args 参数
*/
public static ScriptProcessBuilder create(NodeScriptModel nodeScriptModel, String executeId, String args) {
return FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.computeIfAbsent(executeId, file1 -> {
ScriptProcessBuilder scriptProcessBuilder1 = new ScriptProcessBuilder(nodeScriptModel, executeId, args);
ThreadUtil.execute(scriptProcessBuilder1);
return scriptProcessBuilder1;
});
}
/**
* 创建执行 并监听
*
* @param nodeScriptModel 脚本模版
* @param executeId 执行ID
* @param args 参数
*/
public static ScriptProcessBuilder create(NodeScriptModel nodeScriptModel, String executeId, String args) {
return FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.computeIfAbsent(executeId, file1 -> {
ScriptProcessBuilder scriptProcessBuilder1 = new ScriptProcessBuilder(nodeScriptModel, executeId, args);
ThreadUtil.execute(scriptProcessBuilder1);
return scriptProcessBuilder1;
});
}
/**
* 创建执行 并监听
*
* @param nodeScriptModel 脚本模版
* @param executeId 执行ID
* @param args 参数
* @param session 会话
*/
public static void addWatcher(NodeScriptModel nodeScriptModel, String executeId, String args, Session session) {
ScriptProcessBuilder scriptProcessBuilder = create(nodeScriptModel, executeId, args);
//
if (scriptProcessBuilder.sessions.add(session)) {
if (FileUtil.exist(scriptProcessBuilder.logFile)) {
// 读取之前的信息并发送
FileUtil.readLines(scriptProcessBuilder.logFile, CharsetUtil.CHARSET_UTF_8, (LineHandler) line -> {
try {
SocketSessionUtil.send(session, line);
} catch (IOException e) {
DefaultSystemLog.getLog().error("发送消息失败", e);
}
});
}
}
}
/**
* 创建执行 并监听
*
* @param nodeScriptModel 脚本模版
* @param executeId 执行ID
* @param args 参数
* @param session 会话
*/
public static void addWatcher(NodeScriptModel nodeScriptModel, String executeId, String args, Session session) {
ScriptProcessBuilder scriptProcessBuilder = create(nodeScriptModel, executeId, args);
//
if (scriptProcessBuilder.sessions.add(session)) {
if (FileUtil.exist(scriptProcessBuilder.logFile)) {
// 读取之前的信息并发送
FileUtil.readLines(scriptProcessBuilder.logFile, CharsetUtil.CHARSET_UTF_8, (LineHandler) line -> {
try {
SocketSessionUtil.send(session, line);
} catch (IOException e) {
DefaultSystemLog.getLog().error("发送消息失败", e);
}
});
}
}
}
/**
* 判断是否还在执行中
*
* @param executeId 执行id
* @return true 还在执行
*/
public static boolean isRun(String executeId) {
return FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.containsKey(executeId);
}
/**
* 判断是否还在执行中
*
* @param executeId 执行id
* @return true 还在执行
*/
public static boolean isRun(String executeId) {
return FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.containsKey(executeId);
}
/**
* 关闭会话
*
* @param session 会话
*/
public static void stopWatcher(Session session) {
Collection<ScriptProcessBuilder> scriptProcessBuilders = FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.values();
for (ScriptProcessBuilder scriptProcessBuilder : scriptProcessBuilders) {
Set<Session> sessions = scriptProcessBuilder.sessions;
sessions.removeIf(session1 -> session1.getId().equals(session.getId()));
}
}
/**
* 关闭会话
*
* @param session 会话
*/
public static void stopWatcher(Session session) {
Collection<ScriptProcessBuilder> scriptProcessBuilders = FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.values();
for (ScriptProcessBuilder scriptProcessBuilder : scriptProcessBuilders) {
Set<Session> sessions = scriptProcessBuilder.sessions;
sessions.removeIf(session1 -> session1.getId().equals(session.getId()));
}
}
/**
* 停止脚本命令
*
* @param executeId 执行ID
*/
public static void stopRun(String executeId) {
ScriptProcessBuilder scriptProcessBuilder = FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.get(executeId);
if (scriptProcessBuilder != null) {
scriptProcessBuilder.end("停止运行");
}
}
/**
* 停止脚本命令
*
* @param executeId 执行ID
*/
public static void stopRun(String executeId) {
ScriptProcessBuilder scriptProcessBuilder = FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.get(executeId);
if (scriptProcessBuilder != null) {
scriptProcessBuilder.end("停止运行");
}
}
@Override
public void run() {
//初始化ProcessBuilder对象
try {
this.handle("start execute:" + DateUtil.now());
process = processBuilder.start();
{
inputStream = process.getInputStream();
IoUtil.readLines(inputStream, ExtConfigBean.getInstance().getConsoleLogCharset(), (LineHandler) ScriptProcessBuilder.this::handle);
}
int waitFor = process.waitFor();
JsonMessage<String> jsonMessage = new JsonMessage<>(200, "执行完毕:" + waitFor);
JSONObject jsonObject = jsonMessage.toJson();
jsonObject.put("op", ConsoleCommandOp.stop.name());
this.end(jsonObject.toString());
this.handle("execute done:" + waitFor + " time:" + DateUtil.now());
} catch (Exception e) {
DefaultSystemLog.getLog().error("执行异常", e);
this.end("执行异常:" + e.getMessage());
} finally {
this.close();
}
}
@Override
public void run() {
//初始化ProcessBuilder对象
try {
this.handle("start execute:" + DateUtil.now());
process = processBuilder.start();
{
inputStream = process.getInputStream();
IoUtil.readLines(inputStream, ExtConfigBean.getInstance().getConsoleLogCharset(), (LineHandler) ScriptProcessBuilder.this::handle);
}
int waitFor = process.waitFor();
JsonMessage<String> jsonMessage = new JsonMessage<>(200, "执行完毕:" + waitFor);
JSONObject jsonObject = jsonMessage.toJson();
jsonObject.put("op", ConsoleCommandOp.stop.name());
this.end(jsonObject.toString());
this.handle("execute done:" + waitFor + " time:" + DateUtil.now());
} catch (Exception e) {
DefaultSystemLog.getLog().error("执行异常", e);
this.end("执行异常:" + e.getMessage());
} finally {
this.close();
}
}
/**
* 结束执行
*
* @param msg 响应的消息
*/
@Override
protected void end(String msg) {
Iterator<Session> iterator = sessions.iterator();
while (iterator.hasNext()) {
Session session = iterator.next();
try {
SocketSessionUtil.send(session, msg);
} catch (IOException e) {
DefaultSystemLog.getLog().error("发送消息失败", e);
}
iterator.remove();
}
FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.remove(this.executeId);
}
/**
* 结束执行
*
* @param msg 响应的消息
*/
@Override
protected void end(String msg) {
Iterator<Session> iterator = sessions.iterator();
while (iterator.hasNext()) {
Session session = iterator.next();
try {
SocketSessionUtil.send(session, msg);
} catch (IOException e) {
DefaultSystemLog.getLog().error("发送消息失败", e);
}
iterator.remove();
}
FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.remove(this.executeId);
}
/**
* 响应
*
* @param line 信息
*/
@Override
protected void handle(String line) {
super.handle(line);
//
Iterator<Session> iterator = sessions.iterator();
while (iterator.hasNext()) {
Session session = iterator.next();
try {
SocketSessionUtil.send(session, line);
} catch (IOException e) {
DefaultSystemLog.getLog().error("发送消息失败", e);
iterator.remove();
}
}
}
/**
* 响应
*
* @param line 信息
*/
@Override
protected void handle(String line) {
super.handle(line);
//
Iterator<Session> iterator = sessions.iterator();
while (iterator.hasNext()) {
Session session = iterator.next();
try {
SocketSessionUtil.send(session, line);
} catch (IOException e) {
DefaultSystemLog.getLog().error("发送消息失败", e);
iterator.remove();
}
}
}
}

View File

@ -196,9 +196,7 @@ public class JpomApplication extends ApplicationBuilder {
// Waiting for method caller,For example, the interface response
ThreadUtil.sleep(2, TimeUnit.SECONDS);
try {
String command = SystemUtil.getOsInfo().isWindows() ? StrUtil.EMPTY : CommandUtil.SUFFIX;
command += " " + FileUtil.getAbsolutePath(scriptFile) + " restart upgrade";
String command = CommandUtil.EXECUTE_PREFIX + StrUtil.SPACE + FileUtil.getAbsolutePath(scriptFile) + " restart upgrade";
if (SystemUtil.getOsInfo().isWindows()) {
CommandUtil.execSystemCommand(command, scriptFile.getParentFile());
} else {

View File

@ -23,7 +23,10 @@
package io.jpom.util;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.*;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.system.SystemUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import io.jpom.system.ExtConfigBean;
@ -42,189 +45,193 @@ import java.util.List;
* @date 2019/4/15
*/
public class CommandUtil {
/**
* 系统命令
*/
private static final List<String> COMMAND = new ArrayList<>();
/**
* 文件后缀
*/
public static final String SUFFIX;
/**
* 系统命令
*/
private static final List<String> COMMAND = new ArrayList<>();
/**
* 文件后缀
*/
public static final String SUFFIX;
/**
* 执行前缀
*/
public static final String EXECUTE_PREFIX;
static {
if (SystemUtil.getOsInfo().isLinux()) {
//执行linux系统命令
COMMAND.add("/bin/bash");
COMMAND.add("-c");
} else if (SystemUtil.getOsInfo().isMac()) {
COMMAND.add("/bin/bash");
COMMAND.add("-c");
} else {
COMMAND.add("cmd");
COMMAND.add("/c");
}
/**
* 文件后缀
*/
if (SystemUtil.getOsInfo().isWindows()) {
SUFFIX = "bat";
} else {
SUFFIX = "sh";
}
}
static {
if (SystemUtil.getOsInfo().isLinux()) {
//执行linux系统命令
COMMAND.add("/bin/bash");
COMMAND.add("-c");
} else if (SystemUtil.getOsInfo().isMac()) {
COMMAND.add("/bin/bash");
COMMAND.add("-c");
} else {
COMMAND.add("cmd");
COMMAND.add("/c");
}
//
if (SystemUtil.getOsInfo().isWindows()) {
SUFFIX = "bat";
EXECUTE_PREFIX = StrUtil.EMPTY;
} else {
SUFFIX = "sh";
EXECUTE_PREFIX = "bash";
}
}
/**
* 获取执行命令的 前缀
*
* @return list
*/
public static List<String> getCommand() {
return ObjectUtil.clone(COMMAND);
}
/**
* 获取执行命令的 前缀
*
* @return list
*/
public static List<String> getCommand() {
return ObjectUtil.clone(COMMAND);
}
public static String execSystemCommand(String command) {
return execSystemCommand(command, null);
}
public static String execSystemCommand(String command) {
return execSystemCommand(command, null);
}
/**
* 在指定文件夹下执行命令
*
* @param command 命令
* @param file 文件夹
* @return msg
*/
public static String execSystemCommand(String command, File file) {
String newCommand = StrUtil.replace(command, StrUtil.CRLF, StrUtil.SPACE);
newCommand = StrUtil.replace(newCommand, StrUtil.LF, StrUtil.SPACE);
String result = "error";
try {
List<String> commands = getCommand();
commands.add(newCommand);
String[] cmd = commands.toArray(new String[]{});
result = exec(cmd, file);
} catch (Exception e) {
DefaultSystemLog.getLog().error("执行命令异常", e);
result += e.getMessage();
}
return result;
}
/**
* 在指定文件夹下执行命令
*
* @param command 命令
* @param file 文件夹
* @return msg
*/
public static String execSystemCommand(String command, File file) {
String newCommand = StrUtil.replace(command, StrUtil.CRLF, StrUtil.SPACE);
newCommand = StrUtil.replace(newCommand, StrUtil.LF, StrUtil.SPACE);
String result = "error";
try {
List<String> commands = getCommand();
commands.add(newCommand);
String[] cmd = commands.toArray(new String[]{});
result = exec(cmd, file);
} catch (Exception e) {
DefaultSystemLog.getLog().error("执行命令异常", e);
result += e.getMessage();
}
return result;
}
/**
* 执行命令
*
* @param cmd 命令行
* @return 结果
* @throws IOException IO
*/
private static String exec(String[] cmd, File file) throws IOException {
Process process = new ProcessBuilder(cmd).directory(file).redirectErrorStream(true).start();
Charset charset;
boolean log;
try {
charset = ExtConfigBean.getInstance().getConsoleLogCharset();
log = true;
} catch (Exception e) {
// 直接执行使用默认编码格式
charset = CharsetUtil.systemCharset();
// 不记录日志
log = false;
}
charset = ObjectUtil.defaultIfNull(charset, CharsetUtil.defaultCharset());
String result = RuntimeUtil.getResult(process, charset);
if (log) {
DefaultSystemLog.getLog().debug("exec {} {} {}", charset.name(), Arrays.toString(cmd), result);
}
return result;
}
/**
* 执行命令
*
* @param cmd 命令行
* @return 结果
* @throws IOException IO
*/
private static String exec(String[] cmd, File file) throws IOException {
Process process = new ProcessBuilder(cmd).directory(file).redirectErrorStream(true).start();
Charset charset;
boolean log;
try {
charset = ExtConfigBean.getInstance().getConsoleLogCharset();
log = true;
} catch (Exception e) {
// 直接执行使用默认编码格式
charset = CharsetUtil.systemCharset();
// 不记录日志
log = false;
}
charset = ObjectUtil.defaultIfNull(charset, CharsetUtil.defaultCharset());
String result = RuntimeUtil.getResult(process, charset);
if (log) {
DefaultSystemLog.getLog().debug("exec {} {} {}", charset.name(), Arrays.toString(cmd), result);
}
return result;
}
/**
* 异步执行命令
*
* @param file 文件夹
* @param command 命令
* @throws IOException 异常
*/
public static void asyncExeLocalCommand(File file, String command) throws Exception {
String newCommand = StrUtil.replace(command, StrUtil.CRLF, StrUtil.SPACE);
newCommand = StrUtil.replace(newCommand, StrUtil.LF, StrUtil.SPACE);
//
DefaultSystemLog.getLog().debug(newCommand);
List<String> commands = getCommand();
commands.add(newCommand);
ProcessBuilder pb = new ProcessBuilder(commands);
if (file != null) {
pb.directory(file);
}
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
pb.start();
}
/**
* 异步执行命令
*
* @param file 文件夹
* @param command 命令
* @throws IOException 异常
*/
public static void asyncExeLocalCommand(File file, String command) throws Exception {
String newCommand = StrUtil.replace(command, StrUtil.CRLF, StrUtil.SPACE);
newCommand = StrUtil.replace(newCommand, StrUtil.LF, StrUtil.SPACE);
//
DefaultSystemLog.getLog().debug(newCommand);
List<String> commands = getCommand();
commands.add(newCommand);
ProcessBuilder pb = new ProcessBuilder(commands);
if (file != null) {
pb.directory(file);
}
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
pb.start();
}
/**
* 判断是否包含删除命令
*
* @param script 命令行
* @return true 包含
*/
public static boolean checkContainsDel(String script) {
// 判断删除
String[] commands = StrUtil.splitToArray(script, StrUtil.LF);
for (String commandItem : commands) {
if (checkContainsDelItem(commandItem)) {
return true;
}
}
return false;
}
/**
* 判断是否包含删除命令
*
* @param script 命令行
* @return true 包含
*/
public static boolean checkContainsDel(String script) {
// 判断删除
String[] commands = StrUtil.splitToArray(script, StrUtil.LF);
for (String commandItem : commands) {
if (checkContainsDelItem(commandItem)) {
return true;
}
}
return false;
}
/**
* 执行系统命令 快速删除.
* 执行删除后再检查文件是否存在
*
* @param file 文件或者文件夹
* @return true 文件还存在
*/
public static boolean systemFastDel(File file) {
String path = FileUtil.getAbsolutePath(file);
String command;
if (SystemUtil.getOsInfo().isWindows()) {
// Windows
command = StrUtil.format("rd /s/q \"{}\"", path);
} else {
// Linux MacOS
command = StrUtil.format("rm -rf '{}'", path);
}
CommandUtil.execSystemCommand(command);
// 再次尝试
boolean del = FileUtil.del(file);
if (!del) {
FileUtil.del(file.toPath());
}
return FileUtil.exist(file);
}
/**
* 执行系统命令 快速删除.
* 执行删除后再检查文件是否存在
*
* @param file 文件或者文件夹
* @return true 文件还存在
*/
public static boolean systemFastDel(File file) {
String path = FileUtil.getAbsolutePath(file);
String command;
if (SystemUtil.getOsInfo().isWindows()) {
// Windows
command = StrUtil.format("rd /s/q \"{}\"", path);
} else {
// Linux MacOS
command = StrUtil.format("rm -rf '{}'", path);
}
CommandUtil.execSystemCommand(command);
// 再次尝试
boolean del = FileUtil.del(file);
if (!del) {
FileUtil.del(file.toPath());
}
return FileUtil.exist(file);
}
private static boolean checkContainsDelItem(String script) {
String[] split = StrUtil.splitToArray(script, StrUtil.SPACE);
if (SystemUtil.getOsInfo().isWindows()) {
for (String s : split) {
if (StrUtil.startWithAny(s, "rd", "del")) {
return true;
}
if (StrUtil.containsAnyIgnoreCase(s, " rd", " del")) {
return true;
}
}
} else {
for (String s : split) {
if (StrUtil.startWithAny(s, "rm", "\\rm")) {
return true;
}
if (StrUtil.containsAnyIgnoreCase(s, " rm", " \\rm", "&rm", "&\\rm")) {
return true;
}
}
}
return false;
}
private static boolean checkContainsDelItem(String script) {
String[] split = StrUtil.splitToArray(script, StrUtil.SPACE);
if (SystemUtil.getOsInfo().isWindows()) {
for (String s : split) {
if (StrUtil.startWithAny(s, "rd", "del")) {
return true;
}
if (StrUtil.containsAnyIgnoreCase(s, " rd", " del")) {
return true;
}
}
} else {
for (String s : split) {
if (StrUtil.startWithAny(s, "rm", "\\rm")) {
return true;
}
if (StrUtil.containsAnyIgnoreCase(s, " rm", " \\rm", "&rm", "&\\rm")) {
return true;
}
}
}
return false;
}
}

View File

@ -37,7 +37,6 @@ import cn.hutool.crypto.SecureUtil;
import cn.hutool.extra.ssh.JschUtil;
import cn.hutool.extra.ssh.Sftp;
import cn.hutool.http.HttpStatus;
import cn.hutool.system.SystemUtil;
import cn.jiangzeyin.common.JsonMessage;
import cn.jiangzeyin.common.spring.SpringUtil;
import com.alibaba.fastjson.JSONArray;
@ -86,22 +85,22 @@ import java.util.stream.Collectors;
@Builder
public class ReleaseManage implements Runnable {
private final UserModel userModel;
private final int buildId;
private final BuildExtraModule buildExtraModule;
private final String logId;
private final BuildExecuteService buildExecuteService;
private final UserModel userModel;
private final int buildId;
private final BuildExtraModule buildExtraModule;
private final String logId;
private final BuildExecuteService buildExecuteService;
private LogRecorder logRecorder;
private File resultFile;
private LogRecorder logRecorder;
private File resultFile;
private void init() {
if (this.logRecorder == null) {
File logFile = BuildUtil.getLogFile(buildExtraModule.getId(), buildId);
this.logRecorder = LogRecorder.builder().file(logFile).build();
}
this.resultFile = BuildUtil.getHistoryPackageFile(buildExtraModule.getId(), this.buildId, buildExtraModule.getResultDirFile());
}
private void init() {
if (this.logRecorder == null) {
File logFile = BuildUtil.getLogFile(buildExtraModule.getId(), buildId);
this.logRecorder = LogRecorder.builder().file(logFile).build();
}
this.resultFile = BuildUtil.getHistoryPackageFile(buildExtraModule.getId(), this.buildId, buildExtraModule.getResultDirFile());
}
// /**
// * new ReleaseManage constructor
@ -138,417 +137,417 @@ public class ReleaseManage implements Runnable {
// }
public void updateStatus(BuildStatus status) {
buildExecuteService.updateStatus(this.buildExtraModule.getId(), this.logId, status);
}
public void updateStatus(BuildStatus status) {
buildExecuteService.updateStatus(this.buildExtraModule.getId(), this.logId, status);
}
/**
* 不修改为发布中状态
*/
public void start() {
init();
updateStatus(BuildStatus.PubIng);
logRecorder.info("start release" + FileUtil.readableFileSize(FileUtil.size(this.resultFile)));
if (!this.resultFile.exists()) {
logRecorder.info("不存在构建产物");
updateStatus(BuildStatus.PubError);
return;
}
long time = SystemClock.now();
int releaseMethod = this.buildExtraModule.getReleaseMethod();
logRecorder.info("release method:" + BaseEnum.getDescByCode(BuildReleaseMethod.class, releaseMethod));
try {
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()) {
this.localCommand();
} else if (releaseMethod == BuildReleaseMethod.DockerImage.getCode()) {
this.doDockerImage();
} else {
logRecorder.info(" 没有实现的发布分发:" + releaseMethod);
}
} catch (Exception e) {
this.pubLog("发布异常", e);
return;
}
logRecorder.info("release complete : " + DateUtil.formatBetween(SystemClock.now() - time, BetweenFormatter.Level.MILLISECOND));
updateStatus(BuildStatus.PubSuccess);
}
/**
* 不修改为发布中状态
*/
public void start() {
init();
updateStatus(BuildStatus.PubIng);
logRecorder.info("start release" + FileUtil.readableFileSize(FileUtil.size(this.resultFile)));
if (!this.resultFile.exists()) {
logRecorder.info("不存在构建产物");
updateStatus(BuildStatus.PubError);
return;
}
long time = SystemClock.now();
int releaseMethod = this.buildExtraModule.getReleaseMethod();
logRecorder.info("release method:" + BaseEnum.getDescByCode(BuildReleaseMethod.class, releaseMethod));
try {
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()) {
this.localCommand();
} else if (releaseMethod == BuildReleaseMethod.DockerImage.getCode()) {
this.doDockerImage();
} else {
logRecorder.info(" 没有实现的发布分发:" + releaseMethod);
}
} catch (Exception e) {
this.pubLog("发布异常", e);
return;
}
logRecorder.info("release complete : " + DateUtil.formatBetween(SystemClock.now() - time, BetweenFormatter.Level.MILLISECOND));
updateStatus(BuildStatus.PubSuccess);
}
/**
* 格式化命令模版
*
* @param commands 命令
*/
private void formatCommand(String[] commands) {
for (int i = 0; i < commands.length; i++) {
commands[i] = this.formatCommandItem(commands[i]);
}
//
WorkspaceEnvVarService workspaceEnvVarService = SpringUtil.getBean(WorkspaceEnvVarService.class);
workspaceEnvVarService.formatCommand(this.buildExtraModule.getWorkspaceId(), commands);
/**
* 格式化命令模版
*
* @param commands 命令
*/
private void formatCommand(String[] commands) {
for (int i = 0; i < commands.length; i++) {
commands[i] = this.formatCommandItem(commands[i]);
}
//
WorkspaceEnvVarService workspaceEnvVarService = SpringUtil.getBean(WorkspaceEnvVarService.class);
workspaceEnvVarService.formatCommand(this.buildExtraModule.getWorkspaceId(), commands);
}
}
/**
* 格式化命令模版
*
* @param command 命令
* @return 格式化后
*/
private String formatCommandItem(String command) {
String replace = StrUtil.replace(command, "#{BUILD_ID}", this.buildExtraModule.getId());
replace = StrUtil.replace(replace, "#{BUILD_NAME}", this.buildExtraModule.getName());
replace = StrUtil.replace(replace, "#{BUILD_RESULT_FILE}", FileUtil.getAbsolutePath(this.resultFile));
replace = StrUtil.replace(replace, "#{BUILD_NUMBER_ID}", this.buildId + StrUtil.EMPTY);
return replace;
}
/**
* 格式化命令模版
*
* @param command 命令
* @return 格式化后
*/
private String formatCommandItem(String command) {
String replace = StrUtil.replace(command, "#{BUILD_ID}", this.buildExtraModule.getId());
replace = StrUtil.replace(replace, "#{BUILD_NAME}", this.buildExtraModule.getName());
replace = StrUtil.replace(replace, "#{BUILD_RESULT_FILE}", FileUtil.getAbsolutePath(this.resultFile));
replace = StrUtil.replace(replace, "#{BUILD_NUMBER_ID}", this.buildId + StrUtil.EMPTY);
return replace;
}
private String parseDockerTag(File envFile, String tag) {
if (!FileUtil.isFile(envFile)) {
return tag;
}
final String[] newTag = {tag};
FileUtil.readLines(envFile, StandardCharsets.UTF_8, (LineHandler) line -> {
line = StrUtil.trim(line);
if (StrUtil.startWith(line, "#")) {
return;
}
List<String> list = StrUtil.splitTrim(line, "=");
if (CollUtil.size(list) != 2) {
return;
}
newTag[0] = StrUtil.replace(newTag[0], "${" + list.get(0) + "}", list.get(1));
});
return newTag[0];
}
private String parseDockerTag(File envFile, String tag) {
if (!FileUtil.isFile(envFile)) {
return tag;
}
final String[] newTag = {tag};
FileUtil.readLines(envFile, StandardCharsets.UTF_8, (LineHandler) line -> {
line = StrUtil.trim(line);
if (StrUtil.startWith(line, "#")) {
return;
}
List<String> list = StrUtil.splitTrim(line, "=");
if (CollUtil.size(list) != 2) {
return;
}
newTag[0] = StrUtil.replace(newTag[0], "${" + list.get(0) + "}", list.get(1));
});
return newTag[0];
}
private void doDockerImage() {
// 生成临时目录
File tempPath = FileUtil.file(ConfigBean.getInstance().getTempPath(), "build_temp", "docker_image", this.buildExtraModule.getId() + StrUtil.DASHED + this.buildId);
try {
File sourceFile = BuildUtil.getSourceById(this.buildExtraModule.getId());
FileUtil.copyContent(sourceFile, tempPath, true);
File historyPackageFile = BuildUtil.getHistoryPackageFile(buildExtraModule.getId(), this.buildId, StrUtil.SLASH);
FileUtil.copyContent(historyPackageFile, tempPath, true);
// env file
File envFile = FileUtil.file(tempPath, ".env");
String dockerTag = this.buildExtraModule.getDockerTag();
dockerTag = this.parseDockerTag(envFile, dockerTag);
// docker file
String moduleDockerfile = this.buildExtraModule.getDockerfile();
List<String> list = StrUtil.splitTrim(moduleDockerfile, StrUtil.COLON);
String dockerFile = CollUtil.getLast(list);
File dockerfile = FileUtil.file(tempPath, dockerFile);
if (!FileUtil.isFile(dockerfile)) {
logRecorder.info("仓库目录下没有找到 Dockerfile 文件:", dockerFile);
return;
}
File baseDir = FileUtil.file(tempPath, list.size() == 1 ? StrUtil.SLASH : CollUtil.get(list, 0));
//
String fromTag = this.buildExtraModule.getFromTag();
// 根据 tag 查询
List<DockerInfoModel> dockerInfoModels = buildExecuteService
.dockerInfoService
.queryByTag(this.buildExtraModule.getWorkspaceId(), 1, fromTag);
DockerInfoModel dockerInfoModel = CollUtil.getFirst(dockerInfoModels);
if (dockerInfoModel == null) {
logRecorder.info("没有可用的 docker server");
return;
}
for (DockerInfoModel infoModel : dockerInfoModels) {
this.doDockerImage(infoModel, dockerfile, baseDir, dockerTag);
}
// 发布 docker 服务
this.updateSwarmService(dockerTag, this.buildExtraModule.getDockerSwarmId(), this.buildExtraModule.getDockerSwarmServiceName());
} finally {
CommandUtil.systemFastDel(tempPath);
}
}
private void doDockerImage() {
// 生成临时目录
File tempPath = FileUtil.file(ConfigBean.getInstance().getTempPath(), "build_temp", "docker_image", this.buildExtraModule.getId() + StrUtil.DASHED + this.buildId);
try {
File sourceFile = BuildUtil.getSourceById(this.buildExtraModule.getId());
FileUtil.copyContent(sourceFile, tempPath, true);
File historyPackageFile = BuildUtil.getHistoryPackageFile(buildExtraModule.getId(), this.buildId, StrUtil.SLASH);
FileUtil.copyContent(historyPackageFile, tempPath, true);
// env file
File envFile = FileUtil.file(tempPath, ".env");
String dockerTag = this.buildExtraModule.getDockerTag();
dockerTag = this.parseDockerTag(envFile, dockerTag);
// docker file
String moduleDockerfile = this.buildExtraModule.getDockerfile();
List<String> list = StrUtil.splitTrim(moduleDockerfile, StrUtil.COLON);
String dockerFile = CollUtil.getLast(list);
File dockerfile = FileUtil.file(tempPath, dockerFile);
if (!FileUtil.isFile(dockerfile)) {
logRecorder.info("仓库目录下没有找到 Dockerfile 文件:", dockerFile);
return;
}
File baseDir = FileUtil.file(tempPath, list.size() == 1 ? StrUtil.SLASH : CollUtil.get(list, 0));
//
String fromTag = this.buildExtraModule.getFromTag();
// 根据 tag 查询
List<DockerInfoModel> dockerInfoModels = buildExecuteService
.dockerInfoService
.queryByTag(this.buildExtraModule.getWorkspaceId(), 1, fromTag);
DockerInfoModel dockerInfoModel = CollUtil.getFirst(dockerInfoModels);
if (dockerInfoModel == null) {
logRecorder.info("没有可用的 docker server");
return;
}
for (DockerInfoModel infoModel : dockerInfoModels) {
this.doDockerImage(infoModel, dockerfile, baseDir, dockerTag);
}
// 发布 docker 服务
this.updateSwarmService(dockerTag, this.buildExtraModule.getDockerSwarmId(), this.buildExtraModule.getDockerSwarmServiceName());
} finally {
CommandUtil.systemFastDel(tempPath);
}
}
private void updateSwarmService(String dockerTag, String swarmId, String serviceName) {
if (StrUtil.isEmpty(swarmId)) {
return;
}
List<String> splitTrim = StrUtil.splitTrim(dockerTag, StrUtil.COMMA);
String first = CollUtil.getFirst(splitTrim);
logRecorder.info("start update swarm service: {} use image {}", serviceName, first);
Map<String, Object> pluginMap = buildExecuteService.dockerInfoService.getBySwarmPluginMap(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);
}
}
private void updateSwarmService(String dockerTag, String swarmId, String serviceName) {
if (StrUtil.isEmpty(swarmId)) {
return;
}
List<String> splitTrim = StrUtil.splitTrim(dockerTag, StrUtil.COMMA);
String first = CollUtil.getFirst(splitTrim);
logRecorder.info("start update swarm service: {} use image {}", serviceName, first);
Map<String, Object> pluginMap = buildExecuteService.dockerInfoService.getBySwarmPluginMap(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);
}
}
private void doDockerImage(DockerInfoModel dockerInfoModel, File dockerfile, File baseDir, String dockerTag) {
logRecorder.info("{} start build image {}", dockerInfoModel.getName(), dockerTag);
Map<String, Object> map = dockerInfoModel.toParameter();
map.put("Dockerfile", dockerfile);
map.put("baseDirectory", baseDir);
//
map.put("tags", dockerTag);
Consumer<String> logConsumer = s -> logRecorder.append(s);
map.put("logConsumer", logConsumer);
IPlugin plugin = PluginFactory.getPlugin(DockerInfoService.DOCKER_PLUGIN_NAME);
try {
plugin.execute("buildImage", map);
} catch (Exception e) {
logRecorder.error("调用容器异常", e);
}
}
private void doDockerImage(DockerInfoModel dockerInfoModel, File dockerfile, File baseDir, String dockerTag) {
logRecorder.info("{} start build image {}", dockerInfoModel.getName(), dockerTag);
Map<String, Object> map = dockerInfoModel.toParameter();
map.put("Dockerfile", dockerfile);
map.put("baseDirectory", baseDir);
//
map.put("tags", dockerTag);
Consumer<String> logConsumer = s -> logRecorder.append(s);
map.put("logConsumer", logConsumer);
IPlugin plugin = PluginFactory.getPlugin(DockerInfoService.DOCKER_PLUGIN_NAME);
try {
plugin.execute("buildImage", map);
} catch (Exception e) {
logRecorder.error("调用容器异常", e);
}
}
/**
* 本地命令执行
*/
private void localCommand() {
// 执行命令
String[] commands = StrUtil.splitToArray(this.buildExtraModule.getReleaseCommand(), StrUtil.LF);
if (ArrayUtil.isEmpty(commands)) {
logRecorder.info("没有需要执行的ssh命令");
return;
}
String command = StrUtil.EMPTY;
logRecorder.info(DateUtil.now() + " start exec");
InputStream templateInputStream = null;
try {
templateInputStream = ResourceUtil.getStream("classpath:/bin/execTemplate." + CommandUtil.SUFFIX);
if (templateInputStream == null) {
logRecorder.info("系统中没有命令模版");
return;
}
String sshExecTemplate = IoUtil.readUtf8(templateInputStream);
StringBuilder stringBuilder = new StringBuilder(sshExecTemplate);
// 替换变量
this.formatCommand(commands);
//
stringBuilder.append(ArrayUtil.join(commands, StrUtil.LF));
File tempPath = ConfigBean.getInstance().getTempPath();
File commandFile = FileUtil.file(tempPath, "build", this.buildExtraModule.getId() + StrUtil.DOT + CommandUtil.SUFFIX);
FileUtil.writeUtf8String(stringBuilder.toString(), commandFile);
//
command = SystemUtil.getOsInfo().isWindows() ? StrUtil.EMPTY : CommandUtil.SUFFIX;
command += " " + FileUtil.getAbsolutePath(commandFile);
String result = CommandUtil.execSystemCommand(command);
logRecorder.info(result);
} catch (Exception e) {
this.pubLog("执行本地命令异常:" + command, e);
} finally {
IoUtil.close(templateInputStream);
}
}
/**
* 本地命令执行
*/
private void localCommand() {
// 执行命令
String[] commands = StrUtil.splitToArray(this.buildExtraModule.getReleaseCommand(), StrUtil.LF);
if (ArrayUtil.isEmpty(commands)) {
logRecorder.info("没有需要执行的ssh命令");
return;
}
String command = StrUtil.EMPTY;
logRecorder.info(DateUtil.now() + " start exec");
InputStream templateInputStream = null;
try {
templateInputStream = ResourceUtil.getStream("classpath:/bin/execTemplate." + CommandUtil.SUFFIX);
if (templateInputStream == null) {
logRecorder.info("系统中没有命令模版");
return;
}
String sshExecTemplate = IoUtil.readUtf8(templateInputStream);
StringBuilder stringBuilder = new StringBuilder(sshExecTemplate);
// 替换变量
this.formatCommand(commands);
//
stringBuilder.append(ArrayUtil.join(commands, StrUtil.LF));
File tempPath = ConfigBean.getInstance().getTempPath();
File commandFile = FileUtil.file(tempPath, "build", this.buildExtraModule.getId() + StrUtil.DOT + CommandUtil.SUFFIX);
FileUtil.writeUtf8String(stringBuilder.toString(), commandFile);
//
// command = SystemUtil.getOsInfo().isWindows() ? StrUtil.EMPTY : CommandUtil.SUFFIX;
command = CommandUtil.EXECUTE_PREFIX + StrUtil.SPACE + FileUtil.getAbsolutePath(commandFile);
String result = CommandUtil.execSystemCommand(command);
logRecorder.info(result);
} catch (Exception e) {
this.pubLog("执行本地命令异常:" + command, e);
} finally {
IoUtil.close(templateInputStream);
}
}
/**
* ssh 发布
*/
private void doSsh() {
String releaseMethodDataId = this.buildExtraModule.getReleaseMethodDataId();
SshService sshService = SpringUtil.getBean(SshService.class);
List<String> strings = StrUtil.splitTrim(releaseMethodDataId, StrUtil.COMMA);
for (String releaseMethodDataIdItem : strings) {
SshModel item = sshService.getByKey(releaseMethodDataIdItem, false);
if (item == null) {
logRecorder.info("没有找到对应的ssh项" + releaseMethodDataIdItem);
continue;
}
this.doSsh(item, sshService);
}
}
/**
* ssh 发布
*/
private void doSsh() {
String releaseMethodDataId = this.buildExtraModule.getReleaseMethodDataId();
SshService sshService = SpringUtil.getBean(SshService.class);
List<String> strings = StrUtil.splitTrim(releaseMethodDataId, StrUtil.COMMA);
for (String releaseMethodDataIdItem : strings) {
SshModel item = sshService.getByKey(releaseMethodDataIdItem, false);
if (item == null) {
logRecorder.info("没有找到对应的ssh项" + releaseMethodDataIdItem);
continue;
}
this.doSsh(item, sshService);
}
}
private void doSsh(SshModel item, SshService sshService) {
Session session = SshService.getSessionByModel(item);
try {
String releasePath = this.buildExtraModule.getReleasePath();
if (StrUtil.isEmpty(releasePath)) {
logRecorder.info("发布目录为空");
} else {
logRecorder.info("{} {} start ftp upload", DateUtil.now(), item.getName());
try (Sftp sftp = new Sftp(session, item.getCharsetT())) {
String prefix = "";
if (!StrUtil.startWith(releasePath, StrUtil.SLASH)) {
prefix = sftp.pwd();
}
String normalizePath = FileUtil.normalize(prefix + StrUtil.SLASH + releasePath);
if (this.buildExtraModule.isClearOld()) {
try {
sftp.delDir(normalizePath);
} catch (Exception e) {
if (!StrUtil.startWithIgnoreCase(e.getMessage(), "No such file")) {
this.pubLog("清除构建产物失败", e);
}
}
}
sftp.syncUpload(this.resultFile, normalizePath);
logRecorder.info("{} ftp upload done", item.getName());
} catch (Exception e) {
this.pubLog("执行ssh发布异常", e);
}
}
} finally {
JschUtil.close(session);
}
logRecorder.info("");
// 执行命令
String[] commands = StrUtil.splitToArray(this.buildExtraModule.getReleaseCommand(), StrUtil.LF);
if (commands == null || commands.length <= 0) {
logRecorder.info("没有需要执行的ssh命令");
return;
}
// 替换变量
this.formatCommand(commands);
//
logRecorder.info("{} {} start exec", DateUtil.now(), item.getName());
try {
String s = sshService.exec(item, commands);
logRecorder.info(s);
} catch (Exception e) {
this.pubLog(item.getName() + " 执行异常", e);
}
}
private void doSsh(SshModel item, SshService sshService) {
Session session = SshService.getSessionByModel(item);
try {
String releasePath = this.buildExtraModule.getReleasePath();
if (StrUtil.isEmpty(releasePath)) {
logRecorder.info("发布目录为空");
} else {
logRecorder.info("{} {} start ftp upload", DateUtil.now(), item.getName());
try (Sftp sftp = new Sftp(session, item.getCharsetT())) {
String prefix = "";
if (!StrUtil.startWith(releasePath, StrUtil.SLASH)) {
prefix = sftp.pwd();
}
String normalizePath = FileUtil.normalize(prefix + StrUtil.SLASH + releasePath);
if (this.buildExtraModule.isClearOld()) {
try {
sftp.delDir(normalizePath);
} catch (Exception e) {
if (!StrUtil.startWithIgnoreCase(e.getMessage(), "No such file")) {
this.pubLog("清除构建产物失败", e);
}
}
}
sftp.syncUpload(this.resultFile, normalizePath);
logRecorder.info("{} ftp upload done", item.getName());
} catch (Exception e) {
this.pubLog("执行ssh发布异常", e);
}
}
} finally {
JschUtil.close(session);
}
logRecorder.info("");
// 执行命令
String[] commands = StrUtil.splitToArray(this.buildExtraModule.getReleaseCommand(), StrUtil.LF);
if (commands == null || commands.length <= 0) {
logRecorder.info("没有需要执行的ssh命令");
return;
}
// 替换变量
this.formatCommand(commands);
//
logRecorder.info("{} {} start exec", DateUtil.now(), item.getName());
try {
String s = sshService.exec(item, commands);
logRecorder.info(s);
} catch (Exception e) {
this.pubLog(item.getName() + " 执行异常", e);
}
}
/**
* 差异上传发布
*
* @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<File> files = FileUtil.loopFiles(resultFile);
List<JSONObject> 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);
JsonMessage<JSONObject> requestBody = NodeForward.requestBody(nodeModel, NodeUrl.MANAGE_FILE_DIFF_FILE, this.userModel, jsonObject);
if (requestBody.getCode() != HttpStatus.HTTP_OK) {
throw new JpomRuntimeException("对比项目文件失败:" + 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.info(StrUtil.format("对比文件结果,产物文件 {} 个、需要上传 {} 个、需要删除 {} 个", CollUtil.size(collect), CollUtil.size(diff), delSize));
} else {
logRecorder.info(StrUtil.format("对比文件结果,产物文件 {} 个、需要上传 {} 个", CollUtil.size(collect), CollUtil.size(diff)));
}
// 清空发布才先执行删除
if (delSize > 0 && clearOld) {
jsonObject.put("data", del);
requestBody = NodeForward.requestBody(nodeModel, NodeUrl.MANAGE_FILE_BATCH_DELETE, this.userModel, jsonObject);
if (requestBody.getCode() != HttpStatus.HTTP_OK) {
throw new JpomRuntimeException("删除项目文件失败:" + 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);
//
JsonMessage<String> jsonMessage = OutGivingRun.fileUpload(file, startPath,
projectId, false, last ? afterOpt : AfterOpt.No, nodeModel, this.userModel, false);
if (jsonMessage.getCode() != HttpStatus.HTTP_OK) {
throw new JpomRuntimeException("同步项目文件失败:" + jsonMessage);
}
if (last) {
// 最后一个
logRecorder.info("发布项目包成功:" + jsonMessage);
}
}
}
/**
* 差异上传发布
*
* @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<File> files = FileUtil.loopFiles(resultFile);
List<JSONObject> 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);
JsonMessage<JSONObject> requestBody = NodeForward.requestBody(nodeModel, NodeUrl.MANAGE_FILE_DIFF_FILE, this.userModel, jsonObject);
if (requestBody.getCode() != HttpStatus.HTTP_OK) {
throw new JpomRuntimeException("对比项目文件失败:" + 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.info(StrUtil.format("对比文件结果,产物文件 {} 个、需要上传 {} 个、需要删除 {} 个", CollUtil.size(collect), CollUtil.size(diff), delSize));
} else {
logRecorder.info(StrUtil.format("对比文件结果,产物文件 {} 个、需要上传 {} 个", CollUtil.size(collect), CollUtil.size(diff)));
}
// 清空发布才先执行删除
if (delSize > 0 && clearOld) {
jsonObject.put("data", del);
requestBody = NodeForward.requestBody(nodeModel, NodeUrl.MANAGE_FILE_BATCH_DELETE, this.userModel, jsonObject);
if (requestBody.getCode() != HttpStatus.HTTP_OK) {
throw new JpomRuntimeException("删除项目文件失败:" + 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);
//
JsonMessage<String> jsonMessage = OutGivingRun.fileUpload(file, startPath,
projectId, false, last ? afterOpt : AfterOpt.No, nodeModel, this.userModel, false);
if (jsonMessage.getCode() != HttpStatus.HTTP_OK) {
throw new JpomRuntimeException("同步项目文件失败:" + jsonMessage);
}
if (last) {
// 最后一个
logRecorder.info("发布项目包成功:" + jsonMessage);
}
}
}
/**
* 发布项目
*/
private void doProject() {
/**
* 发布项目
*/
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;
}
File zipFile = BuildUtil.isDirPackage(this.resultFile);
boolean unZip = true;
if (zipFile == null) {
zipFile = this.resultFile;
unZip = false;
}
JsonMessage<String> jsonMessage = OutGivingRun.fileUpload(zipFile, null,
projectId,
unZip,
afterOpt,
nodeModel, this.userModel, clearOld);
if (jsonMessage.getCode() == HttpStatus.HTTP_OK) {
logRecorder.info("发布项目包成功:" + jsonMessage);
} else {
throw new JpomRuntimeException("发布项目包失败:" + jsonMessage);
}
}
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;
}
File zipFile = BuildUtil.isDirPackage(this.resultFile);
boolean unZip = true;
if (zipFile == null) {
zipFile = this.resultFile;
unZip = false;
}
JsonMessage<String> jsonMessage = OutGivingRun.fileUpload(zipFile, null,
projectId,
unZip,
afterOpt,
nodeModel, this.userModel, clearOld);
if (jsonMessage.getCode() == HttpStatus.HTTP_OK) {
logRecorder.info("发布项目包成功:" + jsonMessage);
} else {
throw new JpomRuntimeException("发布项目包失败:" + jsonMessage);
}
}
/**
* 分发包
*/
private void doOutGiving() {
String releaseMethodDataId = this.buildExtraModule.getReleaseMethodDataId();
File zipFile = BuildUtil.isDirPackage(this.resultFile);
boolean unZip = true;
if (zipFile == null) {
zipFile = this.resultFile;
unZip = false;
}
OutGivingRun.startRun(releaseMethodDataId, zipFile, userModel, unZip);
logRecorder.info("开始执行分发包啦,请到分发中查看当前状态");
}
/**
* 分发包
*/
private void doOutGiving() {
String releaseMethodDataId = this.buildExtraModule.getReleaseMethodDataId();
File zipFile = BuildUtil.isDirPackage(this.resultFile);
boolean unZip = true;
if (zipFile == null) {
zipFile = this.resultFile;
unZip = false;
}
OutGivingRun.startRun(releaseMethodDataId, zipFile, userModel, unZip);
logRecorder.info("开始执行分发包啦,请到分发中查看当前状态");
}
/**
* 发布异常日志
*
* @param title 描述
* @param throwable 异常
*/
private void pubLog(String title, Throwable throwable) {
logRecorder.error(title, throwable);
this.updateStatus(BuildStatus.PubError);
}
/**
* 发布异常日志
*
* @param title 描述
* @param throwable 异常
*/
private void pubLog(String title, Throwable throwable) {
logRecorder.error(title, throwable);
this.updateStatus(BuildStatus.PubError);
}
@Override
public void run() {
this.start();
}
@Override
public void run() {
this.start();
}
}

View File

@ -30,7 +30,6 @@ import cn.hutool.core.io.LineHandler;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.system.SystemUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import cn.jiangzeyin.common.JsonMessage;
import cn.jiangzeyin.common.spring.SpringUtil;
@ -55,177 +54,175 @@ import java.util.concurrent.ConcurrentHashMap;
* @date 2022/1/19
*/
public class ScriptProcessBuilder extends BaseRunScript implements Runnable {
/**
* 执行中的缓存
*/
private static final ConcurrentHashMap<String, ScriptProcessBuilder> FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP = new ConcurrentHashMap<>();
/**
* 执行中的缓存
*/
private static final ConcurrentHashMap<String, ScriptProcessBuilder> FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP = new ConcurrentHashMap<>();
private final ProcessBuilder processBuilder;
private final Set<WebSocketSession> sessions = new HashSet<>();
private final String executeId;
private final ProcessBuilder processBuilder;
private final Set<WebSocketSession> sessions = new HashSet<>();
private final String executeId;
private ScriptProcessBuilder(ScriptModel nodeScriptModel, String executeId, String args) {
super(nodeScriptModel.logFile(executeId));
this.executeId = executeId;
//
WorkspaceEnvVarService workspaceEnvVarService = SpringUtil.getBean(WorkspaceEnvVarService.class);
Map<String, String> env = workspaceEnvVarService.getEnv(nodeScriptModel.getWorkspaceId());
private ScriptProcessBuilder(ScriptModel nodeScriptModel, String executeId, String args) {
super(nodeScriptModel.logFile(executeId));
this.executeId = executeId;
//
WorkspaceEnvVarService workspaceEnvVarService = SpringUtil.getBean(WorkspaceEnvVarService.class);
Map<String, String> env = workspaceEnvVarService.getEnv(nodeScriptModel.getWorkspaceId());
File scriptFile = nodeScriptModel.scriptFile();
//
String script = FileUtil.getAbsolutePath(scriptFile);
processBuilder = new ProcessBuilder();
List<String> command = StrUtil.splitTrim(args, StrUtil.SPACE);
command.add(0, script);
if (SystemUtil.getOsInfo().isLinux() || SystemUtil.getOsInfo().isMac()) {
command.add(0, CommandUtil.SUFFIX);
}
DefaultSystemLog.getLog().debug(CollUtil.join(command, StrUtil.SPACE));
processBuilder.redirectErrorStream(true);
processBuilder.command(command);
Map<String, String> environment = processBuilder.environment();
environment.putAll(env);
processBuilder.directory(scriptFile.getParentFile());
}
File scriptFile = nodeScriptModel.scriptFile();
//
String script = FileUtil.getAbsolutePath(scriptFile);
processBuilder = new ProcessBuilder();
List<String> command = StrUtil.splitTrim(args, StrUtil.SPACE);
command.add(0, script);
command.add(0, CommandUtil.EXECUTE_PREFIX);
DefaultSystemLog.getLog().debug(CollUtil.join(command, StrUtil.SPACE));
processBuilder.redirectErrorStream(true);
processBuilder.command(command);
Map<String, String> environment = processBuilder.environment();
environment.putAll(env);
processBuilder.directory(scriptFile.getParentFile());
}
/**
* 创建执行 并监听
*
* @param nodeScriptModel 脚本模版
* @param executeId 执行ID
* @param args 参数
*/
public static ScriptProcessBuilder create(ScriptModel nodeScriptModel, String executeId, String args) {
return FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.computeIfAbsent(executeId, file1 -> {
ScriptProcessBuilder scriptProcessBuilder1 = new ScriptProcessBuilder(nodeScriptModel, executeId, args);
ThreadUtil.execute(scriptProcessBuilder1);
return scriptProcessBuilder1;
});
}
/**
* 创建执行 并监听
*
* @param nodeScriptModel 脚本模版
* @param executeId 执行ID
* @param args 参数
*/
public static ScriptProcessBuilder create(ScriptModel nodeScriptModel, String executeId, String args) {
return FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.computeIfAbsent(executeId, file1 -> {
ScriptProcessBuilder scriptProcessBuilder1 = new ScriptProcessBuilder(nodeScriptModel, executeId, args);
ThreadUtil.execute(scriptProcessBuilder1);
return scriptProcessBuilder1;
});
}
/**
* 创建执行 并监听
*
* @param nodeScriptModel 脚本模版
* @param executeId 执行ID
* @param args 参数
* @param session 会话
*/
public static void addWatcher(ScriptModel nodeScriptModel, String executeId, String args, WebSocketSession session) {
ScriptProcessBuilder scriptProcessBuilder = create(nodeScriptModel, executeId, args);
//
if (scriptProcessBuilder.sessions.add(session)) {
if (FileUtil.exist(scriptProcessBuilder.logFile)) {
// 读取之前的信息并发送
FileUtil.readLines(scriptProcessBuilder.logFile, CharsetUtil.CHARSET_UTF_8, (LineHandler) line -> {
try {
SocketSessionUtil.send(session, line);
} catch (IOException e) {
DefaultSystemLog.getLog().error("发送消息失败", e);
}
});
}
}
}
/**
* 创建执行 并监听
*
* @param nodeScriptModel 脚本模版
* @param executeId 执行ID
* @param args 参数
* @param session 会话
*/
public static void addWatcher(ScriptModel nodeScriptModel, String executeId, String args, WebSocketSession session) {
ScriptProcessBuilder scriptProcessBuilder = create(nodeScriptModel, executeId, args);
//
if (scriptProcessBuilder.sessions.add(session)) {
if (FileUtil.exist(scriptProcessBuilder.logFile)) {
// 读取之前的信息并发送
FileUtil.readLines(scriptProcessBuilder.logFile, CharsetUtil.CHARSET_UTF_8, (LineHandler) line -> {
try {
SocketSessionUtil.send(session, line);
} catch (IOException e) {
DefaultSystemLog.getLog().error("发送消息失败", e);
}
});
}
}
}
/**
* 判断是否还在执行中
*
* @param executeId 执行id
* @return true 还在执行
*/
public static boolean isRun(String executeId) {
return FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.containsKey(executeId);
}
/**
* 判断是否还在执行中
*
* @param executeId 执行id
* @return true 还在执行
*/
public static boolean isRun(String executeId) {
return FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.containsKey(executeId);
}
/**
* 关闭会话
*
* @param session 会话
*/
public static void stopWatcher(WebSocketSession session) {
Collection<ScriptProcessBuilder> scriptProcessBuilders = FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.values();
for (ScriptProcessBuilder scriptProcessBuilder : scriptProcessBuilders) {
Set<WebSocketSession> sessions = scriptProcessBuilder.sessions;
sessions.removeIf(session1 -> session1.getId().equals(session.getId()));
}
}
/**
* 关闭会话
*
* @param session 会话
*/
public static void stopWatcher(WebSocketSession session) {
Collection<ScriptProcessBuilder> scriptProcessBuilders = FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.values();
for (ScriptProcessBuilder scriptProcessBuilder : scriptProcessBuilders) {
Set<WebSocketSession> sessions = scriptProcessBuilder.sessions;
sessions.removeIf(session1 -> session1.getId().equals(session.getId()));
}
}
/**
* 停止脚本命令
*
* @param executeId 执行ID
*/
public static void stopRun(String executeId) {
ScriptProcessBuilder scriptProcessBuilder = FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.get(executeId);
if (scriptProcessBuilder != null) {
scriptProcessBuilder.end("停止运行");
}
}
/**
* 停止脚本命令
*
* @param executeId 执行ID
*/
public static void stopRun(String executeId) {
ScriptProcessBuilder scriptProcessBuilder = FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.get(executeId);
if (scriptProcessBuilder != null) {
scriptProcessBuilder.end("停止运行");
}
}
@Override
public void run() {
//初始化ProcessBuilder对象
try {
this.handle("start execute:" + DateUtil.now());
process = processBuilder.start();
{
inputStream = process.getInputStream();
IoUtil.readLines(inputStream, ExtConfigBean.getInstance().getConsoleLogCharset(), (LineHandler) ScriptProcessBuilder.this::handle);
}
int waitFor = process.waitFor();
JsonMessage<String> jsonMessage = new JsonMessage<>(200, "执行完毕:" + waitFor);
JSONObject jsonObject = jsonMessage.toJson();
jsonObject.put("op", ConsoleCommandOp.stop.name());
this.end(jsonObject.toString());
this.handle("execute done:" + waitFor + " time:" + DateUtil.now());
} catch (Exception e) {
DefaultSystemLog.getLog().error("执行异常", e);
this.end("执行异常:" + e.getMessage());
} finally {
this.close();
}
}
@Override
public void run() {
//初始化ProcessBuilder对象
try {
this.handle("start execute:" + DateUtil.now());
process = processBuilder.start();
{
inputStream = process.getInputStream();
IoUtil.readLines(inputStream, ExtConfigBean.getInstance().getConsoleLogCharset(), (LineHandler) ScriptProcessBuilder.this::handle);
}
int waitFor = process.waitFor();
JsonMessage<String> jsonMessage = new JsonMessage<>(200, "执行完毕:" + waitFor);
JSONObject jsonObject = jsonMessage.toJson();
jsonObject.put("op", ConsoleCommandOp.stop.name());
this.end(jsonObject.toString());
this.handle("execute done:" + waitFor + " time:" + DateUtil.now());
} catch (Exception e) {
DefaultSystemLog.getLog().error("执行异常", e);
this.end("执行异常:" + e.getMessage());
} finally {
this.close();
}
}
/**
* 结束执行
*
* @param msg 响应的消息
*/
@Override
protected void end(String msg) {
Iterator<WebSocketSession> iterator = sessions.iterator();
while (iterator.hasNext()) {
WebSocketSession session = iterator.next();
try {
SocketSessionUtil.send(session, msg);
} catch (IOException e) {
DefaultSystemLog.getLog().error("发送消息失败", e);
}
iterator.remove();
}
FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.remove(this.executeId);
}
/**
* 结束执行
*
* @param msg 响应的消息
*/
@Override
protected void end(String msg) {
Iterator<WebSocketSession> iterator = sessions.iterator();
while (iterator.hasNext()) {
WebSocketSession session = iterator.next();
try {
SocketSessionUtil.send(session, msg);
} catch (IOException e) {
DefaultSystemLog.getLog().error("发送消息失败", e);
}
iterator.remove();
}
FILE_SCRIPT_PROCESS_BUILDER_CONCURRENT_HASH_MAP.remove(this.executeId);
}
/**
* 响应
*
* @param line 信息
*/
@Override
protected void handle(String line) {
super.handle(line);
//
Iterator<WebSocketSession> iterator = sessions.iterator();
while (iterator.hasNext()) {
WebSocketSession session = iterator.next();
try {
SocketSessionUtil.send(session, line);
} catch (IOException e) {
DefaultSystemLog.getLog().error("发送消息失败", e);
iterator.remove();
}
}
}
/**
* 响应
*
* @param line 信息
*/
@Override
protected void handle(String line) {
super.handle(line);
//
Iterator<WebSocketSession> iterator = sessions.iterator();
while (iterator.hasNext()) {
WebSocketSession session = iterator.next();
try {
SocketSessionUtil.send(session, line);
} catch (IOException e) {
DefaultSystemLog.getLog().error("发送消息失败", e);
iterator.remove();
}
}
}
}

View File

@ -1,7 +1,7 @@
<template>
<div class="full-content">
<!-- 数据表格 -->
<a-table :data-source="list" :columns="columns" @change="changePage" :pagination="this.listQuery.total / this.listQuery.limit > 1 ? (this, pagination) : false" bordered rowKey="id">
<a-table :data-source="list" size="middle" :columns="columns" @change="changePage" :pagination="this.listQuery.total / this.listQuery.limit > 1 ? (this, pagination) : false" bordered rowKey="id">
<template slot="title">
<a-space>
<a-input v-model="listQuery['%name%']" placeholder="名称" @pressEnter="loadData" allowClear class="search-input-item" />
@ -46,9 +46,9 @@
</a-tooltip>
<template slot="operation" slot-scope="text, record">
<a-space>
<a-button type="primary" @click="handleExec(record)">执行</a-button>
<a-button type="primary" @click="handleEdit(record)">编辑</a-button>
<a-button type="danger" @click="handleDelete(record)">删除</a-button>
<a-button size="small" type="primary" @click="handleExec(record)">执行</a-button>
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
<a-button size="small" type="danger" @click="handleDelete(record)">删除</a-button>
</a-space>
</template>
</a-table>
@ -137,7 +137,7 @@ export default {
{ title: "修改时间", dataIndex: "modifyTimeMillis", sorter: true, width: 170, ellipsis: true, scopedSlots: { customRender: "modifyTimeMillis" } },
{ title: "修改人", dataIndex: "modifyUser", ellipsis: true, scopedSlots: { customRender: "modifyUser" }, width: 120 },
{ title: "最后操作人", dataIndex: "lastRunUser", ellipsis: true, scopedSlots: { customRender: "lastRunUser" } },
{ title: "操作", dataIndex: "operation", scopedSlots: { customRender: "operation" }, width: 260 },
{ title: "操作", dataIndex: "operation", scopedSlots: { customRender: "operation" }, width: 180 },
],
rules: {
name: [{ required: true, message: "Please input Script name", trigger: "blur" }],

View File

@ -3,6 +3,7 @@
<a-table
:data-source="commandList"
:columns="columns"
size="middle"
bordered
:pagination="this.listQuery.total / this.listQuery.limit > 1 ? (this, pagination) : false"
@change="changePage"
@ -43,9 +44,9 @@
<template slot="operation" slot-scope="text, record">
<a-space>
<a-button type="primary" @click="handleEdit(record)">编辑</a-button>
<a-button type="primary" @click="handleExecute(record)">执行</a-button>
<a-button type="danger" @click="handleDelete(record)">删除</a-button>
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button>
<a-button size="small" type="primary" @click="handleExecute(record)">执行</a-button>
<a-button size="small" type="danger" @click="handleDelete(record)">删除</a-button>
</a-space>
</template>
</a-table>
@ -204,7 +205,7 @@ export default {
ellipsis: true,
scopedSlots: { customRender: "modifyUser" },
},
{ title: "操作", dataIndex: "operation", scopedSlots: { customRender: "operation" }, width: 250 },
{ title: "操作", dataIndex: "operation", scopedSlots: { customRender: "operation" }, width: 180 },
],
};
},

View File

@ -7,6 +7,7 @@
<a-table
:data-source="list"
:columns="columns"
size="middle"
:pagination="this.listQuery.total / this.listQuery.limit > 1 ? (this, pagination) : false"
@change="changePage"
bordered
@ -42,7 +43,7 @@
<div v-if="sshAgentInfo[record.id]">
<div v-if="sshAgentInfo[record.id].javaVersion">
<a-tooltip v-if="sshAgentInfo[record.id].nodeId" placement="topLeft" :title="`节点名称:${sshAgentInfo[record.id].nodeName}`">
<a-button style="width: 90px; padding: 0 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis" type="" @click="toNode(sshAgentInfo[record.id].nodeId)">
<a-button size="small" style="width: 90px; padding: 0 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis" type="" @click="toNode(sshAgentInfo[record.id].nodeId)">
{{ sshAgentInfo[record.id].nodeName }}
</a-button>
</a-tooltip>
@ -54,7 +55,7 @@
placement="topLeft"
:title="`${sshAgentInfo[record.id].pid > 0 ? 'ssh 中已经运行了插件端进程ID' + sshAgentInfo[record.id].pid : '点击快速安装插件端,java :' + sshAgentInfo[record.id].javaVersion}`"
>
<a-button type="primary" @click="install(record)" :disabled="sshAgentInfo[record.id].pid > 0">安装节点</a-button>
<a-button size="small" type="primary" @click="install(record)" :disabled="sshAgentInfo[record.id].pid > 0">安装节点</a-button>
</a-tooltip>
</div>
<div v-else><a-tag>没有Java环境</a-tag></div>
@ -63,9 +64,9 @@
</template>
<template slot="operation" slot-scope="text, record">
<a-space>
<a-button type="primary" @click="handleTerminal(record)">终端</a-button>
<a-button size="small" type="primary" @click="handleTerminal(record)">终端</a-button>
<a-tooltip placement="topLeft" title="如果按钮不可用,请去 ssh 编辑中添加允许管理的授权文件夹">
<a-button type="primary" :disabled="!record.fileDirs" @click="handleFile(record)">文件</a-button>
<a-button size="small" type="primary" :disabled="!record.fileDirs" @click="handleFile(record)">文件</a-button>
</a-tooltip>
<a-dropdown>
<a class="ant-dropdown-link" @click="(e) => e.preventDefault()">
@ -412,8 +413,8 @@ export default {
title: "操作",
dataIndex: "operation",
scopedSlots: { customRender: "operation" },
width: 230,
fixed: "right",
width: 180,
align: "center",
// ellipsis: true,
},
],