前端基于 scp2 的自动化部署方案 可提前备份

188 阅读4分钟

scp2 简介

一个基于ssh的纯javaScript安全复制程序

优点

可以在每个操作系统上执行,unix、Linux、windows等。

一、创建scp2相关的配置文件

【1】、在项目根目录下创建 deploy 文件夹, 在 deploy 文件夹下创建 config.js 文件用来保存服务器的配置信息。(示例如下:)

const client = require("scp2");

const config = require("../deploy.config.js");

console.log('正在部署,请稍等,host为:' + config.host + ',路径为' + config.serverPath)

client.scp(
  config.localPath,
  {
    host: config.host,
    username: config.username,
    password: config.password,
    path: config.serverPath,
  },
  function (err) {
    if (err) {
      console.log("部署失败,请确认账号密码正确", err);
    } else {
      console.log("部署成功");
    }
  }
);

在根目录创建 deploy.config.js文件

module.exports = {
  host: '47.94.96.226',
  username: 'root',
  password: 'Liulin1992',
  serverPath: '/home/muerdi123/dist',
  localPath: 'dist/'
}

image.png

二、添加执行命令

在 package.json 中注册执行命令,自定义命令名称:

scripts": {
    "dev": "vue-cli-service serve",
    "build:prod": "vue-cli-service build --report",
    "build:deploy": "vue-cli-service build && node deploy/index.js",
    "build:stage": "vue-cli-service build --mode staging",
    "preview": "node build/index.js --preview",
    "lint": "eslint --ext .js,.vue src"
  },

安装 npm install scp2@0.5.0

 "scp2": "^0.5.0",

三 执行打包并上传命令

npm run build:deploy

这种有个弊端就是服务器的地址 和密码是用明文显示的在代码中 容易泄露

第二种方法

升级版 手动输入服务器账号密码

1.在根目录下创建deploy文件夹

首先安装 依赖包

"chalk": "^4.1.2",
"ora": "^5.1.0",
"readline": "^1.3.0",
"scp2": "^0.5.0"

创建 index.js文件和config.js文件

index.js 文件如下

const scpClient = require('scp2')
const config = require('./config.js')
const readline = require('readline') // 输入内容使用
const ora = require('ora')  // 执行时打印信息
const chalk = require('chalk') // 打印信息颜色
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
})
const questions = ['Please input publish environment(test\\prod\\dev(defalut:prod)): ', 'Please input server username: ', 'Please input server password: ']
const linelimit = 3
let inputArr = []
let index = 0
let server = null

// 依次执行命令行交互语句
function runQueLoop() {
  if(index === linelimit) {
    if(!inputArr[0]) inputArr[0]='prod'
    server = config[inputArr[0]] 
    if(inputArr[1]) server.username = inputArr[1]
    if( inputArr[2]) server.password = inputArr[2]
    console.log('==========',server)
    deployFile()
    return
  }
  rl.question(questions[index], as => {
    console.log('输入的内容=====',as)
    inputArr[index] = as
    index++
    runQueLoop()
  })
}

runQueLoop()

function deployFile() {
  // 将前面的 scp2 上传文件到服务器代码,封装到这里。


const spinner = ora('正在发布到' + server.host + '服务器...')


spinner.start()

scpClient.scp(
  'dist/',
  {
    host: server.host,
    port: server.port,
    username: server.username,
    password: server.password,
    path: server.path
  },
  function (err) {
    spinner.stop()
    if (err) {
      console.log(chalk.red('发布失败.\n',err))
      
      throw err
    } else {
      console.log(chalk.green('Success! 成功发布到' + server.host + '服务器! \n'))
      
    }
  }
)
}

config.js 文件如下

// config.js
 /*
  *定义多个服务器账号 及 根据 SERVER_ID 导出当前环境服务器账号
  */
 const SERVER_LIST = {
   dev: {
     id: 0,
     name: 'A-生产环境',
     domain: 'qq.zbc.com', // 网站域名
     host: '110.42.130.82', // 服务器ip
     port: 22, // 登录服务器端口
     username: 'root', // 登录服务器的账号
     password: 'prod123456', // 登录服务器的账号
     path: '/www/wwwroot/site/dist', // 发布至静态服务器的项目路径
     rootPath: '/www/wwwroot/site'
   },
   prod: {
     id: 1,
     name: 'B-生产环境',
     port: 22,
     host: '47.94.98.234',
     username: 'root',
     password: '123456',
     path: '/home/muerdi123/dist',
     rootPath: 'dist/',
   }
 }
 
 module.exports = SERVER_LIST
 

执行命令 package.json

"scripts": {
    "dev": "vue-cli-service serve",
    "build:prod": "vue-cli-service build --report",
    "build:deploy": "vue-cli-service build && node deploy/index.js",
    "build:stage": "vue-cli-service build --mode staging",
    "preview": "node build/index.js --preview",
    "lint": "eslint --ext .js,.vue src",
    "deploy:prod": " node ./deploy"
  },

先备份在部署

二、上传前备份旧版文件

强烈建议,

在上传文件前对旧版文件进行备份,方便版本回退或部署失败后能够保证生产环境能够正常运行。

对代码进行简单的修改,在上传文件前,使用 ssh 先对原文件进行拷贝,删除,之后再上传文件。

基本实现思路如下:

进入到上传文件的根目录下 — 即 server 配置中的 rootPath 属性 新建一个文件夹用于备份 _backUp/文件名

  • 拷贝旧版部署包到备份文件夹下
  • 删除旧版部署包
  • 上传新版部署包
  • 具体实现方案:
  1. 安装ssh2
npm i ssh2 -D
  1. 导入ssh2模块,并声明要执行的命令
// ssh2 传输
const ora = require('ora') // 执行时打印信息
const chalk = require('chalk') // 打印信息颜色
const Client = require('ssh2').Client
const conn = new Client()
const server = {
  id: 1,
  name: 'B-生产环境',
  port: 22,
  host: '47.945.965.2526',
  username: 'root',
  password: 'Liu',
}
const rootPath = '/home'
const rootFolder = 'testcopy'
var today = new Date();
 
// 获取年、月、日、时、分、秒
var year = today.getFullYear();
var month = today.getMonth() + 1; // 月份是从 0 开始计数的,需要加1
var day = today.getDate();
var hours = today.getHours();
var minutes = today.getMinutes();
var seconds = today.getSeconds();
 
// 格式化输出
var currentTime = year + "-" + 
                   (month < 10 ? "0" : "") + month + "-" + 
                   (day < 10 ? "0" : "") + day + "-" + 
                   (hours < 10 ? "0" : "") + hours + ":" + 
                   (minutes < 10 ? "0" : "") + minutes + ":" + 
                   (seconds < 10 ? "0" : "") + seconds;
 
                   console.log(currentTime);
// 声明要执行的命令
          
let cmd = `cd ${rootPath}\n
            mkdir -p ${rootFolder}_${currentTime}\n 
            cp -r /home/test /home/${rootFolder}_${currentTime}\n
            
           `
conn.on('ready', function () {
    conn.exec(cmd, function (err, stream) {
      // 备份命令执行后
      console.log(chalk.green('已执行备份命令'))
      console.log('err====', err)
      // console.log('stream====', stream)
      if (err) throw err
      console.log('123')
      // 在执行命令行成功后上传文件到服务器上
      stream.on('close', () => {
          // 执行部署工作
          // deployFile()
          console.log('开始部署')
          conn.end(); // 执行完部署后要执行这个
        })
        .on('data', (data) => {
          console.log('STDOUT: ' + data);
        }).stderr.on('data', (data) => {
          console.log('STDERR:====== ' + data);
        });
        
    })
  })
  .on('error', function () {
    console.log('chucuole ==========')
  })

  .connect({
    host: server.host,
    port: server.port,
    username: server.username,
    password: server.password
  })

执行命令之前

image.png 执行命令之后 是把整个文件夹拷贝到新建的文件夹下

image.png

image.png