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. *

* # 搜索 * docker search registry.cn-hangzhou.aliyuncs.com/yltcharge/zhu-que:20240903 * #拉取 * docker pull registry.cn-hangzhou.aliyuncs.com/yltcharge/zhu-que:20240903 *

* 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 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 getProjectNames() throws IOException { String basedir = new File(System.getProperty("user.dir")).getCanonicalPath(); //获取到的值: D:\dsWork\dsProject //在这个目录下遍历所有的子目录,并且,需要以ds开头,而且,不能是dsBuild目录 File directory = new File(basedir); List 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 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 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 listProject, int choiceProject) throws IOException { //输出choice对应的项目名称 List 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() + "之间的数值!"); // 提示用户输入不正确 } } } }