You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

415 lines
17 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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();
}
/**
* 功能获取项目属性1JAVA后端2WEB前端
*
* @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() + "之间的数值!"); // 提示用户输入不正确
}
}
}
}