likes
comments
collection
share

基于 node-ssh 开发前端部署脚本

作者站长头像
站长
· 阅读数 14

前言

当在前端开发中涉及部署时,自动化工具和脚本可以大大简化和改善部署流程。node.js 的 node-ssh 库为开发人员提供了一种便捷的方式来执行远程服务器上的 ssh 操作,使得前端部署脚本的开发变得更加高效。本文将介绍如何使用 node-ssh 开发前端部署脚本,以简化部署流程并提高效率。

什么是 node-ssh?

node-ssh 是一个 node.js 库,用于在 node.js 环境中执行 ssh 操作。它为开发人员提供了通过 ssh 协议连接远程服务器的功能,可以执行诸如上传文件、执行命令等操作。

初始化项目

确定结构

创建项目目录,并使用 npm init 或者 yarn init 初始化项目。

ssh-publish/
  ├── config.js    # 配置文件
  ├── index.js     # 入口文件
  ├── package.json
  └── ...

安装插件

注意 inquirer v9 使用了 esm 模块,如果使用 commonjs 需要使用 v8 版本。

yarn add node-ssh commander inquirer@^8.0.0 glob chalk

编写脚本

配置文件

// config.js
module.exports = {
  option: {
    dev: {
      remoteDir: "服务器项目目录",
      host: "服务器 ip 地址",
      port: "端口",
      username: "服务器用户名",
      passdword: "服务器密码",
      localDir: "本地项目目录",
    },
  },
};

获取配置信息

// index.js
const process = require("process"); 
const dirArr = glob.sync(`${process.cwd()}/config.js`); // 加载工作目录的配置文件

const getConf = (types = [], config = []) => {
  if (dirArr.length) {
    const src = dirArr[0];
    const { option } = require(src);
    if (option) {
      config = option;
      types = Object.keys(option);
    }
  }
  return { types, config };
};
const { types, config } = getConf();

命令行交互代码

// index.js
const { program } = require("commander"); 
const inquirer = require("inquirer"); 

program
  .command("publish")
  .description("发布项目")
  .action(() => {
    (async () => {
      const { devType } = await inquirer.prompt([
        {
          name: "devType",
          type: "list",
          message: `请选择发布的版本`,
          choices: "版本列表",
          default: "默认选择的版本",
        },
      ]);  
      // 获取选中的配置信息
      const selectConfig = config[devType];
      // ssh 连接并上传文件
      ...
    })();
  });

ssh 操作代码

// index.js
ssh
    .connect({
      host: selectConfig.host,
      username: selectConfig.username,
      port: selectConfig.port,
      password: selectConfig.passdword,
      tryKeyboard: true,
    })
    .then(() => {
      // 连接成功开始上传文件
      ssh
        .putDirectory(selectConfig.localDir, selectConfig.remoteDir, {
          // 文件上传回调
          tick: (localPath, remotePath, error) => {},
        })
        .then(() => {
         // 上传成功
        });
    });

增加检查配置和美化输出的函数

// index.js
const process = require("process"); 
const chalk = require("chalk"); /
const dirArr = glob.sync(`${process.cwd()}/config.js`); 

// chalk 输出函数
const log = (str, color = "white") => {
  console.log(chalk[color](str));
};

// 检查配置文件
const checkConf = (flag = true) => {
  if (!dirArr.length) {
    log(`${process.cwd()} 中没有 config.js`, "red");
    return;
  }
  const target = [
    "remoteDir",
    "host",
    "port",
    "username",
    "passdword",
    "localDir",
  ];
  if (config.length === 0) {
    log("配置文件中缺少参数 options", "red");
    return;
  }
  for (let item in config) {
    const arr = Object.keys(config[item]);
    target.forEach((e) => {
      if (arr.indexOf(e) == -1) {
        log(`${item} 中缺少参数 ${e}`, "red");
        flag = false;
      }
    });
  }
  return flag;
};

index.js 完整代码

#!/usr/bin/env node
const { program } = require("commander"); 
const chalk = require("chalk"); 
const inquirer = require("inquirer");
const process = require("process");
const glob = require("glob");
const { NodeSSH } = require("node-ssh");
const ssh = new NodeSSH();
const dirArr = glob.sync(`${process.cwd()}/config.js`); 

// 打印函数
const log = (str, color = "white") => {
  console.log(chalk[color](str));
};

// 获取配置信息
const getConf = (types = [], config = []) => {
  if (dirArr.length) {
    const src = dirArr[0];
    const { option } = require(src);
    if (option) {
      config = option;
      types = Object.keys(option);
    }
  }
  return { types, config };
};
const { types, config } = getConf();

// 检查配置文件
const checkConf = (flag = true) => {
  if (!dirArr.length) {
    log(`${process.cwd()} 中没有 config.js`, "red");
    return;
  }
  const target = [
    "remoteDir",
    "host",
    "port",
    "username",
    "passdword",
    "localDir",
  ];
  if (config.length === 0) {
    log("配置文件中缺少参数 options", "red");
    return;
  }
  for (let item in config) {
    const arr = Object.keys(config[item]);
    target.forEach((e) => {
      if (arr.indexOf(e) == -1) {
        log(`${item} 中缺少参数 ${e}`, "red");
        flag = false;
      }
    });
  }
  return flag;
};

// 定义当前版本,通过 command 设置 -v 和 --version 参数输出版本号
const package = require("./package.json");
program.option("-v, --version").action(() => {
  console.log(`v${package.version}`);
});

// publish 操作
program
  .command("publish")
  .description("发布项目")
  .action(() => {
    if (!checkConf()) return;
    (async () => {
      const { devType } = await inquirer.prompt([
        {
          name: "devType",
          type: "list",
          message: `请选择发布的版本`,
          choices: types.filter((e) => {
            return { name: e, value: e };
          }),
          default: types[0],
        },
      ]);
      const selectConfig = config[devType];
      log(`配置: ${JSON.stringify(selectConfig, null, 2)}`); 
      //ssh连接服务器
      ssh
        .connect({
          host: selectConfig.host,
          username: selectConfig.username,
          port: selectConfig.port || 22,
          password: selectConfig.passdword,
          tryKeyboard: true,
        })
        .then(() => {
          log(`连接服务器成功`, "green");
          log(`开始上传文件`, "green");
          // 上传文件
          ssh
            .putDirectory(selectConfig.localDir, selectConfig.remoteDir, {
              // 文件上传回调
              tick: (localPath, remotePath, error) => {
                if (error) {
                  log(error, "red");
                } else {
                  log(`传输:${localPath}  -> ${remotePath}`, "green");
                }
              },
            })
            .then(() => {
              log(`✨ 上传成功`, "green");
              process.exit(0);
            });
        });
    })();
  });

// 注册 -h
program.on("--help", () => {});

// 解析用户执行命令传入参数
program.parse(process.argv);

开发完成后可以发布到 npm 仓库或者直接在项目中封装脚本。

总结

node-ssh 可以帮助开发人员简化前端部署流程。通过使用它,你可以编写自定义的部署脚本,以自动化部署任务,提高效率并减少人为错误。