快速部署构建产物的脚本

图例:
An image An image

# 使用

  • 收集 git 分支下的 commit 记录,将这些 commit 记录处理后存入数据库

  • 前端页面根据 commit 记录,实现一个开发日志的页面,如下: An image

  • 自动将打包产物的文件夹打包,然后通过 ssh 服务连接服务器后,将打包 zip 发送到服务器指定路径下,进行解压,覆盖掉原本的构建产物,实现自动部署

# 实现代码

点击 展开/收起 jsx 代码
import fs from 'fs';
import archiver from 'archiver';
import { Client } from 'ssh2';
import path from 'path';
import { fileURLToPath } from 'url';

// 获取当前模块的文件路径
const __filename = fileURLToPath(import.meta.url);

// 获取当前模块的目录路径
const __dirname = path.dirname(__filename);

// 打包产物构建
const archiveBuild = (buildDir, outputPath) => {
  return new Promise((resolve, reject) => {
    // 删除本地现有的 d.zip 文件
    if (fs.existsSync(outputPath)) {
      fs.unlinkSync(outputPath);
    }
    const output = fs.createWriteStream(outputPath);
    const archive = archiver('zip', {
      zlib: { level: 9 },
    });

    output.on('close', () => {
      // 获取压缩包文件大小
      const fileSizeInBytes = archive.pointer();
      const fileSizeInKB = fileSizeInBytes / 1024;
      const fileSize = fileSizeInKB.toFixed(0) + ' KB';

      // 输出压缩包大小信息
      console.log(`打包完成,包大小: ${fileSize}`);
      resolve();
    });
    output.on('error', (err) => reject(err));

    archive.pipe(output);
    archive.directory(buildDir, false);
    archive.finalize();
  });
};

// 上传并解压
const uploadAndExtract = (
  localPath,
  remoteZipPath,
  remoteDirPath,
  sshConfig,
  retries = 1,
) => {
  return new Promise((resolve, reject) => {
    const attemptUpload = (retryCount) => {
      const conn = new Client();
      conn
        .on('ready', () => {
          console.log('连接服务器成功,开始上传文件...');
          conn.sftp((err, sftp) => {
            if (err) {
              console.error('SFTP 错误:', err);
              return handleRetry(retryCount, err);
            }
            sftp.fastPut(localPath, remoteZipPath, {}, (err) => {
              if (err) {
                console.error('上传错误:', err);
                return handleRetry(retryCount, err);
              }
              const execute = `unzip -o ${remoteZipPath} -d ${remoteDirPath}`;
              console.log(`上传完成,开始执行解压任务命令: ${execute}  ...`);
              conn.exec(execute, (err, stream) => {
                if (err) {
                  console.error('解压错误:', err);
                  return handleRetry(retryCount, err);
                }
                stream
                  .on('close', (code, sinal) => {
                    console.log('解压完成,关闭连接...');
                    conn.end();
                    resolve();
                  })
                  .on('data', (data) => {})
                  .stderr.on('data', (errData) => {
                    reject(errData.toString());
                  });
              });
            });
          });
        })
        .connect(sshConfig);

      conn.on('error', (err) => {
        console.error('连接错误:', err);
        handleRetry(retryCount, err);
      });

      conn.on('end', () => {
        console.log('连接结束');
      });

      conn.on('close', (hadError) => {
        if (hadError) {
          console.error('连接关闭,发生错误');
          handleRetry(retryCount, new Error('连接关闭'));
        } else {
          console.log('连接关闭');
        }
      });
    };

    const handleRetry = (retryCount, err) => {
      if (retryCount > 0) {
        console.log(`重试中... (${retries - retryCount + 1}/${retries})`);
        setTimeout(() => attemptUpload(retryCount - 1), 1000);
      } else {
        reject(new Error(`操作失败: ${err.message}`));
      }
    };

    attemptUpload(retries);
  });
};

// 配置参数
const buildDir = path.join(__dirname, 'quantanalysis');
const outputPath = path.join(__dirname, 'quantanalysis.zip');
const remoteZipPath = '/etc/nginx/html/quantanalysis.zip';
const remoteDirPath = '/etc/nginx/html/quantanalysis';

const sshConfigStr = fs.readFileSync(
  '/Users/ming/Downloads/ssh/sshConfig.json',
  'utf8',
);
const sshConfig = JSON.parse(sshConfigStr);

// 执行打包和上传解压流程
const main = async () => {
  try {
    console.log(
      '--------------------------------------------------------------------------',
    );
    console.log('正在打包构建产物...');
    await archiveBuild(buildDir, outputPath);
    console.log('开始上传并解压...');
    await uploadAndExtract(outputPath, remoteZipPath, remoteDirPath, sshConfig);
    console.log('上传并解压任务完成!!!');
    // 删除本地的 build.zip 文件
    if (fs.existsSync(outputPath)) {
      fs.unlinkSync(outputPath);
    }
    console.log(
      '--------------------------------------------------------------------------',
    );
  } catch (error) {
    console.error('操作失败:', error);
    prcess.exit(1); // 停止构建过程
  }
};

main();