记录一次自动化部署项目流程

42 阅读1分钟

自动化部署核心SSH: 主要是通过node-ssh 进行 SSH 连接并执行命令,上传文件夹,完成部署工作

// 引入对应模块
import { execSync } from "child_process";
import fs from "fs";
import path from "path";
import inquirer from "inquirer";
import { NodeSSH } from "node-ssh";
import Client from "ssh2-sftp-client";
const __dirname = path.resolve();

// 配置信息
let serverHost = ""; // 服务器ip地址
const serverUser = "root";
const localDistPath = path.join(__dirname, "");  // 打包后的文件夹路径
const remoteDistPath = ""; // 服务器上的目标路径
let password = "";// 服务器秘密


// 编辑器里交互方法 使用 inquirer
inquirer
  .prompt([
    {
      type: "list",
      message: "请选择需要操作的服务环境",
      name: "environment",
      choices: ["测试", "生产"],
    },
  ])
  .then(({ environment }) => {
    if (environment === "生产") { //生产需要更换默认服务器信息
      serverHost = ""; 
	    password = "";
    }
	  executeRemoteCommand();
  });


// 开始上传包
async function pushDist() {
  try {
    const exists = fs.existsSync(localDistPath);
    if (!exists) {
      return console.log("dist文件夹不存在,请确保已经打包完毕!! ");
    }
    const sftp = new Client();

    try {
      await sftp.connect({
        host: serverHost,
        username: serverUser,
        password,
      });
      // 上传dist目录
	  console.log(`文件上传中,请稍等~`);
	  await sftp.uploadDir(localDistPath, remoteDistPath); 
	  // 递归遍历文件夹进行文件上传
      console.log(`文件已上传到 ${remoteDistPath}`);
    } catch (err) {
      console.error("文件上传失败:", err);
    } finally {
      await sftp.end();
    }
  } catch (err) {
    console.log("文件上传失败", err);
  }
}

// 删除文件
async function executeRemoteCommand() {
  let filePath = ""; //同remoteDistPath一样
  // node-ssh 不支持文件上传功能,它主要是一个 SSH 客户端库,用于执行远程命令
  const ssh = new NodeSSH();
  try {
    // 连接到远程服务器
    await ssh.connect({
      host: serverHost,
      username: serverUser,
      password, // 或者使用privateKey等其他认证方式
    });
    // 执行远程命令
    await ssh.execCommand(`rm -rf ${filePath}`);
    console.log(`文件 ${filePath} 已从远程服务器成功删除。开始上传dist包`);
	pushDist();
  } catch (error) {
    console.error(`删除文件 ${filePath} 失败:`, error);
  } finally {
    // 断开连接并释放资源
    ssh.dispose();
  }
}

//  递归上传文件
async function uploadDirectory(sftp, localDir, remoteDir) {
  // 读取本地目录中的所有文件和子目录
  const files = await fs.promises.readdir(localDir, { withFileTypes: true });
  // 遍历文件和子目录
  for (const file of files) {
    const localFilePath = path.join(localDir, file.name);
    const remoteFilePath = path.join(remoteDir, file.name);
    // 如果是文件,则上传
    if (file.isFile()) {
      try {
        await sftp.mkdir(remoteDir, true);
        await sftp.put(localFilePath, remoteFilePath);
        console.log(`上传文件: ${localFilePath} -> ${remoteFilePath}`);
      } catch (err) {
        console.error(`上传文件失败: ${localFilePath} -> ${remoteFilePath}`, err);
      }
    } else if (file.isDirectory()) { // 如果是目录,则递归上传
      try {
        // 确保远程目录存在
        await sftp.mkdir(remoteFilePath, true); // 第二个参数为true时,如果目录已存在则不会报错
        console.log(`创建目录: ${remoteFilePath}`);

        // 递归调用自身,上传子目录
        await uploadDirectory(sftp, localFilePath, remoteFilePath);
      } catch (err) {
        console.error(`创建目录失败: ${remoteFilePath}`, err);
      }
    }
  }
}