Gadzan

Vue/cli3 在小服务器上的自动部署方法

问题

小服务上并没有Jenkins这种自动部署集成工具,也不想在服务器有限的空间里安装依赖。但是每次用sftp手动操作非常麻烦。

针对vue-cli3想想办法一键部署提高生产力。

原理

利用filemanager-webpack-plugin在项目build后自动生成dist的zip文件,并使用ssh2上传zip文件并运行远程命令。

配置

修改项目根目录下的vue.config.js

const FileManagerPlugin = require('filemanager-webpack-plugin');
const isProduction = process.env.NODE_ENV === 'production';
// ...
module.exports = {
  // ...
  configureWebpack: config => {
    if (isProduction) {
      config.plugins.push(new FileManagerPlugin({
        //初始化 filemanager-webpack-plugin plugin
        onEnd: {
          delete: [
            // 删除在 dist 文件夹的 deploy.zip.
            './dist/deploy.zip',   
          ],
          archive: [
            // 压缩 dist 后放到 dist/deploy.zip.
            {source: './dist', destination: './dist/deploy.zip'},   
          ]
        }
      }))
    }
  }
}

安装依赖:

npm i -D filemanager-webpack-plugin ssh2 readline-sync ora chalk

代码

在src目录下新建一个deploy文件夹,并新建一个index.js,自行修改对应服务器地址和静态资源文件夹。
src/deploy/index.js

const host = '<你的服务器地址>'
const WEBFOLDER = '<服务器静态资源文件夹绝对地址>';
const FILENAME = 'deploy.zip';

const readlineSync = require('readline-sync');
const ora = require('ora');
const chalk = require('chalk');
const sshClient = require('ssh2').Client;
const conn = new sshClient();
const connectingServer = ora('Connecting server...');

const sshExec = cmd => new Promise((resolve, reject) => {
  conn.exec(cmd, function(
    err,
    stream
  ){
    if(err) reject(err);
    stream
    .on('close', function(code, signal) {
      resolve([code, signal]);
    })
    .on('data', () => {})
    .stderr
      .on('data', (err) => {
        reject(err)
      });
  })
});

const shhUpload = (origin, target) => new Promise((resolve, reject) => {
  const uploadingFile = ora('Uploading...');
  conn.sftp(function(err, sftp) {
    if (err) reject(err);
    uploadingFile.start();
    sftp.fastPut(
      origin, 
      target, 
      {}, 
      (err, result) => {
        uploadingFile.stop();
        if (err) reject(err);
        if (result) console.log(result);
        resolve();
      }
    );
  });
})

readlineSync.setDefaultOptions({
  encoding: 'utf8'
});

(async () => {
  console.log(chalk.black.bold.bgYellow('    Server Account    '));
  let username = await readlineSync.question(chalk.gray.underline('  Account:  '));
  let password = await readlineSync.question(chalk.gray.underline('  Password:  '), {
    hideEchoBack: true,
  });
  connectingServer.start();
  conn
  .on('ready', async () => {
    try{
      console.log(chalk.green('Connected'));
      connectingServer.stop();

      console.log('----------------------------')
      console.log(chalk.blue('Deleting old files...'));
      await sshExec(`rm -rf ${WEBFOLDER}/*`);
      console.log(chalk.bgGreen.black('Deleted.'));
      
      console.log(chalk.blue('Prepare to upload zip file...'));
      await shhUpload(`${__dirname}/../../dist/${FILENAME}`, `${WEBFOLDER}/${FILENAME}`) 
      console.log(chalk.bgGreen.black('Uploaded successfully.'));

      console.log(chalk.blue('Deploying...'));
      await sshExec(`unzip -o ${WEBFOLDER}/${FILENAME} -d ${WEBFOLDER}`);
      
      console.log(chalk.blue('Removing zip file...'));
      await sshExec(`rm -rf ${WEBFOLDER}/deploy.zip`);
      console.log(chalk.bgGreen.black('Removed.'));

      conn.end();
    } catch (error) {
      conn.end();
      console.log(chalk.bold.bgRed.black(error));
    }
  })
  .connect({
    host,
    port: 22,
    username,
    password,
  });
})();

在项目根目录运行node src/deploy即可运行部署脚本。

修改项目根目录package.json,在build命令后加上 && node src/deploy即可在build的同时自动部署到服务器。运行后会提示输入服务器用户名和密码。

 ...
 "scripts": {
    ...
    "build": "vue-cli-service build && node src/deploy",
    ...
  },

评论