|
|
package com.dsideal.Utils;
|
|
|
|
|
|
import cn.hutool.core.io.FileUtil;
|
|
|
import cn.hutool.core.util.ZipUtil;
|
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
|
import com.jfinal.kit.Kv;
|
|
|
import com.jfinal.kit.PathKit;
|
|
|
import com.jfinal.kit.PropKit;
|
|
|
import com.jfinal.kit.StrKit;
|
|
|
|
|
|
import java.io.*;
|
|
|
import java.time.LocalDateTime;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
import java.util.*;
|
|
|
|
|
|
public class PublishUtil {
|
|
|
|
|
|
/**
|
|
|
* 功能:执行批处理命令
|
|
|
*
|
|
|
* @param path
|
|
|
* @throws IOException
|
|
|
* @throws InterruptedException
|
|
|
*/
|
|
|
public static void ExecBatchCmd(String path) throws IOException, InterruptedException {
|
|
|
String[] cmd = {path};
|
|
|
Process process = Runtime.getRuntime().exec(cmd);
|
|
|
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
|
|
String line;
|
|
|
while ((line = reader.readLine()) != null) {
|
|
|
System.out.println(line);
|
|
|
}
|
|
|
process.waitFor();
|
|
|
}
|
|
|
|
|
|
public static void doLogin(SSHUtil ssh, String username, String password) throws Exception {
|
|
|
String cmd = "docker login --username=" + username + " --password=" + password + " registry.cn-hangzhou.aliyuncs.com";
|
|
|
ssh.exec(cmd);
|
|
|
}
|
|
|
|
|
|
public static void bluePrint(String msg) {
|
|
|
// 设置文本颜色
|
|
|
System.out.println("\033[34m" + msg + "\033[0m"); // 蓝色
|
|
|
}
|
|
|
|
|
|
public static void redPrint(String msg) {
|
|
|
// 设置文本颜色
|
|
|
System.out.println("\033[31m" + msg + "\033[0m"); // 红色文本
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 删除本机所有Docker镜像
|
|
|
* docker images --format "{{.Repository}}:{{.Tag}}" | xargs -r docker rmi -f
|
|
|
* 登录远程仓库
|
|
|
* docker login --username=东师黄海 registry.cn-hangzhou.aliyuncs.com --password DsideaL4r5t6y7u
|
|
|
* docker login --username=驿来特充电 registry.cn-hangzhou.aliyuncs.com --password=ylt5033.
|
|
|
* <p>
|
|
|
* # 搜索
|
|
|
* docker search registry.cn-hangzhou.aliyuncs.com/yltcharge/zhu-que:20240903
|
|
|
* #拉取
|
|
|
* docker pull registry.cn-hangzhou.aliyuncs.com/yltcharge/zhu-que:20240903
|
|
|
* <p>
|
|
|
* docker pull registry.cn-hangzhou.aliyuncs.com/dsideal/jdk:21
|
|
|
* docker tag 19a54d2204aa registry.cn-hangzhou.aliyuncs.com/yltcharge/jdk:21
|
|
|
* docker push registry.cn-hangzhou.aliyuncs.com/yltcharge/jdk:21
|
|
|
*/
|
|
|
public static Kv publish(String projectName, String devProjectName, boolean isStatic, String workingPath, String localLibPath, Kv choiceWarehouse, String choiceConfig) throws Exception {
|
|
|
|
|
|
System.out.println("正在生成" + projectName + "的镜像...");
|
|
|
//配置文件
|
|
|
String path = PathKit.getRootClassPath() + "\\publishImage.json";
|
|
|
JSONObject jo = JSONObject.parseObject(FileUtil.readUtf8String(path));
|
|
|
//主机
|
|
|
String host = jo.getString("host");
|
|
|
String user = jo.getString("user");
|
|
|
String pwd = jo.getString("pwd");
|
|
|
int port = jo.getIntValue("port");
|
|
|
|
|
|
String project_port = null;
|
|
|
|
|
|
//声明SSH对象
|
|
|
SSHUtil ssh = new SSHUtil(user, pwd, host, port);
|
|
|
ssh.connect();
|
|
|
|
|
|
// 格式化日期
|
|
|
LocalDateTime now = LocalDateTime.now();
|
|
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
String newVersion = now.format(formatter);
|
|
|
if (!StrKit.isBlank(choiceConfig)) {
|
|
|
newVersion = choiceConfig + "_" + newVersion.replace(" ", "").replace("-", "").replace(":", "");
|
|
|
} else {
|
|
|
newVersion = newVersion.replace(" ", "").replace("-", "").replace(":", "");
|
|
|
}
|
|
|
//准备工作
|
|
|
String remotePath = "/usr/local/" + projectName + "/";
|
|
|
ssh.exec("rm -rf " + remotePath);
|
|
|
ssh.mkdir(remotePath);
|
|
|
|
|
|
if (!isStatic) {
|
|
|
System.out.println("正在上传Dockerfile...");
|
|
|
ssh.upload(workingPath + "Dockerfile", remotePath + "Dockerfile");
|
|
|
}
|
|
|
|
|
|
System.out.println("正在上传" + projectName + "的jar包...");
|
|
|
//上传jar包
|
|
|
String fatjarName = devProjectName + "-jar-with-dependencies.jar";
|
|
|
String localFile = workingPath + "target/" + fatjarName;
|
|
|
//获取项目端口
|
|
|
String undertow_dev = workingPath.replace("\\","/") + "target/classes/undertow_dev.properties";
|
|
|
List<String> list=FileUtil.readLines(undertow_dev, "UTF-8");
|
|
|
for (String line : list) {
|
|
|
if (line.contains("undertow.port")) {
|
|
|
String[] split = line.split("=");
|
|
|
project_port = split[1];
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
if (FileUtil.exist(localFile)) {
|
|
|
System.out.println("正在上传" + fatjarName + "...");
|
|
|
//上传
|
|
|
ssh.upload(localFile, remotePath + fatjarName);
|
|
|
}
|
|
|
|
|
|
//处理静态文件
|
|
|
if (isStatic) {
|
|
|
//倒数第二个/
|
|
|
int cnt = 0;
|
|
|
int pos;
|
|
|
for (int i = workingPath.length() - 1; ; i--) {
|
|
|
if (workingPath.charAt(i) == '\\') cnt++;
|
|
|
if (cnt == 2) {
|
|
|
pos = i;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
localFile = workingPath.substring(0, pos) + "\\static.zip";
|
|
|
if (FileUtil.exist(localFile)) {
|
|
|
FileUtil.del(localFile);
|
|
|
}
|
|
|
System.out.println("正在上传static.zip...");
|
|
|
// 将目录打包成ZIP文件,不包含目录本身,只包含目录下的文件和子目录
|
|
|
ZipUtil.zip(workingPath, localFile, false);
|
|
|
//上传
|
|
|
ssh.upload(localFile, remotePath + "static.zip");
|
|
|
//删除target.zip
|
|
|
FileUtil.del(localFile);
|
|
|
//解压缩
|
|
|
System.out.println("正在解压缩static.zip...");
|
|
|
ssh.exec("cd " + remotePath + " && unzip static.zip");
|
|
|
ssh.exec("cd " + remotePath + " && rm -rf static.zip");
|
|
|
}
|
|
|
//登录镜像仓库
|
|
|
//处理一下Dockerfile中的JDK21此用问题,因为不同的打包操作,需要登录不同的仓库,这里临时用shell修改一下
|
|
|
String remoteDockerFile = remotePath + "Dockerfile";
|
|
|
|
|
|
if (choiceWarehouse.get("id") != null) {
|
|
|
doLogin(ssh, choiceWarehouse.getStr("username"), choiceWarehouse.getStr("password"));
|
|
|
System.out.println("仓库登录成功!");
|
|
|
}
|
|
|
|
|
|
//修改镜像要用的配置文件
|
|
|
String cmd = "sed -i 's|ENV WORKING_ENV=\"pro\"|ENV WORKING_ENV=\"" + choiceConfig + "\"|g' " + remoteDockerFile;
|
|
|
ssh.exec(cmd);
|
|
|
System.out.println(cmd);
|
|
|
System.out.println("环境变量修改成功!");
|
|
|
|
|
|
//如果容器正在运行中,那么容器对应的镜像删除失败
|
|
|
cmd = "docker rm -f $(docker ps -aq)";
|
|
|
ssh.exec(cmd);
|
|
|
|
|
|
//删除所有镜像
|
|
|
cmd = "docker rmi -f $(docker images -q)";
|
|
|
ssh.exec(cmd);
|
|
|
|
|
|
//打包
|
|
|
System.out.println("开始打包镜像,稍等....");
|
|
|
cmd = "cd /usr/local/" + projectName + " && docker build -t " + projectName + ":" + newVersion + " .";
|
|
|
System.out.println(cmd);
|
|
|
ssh.exec(cmd);
|
|
|
|
|
|
//获取最新打包后的镜像ID
|
|
|
cmd = "docker images --format \"{{.Repository}} {{.Tag}} {{.ID}} {{.CreatedAt}}\" | grep " + projectName + " | head -n1 | awk '{print $3}'";
|
|
|
Kv kv = ssh.exec(cmd);
|
|
|
String imageId = kv.getStr("message").replace("[", "").replace("]", "");
|
|
|
System.out.println("镜像打包完成,镜像ID=" + imageId);
|
|
|
|
|
|
if (StrKit.isBlank(imageId)) {
|
|
|
System.out.println("镜像ID生成异常,请检查后再试!");
|
|
|
System.exit(0);
|
|
|
}
|
|
|
|
|
|
if (choiceWarehouse.get("id") != null) {
|
|
|
System.out.println("打标签...");
|
|
|
cmd = "docker tag " + imageId + " registry.cn-hangzhou.aliyuncs.com/" + choiceWarehouse.getStr("name") + "/" + projectName + ":" + newVersion;
|
|
|
ssh.exec(cmd);
|
|
|
|
|
|
System.out.println("开始推送到远程仓库,稍等...");
|
|
|
cmd = "docker push registry.cn-hangzhou.aliyuncs.com/" + choiceWarehouse.getStr("name") + "/" + projectName + ":" + newVersion;
|
|
|
System.out.println(cmd);
|
|
|
ssh.exec(cmd);
|
|
|
System.out.println("推送到远程仓库完成!");
|
|
|
}
|
|
|
|
|
|
ssh.disconnect();
|
|
|
System.out.println("恭喜,镜像打包成功!");
|
|
|
|
|
|
Kv ret = Kv.by("projectName", projectName + ":" + newVersion);
|
|
|
ret.set("project_port", project_port);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 功能:获取项目列表
|
|
|
*
|
|
|
* @return
|
|
|
* @throws IOException
|
|
|
*/
|
|
|
public static List<String> getProjectNames() throws IOException {
|
|
|
String basedir = new File(System.getProperty("user.dir")).getCanonicalPath();
|
|
|
//获取到的值: D:\dsWork\dsProject
|
|
|
//在这个目录下遍历所有的子目录,并且,需要以ds开头,而且,不能是dsBuild目录
|
|
|
File directory = new File(basedir);
|
|
|
List<String> dirList = new ArrayList<>();
|
|
|
// 获取目录下的所有文件和子目录
|
|
|
File[] files = directory.listFiles();
|
|
|
if (files != null) {
|
|
|
for (File file : files) {
|
|
|
// 确保是目录
|
|
|
if (file.isDirectory()) {
|
|
|
// 获取目录名称
|
|
|
String dirName = file.getName();
|
|
|
// 检查目录名称是否以"ds"开头且不是"dsBuild"
|
|
|
if (dirName.startsWith("ds") && !dirName.equals("dsBuild") && !dirName.contains("-")) {
|
|
|
dirList.add(file.getAbsolutePath());
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return dirList;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 功能:获取项目名称
|
|
|
*
|
|
|
* @param pathName 路径名称
|
|
|
* @return 项目名称
|
|
|
*/
|
|
|
public static String getPublishProjectName(String pathName) {
|
|
|
|
|
|
//遍历 pathName的每个字符,如果是不是大写字母,就照抄下来,如果是大写字母,就变成小写字母,并且在小写字母前面加上下划线_
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
for (int i = 0; i < pathName.length(); i++) {
|
|
|
char c = pathName.charAt(i);
|
|
|
if (Character.isUpperCase(c)) {
|
|
|
sb.append("_").append(Character.toLowerCase(c));
|
|
|
} else {
|
|
|
sb.append(c);
|
|
|
}
|
|
|
}
|
|
|
return sb.toString();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 功能:获取项目属性,1:JAVA后端,2:WEB前端
|
|
|
*
|
|
|
* @return
|
|
|
*/
|
|
|
public static String getProjectAttribute(int id, List<ProjectBean> listProject) throws IOException {
|
|
|
for (ProjectBean projectBean : listProject) {
|
|
|
if (projectBean.getId() == id) {
|
|
|
String basedir = new File(System.getProperty("user.dir")).getCanonicalPath();
|
|
|
String path = basedir + "\\" + projectBean.getDevProjectName();
|
|
|
//这个path目录下有没有src这样的子目录,如果有,则判定为JAVA项目,否则为WEB项目
|
|
|
if (new File(path + "\\src").exists()) {
|
|
|
return "JAVA";
|
|
|
} else {
|
|
|
return "WEB";
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 功能:提示用户选择项目,并返回项目编号
|
|
|
*/
|
|
|
public static int userSelectProject(List<ProjectBean> listProject) {
|
|
|
Scanner scanner = new Scanner(System.in); // 创建Scanner对象来读取控制台输入
|
|
|
int choice;
|
|
|
while (true) {
|
|
|
redPrint("1、请选择您本次要发布的项目编号:");
|
|
|
for (ProjectBean projectBean : listProject) {
|
|
|
System.out.print(projectBean.getId() + ":" + projectBean.getDevProjectName() + " ");
|
|
|
}
|
|
|
System.out.println();
|
|
|
choice = scanner.nextInt(); // 读取用户输入的整数
|
|
|
//输出choice对应的项目名称
|
|
|
for (ProjectBean projectBean : listProject) {
|
|
|
if (projectBean.getId() == choice) {
|
|
|
System.out.println("您选择的项目是:" + projectBean.getDevProjectName());
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
if (choice >= 1 && choice <= listProject.size()) {
|
|
|
break; // 如果输入正确,退出循环
|
|
|
} else {
|
|
|
System.out.println("输入不正确,选择一个大于等于1,小于等于" + listProject.size() + "的数字!"); // 提示用户输入不正确
|
|
|
}
|
|
|
}
|
|
|
return choice;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 功能:用户选择发布的仓库
|
|
|
*
|
|
|
* @return
|
|
|
*/
|
|
|
public static Kv userSelectWarehouse() {
|
|
|
Scanner scanner = new Scanner(System.in); // 创建Scanner对象来读取控制台输入
|
|
|
//输出choice对应的项目名称
|
|
|
Kv kv = Kv.create();
|
|
|
while (true) {
|
|
|
redPrint("2、请选择您本次要发布的仓库:");
|
|
|
System.out.println("1:不上传到仓库 2:黄海私人仓库 3:驿来特仓库");
|
|
|
int choice = scanner.nextInt(); // 读取用户输入的整数
|
|
|
if (choice == 1) {
|
|
|
kv.set("id", null);
|
|
|
kv.set("name", null);
|
|
|
kv.set("username", null);
|
|
|
kv.set("password", null);
|
|
|
System.out.println("您选择的仓库是:不上传到仓库");
|
|
|
}
|
|
|
if (choice == 2) {
|
|
|
kv.set("id", 2);
|
|
|
kv.set("name", "dsideal");
|
|
|
kv.set("username", "东师黄海");
|
|
|
kv.set("password", "DsideaL4r5t6y7u");
|
|
|
System.out.println("您选择的仓库是:黄海私人仓库");
|
|
|
}
|
|
|
if (choice == 3) {
|
|
|
kv.set("id", 3);
|
|
|
kv.set("name", "yltcharge");
|
|
|
kv.set("username", "驿来特充电");
|
|
|
kv.set("password", "ylt5033.");
|
|
|
System.out.println("您选择的仓库是:驿来特仓库");
|
|
|
}
|
|
|
|
|
|
if (choice == 1 || choice == 2 || choice == 3) {
|
|
|
break; // 如果输入正确,退出循环
|
|
|
} else {
|
|
|
System.out.println("输入不正确,请输入1, 2 或者 3 !"); // 提示用户输入不正确
|
|
|
}
|
|
|
}
|
|
|
return kv;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 功能:让用户选择使用哪个配置文件
|
|
|
*
|
|
|
* @param listProject
|
|
|
* @param choiceProject
|
|
|
* @return
|
|
|
* @throws IOException
|
|
|
*/
|
|
|
public static String userSelectConfig(List<ProjectBean> listProject, int choiceProject) throws IOException {
|
|
|
//输出choice对应的项目名称
|
|
|
List<String> setConfigFiles = new ArrayList<>();
|
|
|
for (ProjectBean projectBean : listProject) {
|
|
|
if (projectBean.getId() == choiceProject) {
|
|
|
System.out.println("您选择的项目是:" + projectBean.getDevProjectName());
|
|
|
String basedir = new File(System.getProperty("user.dir")).getCanonicalPath() + "\\";
|
|
|
String directoryPath = basedir + projectBean.getDevProjectName() + "\\src\\main\\resources\\";
|
|
|
// 创建一个 File 对象,代表目录
|
|
|
File directory = new File(directoryPath);
|
|
|
// 获取目录下的所有文件和子目录
|
|
|
File[] files = directory.listFiles();
|
|
|
// 定义一个 FilenameFilter 来过滤文件
|
|
|
FilenameFilter filter = (dir, name) -> {
|
|
|
// 检查是否是文件以及文件名是否匹配 application_?.yaml 模式
|
|
|
return new File(dir, name).isFile() && name.matches("application_.*\\.yaml");
|
|
|
};
|
|
|
|
|
|
if (files != null) {
|
|
|
// 过滤出符合条件的文件
|
|
|
for (File file : files) {
|
|
|
if (filter.accept(directory, file.getName())) {
|
|
|
setConfigFiles.add(file.getName());
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
System.out.println("项目" + projectBean.getDevProjectName() + "没有配置文件");
|
|
|
return "";
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
Scanner scanner = new Scanner(System.in); // 创建Scanner对象来读取控制台输入
|
|
|
//输出choice对应的项目名称
|
|
|
while (true) {
|
|
|
redPrint("3、请选择您本次要发布的配置文件:");
|
|
|
for (int i = 0; i < setConfigFiles.size(); i++) {
|
|
|
System.out.print((i + 1) + ":" + setConfigFiles.get(i) + " ");
|
|
|
}
|
|
|
System.out.println();
|
|
|
|
|
|
int choice = scanner.nextInt(); // 读取用户输入的整数
|
|
|
if (choice >= 1 && choice <= setConfigFiles.size()) {
|
|
|
return setConfigFiles.get(choice - 1).replace("application_", "").replace(".yaml", "");
|
|
|
} else {
|
|
|
System.out.println("输入不正确,请输入1与 " + setConfigFiles.size() + "之间的数值!"); // 提示用户输入不正确
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|