# ajida **Repository Path**: wmjtyd_admin/ajida ## Basic Information - **Project Name**: ajida - **Description**: 自动化部署工具,特点是非常小,可以很方便的放入工程代码里,无侵入,可以随代码上线。用于发布java应用、多节点部署热启动、切换nginx配置、发布静态页面、发布前端工程都可以。发布流程可以代码编写,可以直接从maven工程代码编译->打包->替换配置->过滤文件->表达式替换文件内容->压缩打包->上传服务器->服务器操作->启动应用,完整流程。 - **Primary Language**: Java - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2023-07-26 - **Last Updated**: 2024-02-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ajida #### 介绍 提供Window和linux下的远程和本地执行系统命令能力 旧版本提供: 自动化部署工具,特点是非常小,可以很方便的放入工程代码里,无侵入,可以随代码上线。用于发布java应用、多节点部署热启动、切换nginx配置、发布静态页面、发布前端工程都可以。发布流程可以代码编写,可以直接从maven工程代码编译->打包->替换配置->过滤文件->表达式替换文件内容->压缩打包->上传服务器->服务器操作->启动应用,完整流程。 #### 安装教程 1. git clone 或者 下载源码到本地 2. 进入到pom.xml文件所在目录,mvn install安装到本地maven仓库 3. 在项目中添加如下maven依赖 ```xml com.ajida ajida 19.8.10 ``` #### 使用说明(*如下出现xxx都表示您自己的工程名称) 1. 在工程下添加config目录 ![avatar][folder_png] 2. 如果研发本地直接更新Java工程至测试环境(线上同理) ```java public static void script(String ip,String even,String password){ try { LogUtil.log("connect to "+ip); if(StringUtil.isEmpty(password)){ LogUtil.log(">>please enter password:"); Scanner sc = new Scanner(System.in); password = sc.nextLine(); sc.close(); } //ssh连接配置 SSHConfig sshConfig = new SSHConfig(ip, "root", password); AxeAppConfig appConfig1 = new AxeAppConfig("com.xxx.JettyStart 8081"); appConfig1.addConfigParams("proxy_pass", "http://localhost:8081/"); AxeAppConfig appConfig2 = new AxeAppConfig("com.xxx.JettyStart 8082"); appConfig2.addConfigParams("proxy_pass", "http://localhost:8082/"); //Ajida.axeProjectUpdate(even, appConfig1, appConfig2, new String[]{}, sshConfig, "/usr/local/xxx"); Ajida.axeProjectUpdate(false,true,even, appConfig1, appConfig2, new String[]{}, sshConfig, "/usr/local/xxx"); if("test".equals(even)){ //每次更新完都设置下系统时间 SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); Connection connect = SSHUtil.connect(sshConfig); SSHUtil.exec(connect, "date -s "+sdf.format(new Date()), 10, false); } } catch (Exception e) { LogUtil.error(e); } } ``` ```java public static void main(String[] args) { script("192.168.199.45", "test", "rootPassword"); // script("218.90.120.43", "pro","rootPassword"); } ``` 3. 如果更新静态前端工程到测试环境(线上同理) ```java public static void script(String ip,String even,String password){ try { LogUtil.log("connect to "+ip); if(StringUtil.isEmpty(password)){ LogUtil.log(">>please enter password:"); Scanner sc = new Scanner(System.in); password = sc.nextLine(); sc.close(); } //ssh连接配置 SSHConfig sshConfig = new SSHConfig(ip, "root", password); String version = StringUtil.getRandomString(4,"0123456789"); Ajida.htmlProjectUpdate(true,even, sshConfig, "D:\\project_folder", "/usr/local/xxx", new String[]{ "/index.html", "/login.html", "/js/router.js" },new String[]{ "\\.js:.js?v=ajida_"+version, "\\.css:.css?v=ajida_"+version, "\\.html:.html?v=ajida_"+version }); if("test".equals(even)){ //每次更新完都设置下系统时间 SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); Connection connect = SSHUtil.connect(sshConfig); SSHUtil.exec(connect, "date -s "+sdf.format(new Date()), 10, false); } } catch (Exception e) { LogUtil.error(e); } } ``` ```java public static void main(String[] args) { script("192.168.199.45", "test","rootPassword"); // script("218.90.120.43", "pro","rootPassword"); } ``` 4. 如果是单节点java工程(比如嵌入式设备),可以这样更新到测试环境(线上同理) ```java public static void fullProjectUpdate(String even,AxeAppConfig appConfig,String version,String ip,int port,String password,String distDir,String appName){ try { System.out.println("connect to "+ip); if(StringUtil.isEmpty(password)){ System.out.println(">>please enter password:"); Scanner sc = new Scanner(System.in); password = sc.nextLine(); sc.close(); } //ssh连接配置 SSHConfig sshConfig = new SSHConfig(ip, "root", password,port); commonConfig(appConfig,distDir, appName); Ajida4SinglePoint.axeFullProjectUpdate(false,even, appConfig, new String[]{}, sshConfig, distDir,new String[]{ //排除掉 "windows", "ios", "android", "macosx", "linux-x86", "linux-ppc64le", "artoolkitplus", "flycapture", }); //清空log Connection connect = SSHUtil.connect(sshConfig); SSHUtil.exec(connect, "rm -rf "+distDir+"/"+appName+"/log.txt", 10, false); //每次更新完都设置下系统时间 SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); SSHUtil.exec(connect, "date -s "+sdf.format(new Date()), 10, false); //重启 SSHUtil.exec(connect, "reboot", 10, false); } catch (Exception e) { LogUtil.error(e); } } //统一的配置 public static void commonConfig(AxeAppConfig appConfig,String distDir,String appName){ //不需每次改的 appConfig.addConfigParams("version", this.version);//版本号 appConfig.addConfigParams("dist_dir", distDir); appConfig.addConfigParams("app_dir", distDir+"/"+appName); } ``` ```java public static void main(String[] args) { AxeAppConfig config = getConfig(2, 1); TestUpdateCodeScript.fullProjectUpdate("test", config,TestUpdateCodeScript.version, "192.168.100.100", 22, "rootPassword", "/usr/local","xxx"); } public static AxeAppConfig getConfig(int boxNumber,int boxIndexStart){ AxeAppConfig appConfig = new AxeAppConfig("com.xxx.JettyStart 80"); appConfig.addConfigParams("box_number", String.valueOf(boxNumber)); appConfig.addConfigParams("box_index_start", String.valueOf(boxIndexStart)); return appConfig; } ``` 5. 如果不希望全量更新,只要部分更新,速度更快一些,特别是嵌入式设备消耗流量更需要减少更新包大小。 ```java //统一的配置 public static void commonConfig(AxeAppConfig appConfig,String distDir,String appName){ //不需每次改的 appConfig.addConfigParams("version", version);//版本号 appConfig.addConfigParams("dist_dir", distDir); appConfig.addConfigParams("app_dir", distDir+"/"+appName); appConfig.addConfigParams("tunnel_install_dir", distDir); appConfig.addConfigParams("_URL", "http://127.0.0.1/");//html的api请求地址配置 } public static void smallProjectUpdate(AxeAppConfig appConfig,boolean rebuild,String even,String[] fileContains,String ip,int port,String password,String distDir,String appName){ try { System.out.println("connect to "+ip); if(StringUtil.isEmpty(password)){ System.out.println(">>please enter password:"); Scanner sc = new Scanner(System.in); password = sc.nextLine(); sc.close(); } //ssh连接配置 SSHConfig sshConfig = new SSHConfig(ip, "root", password,port); commonConfig(appConfig,distDir, appName); //Build if(rebuild){ boolean needGitPull = false; Ajida4SinglePoint.axeBuildProject(needGitPull, even, appConfig, new String[]{}); } //压缩,只是部分压缩 String zipName = Ajida4SinglePoint.axeCompressBuild(fileContains, null); //上传并更新 Ajida4SinglePoint.axeUploadZip(sshConfig, zipName, distDir, false); //清空log Connection connect = SSHUtil.connect(sshConfig); SSHUtil.exec(connect, "rm -rf "+distDir+"/"+appName+"/log.txt", 10, false); //每次更新完都设置下系统时间 SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); SSHUtil.exec(connect, "date -s "+sdf.format(new Date()), 10, false); //重启 SSHUtil.exec(connect, "reboot", 10, false); } catch (Exception e) { LogUtil.error(e); } } ``` ```java public static void smallUpdate(){ String[] updateFiles = { "xxx.jar", "sys.properties" }; AxeAppConfig config = getConfig(2, 1); TestUpdateCodeScript.smallProjectUpdate(config, true, "pro", updateFiles, "218.90.120.43", 22, "rootPassword", "/usr/local","xxx"); } public static AxeAppConfig getConfig(int boxNumber,int boxIndexStart){ AxeAppConfig appConfig = new AxeAppConfig("com.xxx.JettyStart 80"); appConfig.addConfigParams("box_number", String.valueOf(boxNumber)); appConfig.addConfigParams("box_index_start", String.valueOf(boxIndexStart)); return appConfig; } ``` #### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request [folder_png]:data:image/png;base64,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