node实现自动化部署

node实现自动化部署_第1张图片

 首先看一下需要什么依赖?

{
  "name": "zdpl",
  "version": "1.0.1",
  "description": "自动部署",
  "main": "./src/index.js",
  "bin": {
    "zc-deploy": "./src/index.js"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "deploy"
  ],
  "publicConfig": {
    "registry": "https://registry.npmjs.org/"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "chalk": "^4.1.0",
    "inquirer": "^8.0.0",
    "node-ssh": "^11.1.1",
    "ora": "^5.3.0",
    "shelljs": "^0.8.5",
    "zip-local": "^0.3.5"
  }
}

脚本的目录结构如下:

 node实现自动化部署_第2张图片

 

1. chalk插件主要用于改变终端输出文字的样式

2. inquirer 用户交互命令行

3. node-ssh        connect 用于用户连接服务器,exec 用于执行命令, putFIle用于推送文件到服务器指定位置。

4. ora 用于等待某个指令执行,类似loading效果

5. shelljs 是基于node.js的Unix shell命令的可移植实现。类似shell脚本可以写一下指令,然后执行。shelljs就是可以在js通过其中方法执行指令,在运行代码时依次执行。

6. zip-local用于压缩文件,用法查看npm官网

7. download-git-repo用于从github,gitlab等上拉取摸板。

8. commander 这里主要用来生成不同的指令,zc-deploy init 和 zc-deploy push

主要代码如下

需要在代码中配置deploy.config.ts 文件,使用zc-deploy init 初始化摸板。也可以自己配置,配置格式如下,需要知道公司项目发布的服务器ip,在此文件中声明即可。在执行yarn deploy时,会出现如下图选择部署的服务器。

node实现自动化部署_第3张图片  

module.exports = Object.freeze({
  PRIVATE_KEY: 'C:/Us/.ssh/id_rsa', // 密钥地址
  SERVER: [
    {
      NAME: 'server-501',
      SERVER_PATH: '39.98.82.82',
      SSH_USER: 'root', // 服务器用户名
      SSH_KEY: ``, // 服务器密码
      PORT: 22, // 端口 默认为22
      DIST: './dist', // 需要部署的打包过后的文件夹 根据项目不同值不同 一般为 build static 或者dist
      SCRIPT: 'yarn build:prod', // 打包命令 可能项目由不同的构建命令 如打包指定环境的代码
      PATH: '/root/501', // 服务器存放静态文件目录
    },
    {
      NAME: 'server-506',
      SERVER_PATH: '192.168.117.133',
      SSH_USER: 'root', // 服务器用户名
      SSH_KEY: 'root', // 服务器密码
      PORT: 22, // 端口 默认为22
      DIST: './dist', // 需要部署的打包过后的文件夹 根据项目不同值不同 一般为 build static 或者dist
      SCRIPT: 'yarn build:prod', // 打包命令 可能项目由不同的构建命令 如打包指定环境的代码
      PATH: '/root/506', // 服务器存放静态文件目录
    },
    {
      NAME: 'server-603',
      SERVER_PATH: '192.168.117.133',
      SSH_USER: 'root', // 服务器用户名
      SSH_KEY: 'root', // 服务器密码
      PORT: 22, // 端口 默认为22
      DIST: './dist', // 需要部署的打包过后的文件夹 根据项目不同值不同 一般为 build static 或者dist
      SCRIPT: 'yarn build:prod', // 打包命令 可能项目由不同的构建命令 如打包指定环境的代码
      PATH: '/root/603', // 服务器存放静态文件目录
    },
  ],
});

src/index.js:这是全局的入口。主要配置了两个命令:zc-deploy init 和zc-deploy push 

#!/usr/bin/env node


program
  .version(packageJson.version, "-v, --version")
  .command("init")
  .description("初始化部署相关配置")
  .action(() => {
    require("../lib/init")();
  });

program
  .version(packageJson.version)
  .command("push")
  .description("部署项目")
  .action(async () => {
    if (fs.existsSync(path.resolve(process.cwd(), "./deploy.config.ts"))) {
      const CONFIG = require(path.resolve(process.cwd(), "./deploy.config.ts"));

      const serverConfig = [...CONFIG.SERVER];
      const answers = await inquirer.prompt([
        {
          type: "confirm",
          name: "hasBuild",
          message: "是否需要打包",
        },
      ]);
      let hasBuild = answers.hasBuild ? true : !existsSync(distZipPath);
      const serverName = await inquirer.prompt([
        {
          type: "list",
          name: "server_name",
          message: "请选择部署的服务器",
          choices: serverConfig.map((item) => item.NAME),
        },
      ]);
      runTask(hasBuild, serverName, serverConfig, CONFIG);
    } else {
      defaultLog(`缺少部署相关的配置,请运行"zc-deploy init"下载部署配置`);
    }
  });

lib/deploy.js:这个文件是,项目的部署流程文件。

#!/usr/bin/env node

const distZipPath = resolve(process.cwd(), "./dist.zip");

// 打包 npm run build 或yarn build
const compileDist = async (CURCONFIG) => {
  // 进入本地文件夹
  cd(process.cwd(), "./");
  exec(CURCONFIG.SCRIPT || "yarn build:prod");
  success("打包完成");
};

// ******压缩dist文件******

const zipDist = async (CURCONFIG) => {
  try {
    if (existsSync(distZipPath)) {
      defaultLog("dist.zip已经存在,即将删除压缩包");
      unlinkSync(distZipPath);
    } else {
      defaultLog("即将开始压缩zip文件");
    }
    let spinner = ora("文件开始压缩").start();

    sync
      .zip(path.resolve(process.cwd(), CURCONFIG.DIST || "./dist"))
      .compress()
      .save(distZipPath);
    spinner.succeed("文件压缩成功");
  } catch (error) {
    errorLog(error);
    errorLog("压缩dist文件失败");
  }
};

// ********执行清空线上文件夹指令**********
const runCommond = async (commond, CONFIG) => {
  const result = await SSH.exec(commond, []);
  console.log(warning(result));
};
// ********* 执行清空线上文件夹指令 *********
const runBeforeCommand = async (CONFIG) => {
  let spinner = ora("正在删除服务器文件").start();
  await runCommond(`rm -rf ${CONFIG.PATH}/dist.zip`);
  await runCommond(`rm -rf ${CONFIG.PATH}/dist`);
  spinner.succeed("删除服务器文件成功");
};

// 通过ssh上传文件服务器
const uploadZipBySSH = async (CONFIG) => {
  // 上传文件
  let spinner = ora("准备上传文件").start();
  try {
    await SSH.putFile(distZipPath, CONFIG.PATH + "/dist.zip");
    // await SSH.putFile(distZipPath, '/root/dist.zip');
    spinner.succeed("文件上传服务器成功");
    let spinner1 = ora("完成上传,开始解压").start();
    await runCommond(`unzip ${CONFIG.PATH}"/dist.zip" -d ${CONFIG.PATH}/dist`);
    spinner1.succeed("文件解压成功");
    success(`${CONFIG.NAME}部署完成`);
    process.exit(0);
  } catch (err) {
    errorLog(err);
    errorLog("上传失败");
  }
};

const getServerConfig = (serverName, serverConfig) => {
  return serverConfig.find((item) => {
    return item.NAME == serverName;
  });
};

// 连接ssh
const connectSSH = async (CURCONFIG, CONFIG) => {
  defaultLog(`尝试连接服务:${CURCONFIG.SERVER_PATH}`);
  defaultLog(`密钥路径${CONFIG.PRIVATE_KEY}`);
  let spinner = ora("正在连接").start();
  try {
    SSH.connect({
      host: CURCONFIG.SERVER_PATH,
      username: CURCONFIG.SSH_USER,
      password: CURCONFIG.SSH_KEY,
      privateKey: Buffer.from(CONFIG.PRIVATE_KEY).toString() ?? "",
      port: CURCONFIG.PORT || 22,
    }).then(async () => {
      // exec(`sh ${connect} ${CURCONFIG.SERVER_PATH} ${CURCONFIG.PATH}`);
      spinner.succeed("服务器连接成功");
      await runBeforeCommand(CURCONFIG);
      await uploadZipBySSH(CURCONFIG);
    });
  } catch (err) {
    errorLog(err);
    errorLog("ssh连接失败");
  }
};

async function runTask(hasBuild, serverName, serverConfig, CONFIG) {
  const CURCONFIG = getServerConfig(serverName.server_name, serverConfig);
  if (hasBuild) {
    await compileDist(CURCONFIG);
  }
  await zipDist(CURCONFIG);
  await connectSSH(CURCONFIG, CONFIG);
}

module.exports = runTask;

 lib/init.js  :这个文件是初始化配置文件,生成一系列配置信息(deploy.config.ts)

const deployTem = "github:success-c/deploy-template";
const checkDeployExists = () => {
  if (fs.existsSync(deployConfigPath)) {
    defaultLog("根目录下已经存在deploy.config.ts文件,请勿重新下载");
    process.exit(1);
  }
  downloadAndGenerate(deployTem);
};

const downloadAndGenerate = (templateURL) => {
  const spinner = ora("开始生成部署模板");
  spinner.start();
  download(templateURL, process.cwd(), { clone: false }, (err) => {
    if (err) {
      errorLog(err);
      process.exit(1);
    }
    spinner.stop();
    success("模板下载成功,模板位置:/deploy.config.js");
    defaultLog("请配置根目录下的deploy.config.js配置文件");
    console.log("注意:请删除不必要的环境配置");
    process.exit(0);
  });
};
module.exports = () => {
  checkDeployExists();
};

lib/utils : 打印信息的方法

const chalk = require("chalk");
const { red, blue, green, yellow } = chalk;
const errorLog = (error) => console.log(red(`=========>${error}`));
const defaultLog = (log) => console.log(blue(`=========>${log}`));
const success = (log) => console.log(green(`=========>${log}`));
const warning = (log) => console.log(yellow(`${log}`));

module.exports = {
  errorLog,
  defaultLog,
  success,
  warning,
};

 

源码地址: 

deploy: node脚本实现自动化部署https://gitee.com/liu--zicheng/deploy.git

你可能感兴趣的:(自动化,运维)