index.js 7.25 KB
/* eslint-disable */
const path = require('path')
const moment = require('moment')
const util = require('util')
const events = require('events')
const Client = require('ssh2').Client
const fs = require('fs')
const ProgressBar = require('progress')
const inquirer = require('inquirer')

//cnpm install moment util events ssh2 progress inquirer --dev  请先在deploy所在目录安装以上包

/******************************请手动配置以下内容*********************************/
/** 远程服务器配置
* @type {{password: string, port: number, host: string, username: string}}
*/

const serveEnv = {
  stage: {
    host: '8.142.143.40',
    port: '22',                //SSH 连接端口
    username: 'changfa',        //用户名
    password: 'changFA123',     //用户登录密码
  },
}


// changfa07
// 公网IP 8.142.143.40    端口22

// changfa / changFA123 

// 目录      /home/changfa/app/apache-tomcat-8.5.5new/webapps/dist/


let env = process.argv[process.argv.length - 1]
console.log('当前环境', env)
let server = serveEnv[env]
if (!server) {
  console.log('请检查当前环境')
}

const basePath = '/home' //服务器网站根目录
let baseDir =  '/changfa/app/apache-tomcat-8.5.5new/webapps/dist' //项目目录名称8.5.5new
let back_up_dir = '' //备份目录名称,需手动创建,可选,注意目录名后有斜杠   比如    back_up/
const bakDirName = baseDir + '.bak' + moment(new Date()).format('YYYY-M-D-HH:mm:ss') //备份文件名
const buildPath = path.resolve('./dist') //本地项目编译后的文件目录

console.log(basePath, );
/**********************************配置结束***************************************/

function doConnect(server, then) {
  const conn = new Client()
  conn.on('ready', function () {
    then && then(conn)
  }).on('error', function (err) {
    console.error('connect error!', err)
  }).on('close', function () {
    conn.end()
  }).connect(server)
}

function doShell(server, cmd, then) {
  doConnect(server, function (conn) {
    conn.shell(function (err, stream) {
      if (err) throw err
      else {
        let buf = ''
        stream.on('close', function () {
          conn.end()
          then && then(err, buf)
        }).on('data', function (data) {
          buf = buf + data
        }).stderr.on('data', function (data) {
          console.log('stderr: ' + data)
        })
        stream.end(cmd)
      }
    })
  })
}

function doGetFileAndDirList(localDir, dirs, files) {
  const dir = fs.readdirSync(localDir)
  for (let i = 0; i < dir.length; i++) {
    const p = path.join(localDir, dir[i])
    const stat = fs.statSync(p)
    if (stat.isDirectory()) {
      dirs.push(p)
      doGetFileAndDirList(p, dirs, files)
    }
    else {
      files.push(p)
    }
  }
}

function Control() {
  events.EventEmitter.call(this)
}

util.inherits(Control, events.EventEmitter)

const control = new Control()

control.on('doNext', function (todos, then) {
  if (todos.length > 0) {
    const func = todos.shift()
    func(function (err) {
      if (err) {
        then(err)
        throw err
      }
      else {
        control.emit('doNext', todos, then)
      }
    })
  }
  else {
    then(null)
  }
})

function doUploadFile(server, localPath, remotePath, then) {
  doConnect(server, function (conn) {
    conn.sftp(function (err, sftp) {
      if (err) {
        then(err)
      }
      else {
        sftp.fastPut(localPath, remotePath, function (err, result) {
          conn.end()
          then(err, result)
        })
      }
    })
  })
}

function doUploadDir(server, localDir, remoteDir, then) {
  let dirs = []
  let files = []
  doGetFileAndDirList(localDir, dirs, files)

  // 创建远程目录
  console.log('开始创建远程目录')
  let todoDir = []
  dirs.forEach(function (dir) {
    todoDir.push(function (done) {
      const to = path.join(remoteDir, dir.slice(localDir.length + 1)).replace(/[\\]/g, '/')
      const cmd = 'mkdir -p ' + to + '\r\nexit\r\n'
      // console.log(`cmd::${cmd}`)
      doShell(server, cmd, done)
    })// end of push
  })

  // 上传文件
  console.log('准备上传文件:')
  let todoFile = []
  let total = files.length;
  let bar = new ProgressBar('上传进度:[:bar] :percent   剩余时长::etas', { total, width: 50 });
  files.forEach(function (file) {
    todoFile.push(function (done) {
      const to = path.join(remoteDir, file.slice(localDir.length + 1)).replace(/[\\]/g, '/')
      // console.log('upload ' + to)
      bar.tick(1);
      doUploadFile(server, file, to, done)
    })
  })
  control.emit('doNext', todoDir, function (err) {
    if (err) {
      throw err
    }
    else {
      control.emit('doNext', todoFile, then)
    }
  })
}


let mutual = {
  chooseDir: function (err, dirList) {
    if (err) {
      console.log(err)
      return false
    }
    if (baseDir) {
      init()
      return
    }
    dirList.unshift('我要新建目录')
    const promptList = [
      {
        type: 'list',
        message: '请选择要上传到的项目目录:',
        name: 'dir',
        choices: dirList,
      }
    ]

    inquirer.prompt(promptList).then(answers => {

      if (answers.dir === '我要新建目录') {
        mutual.mkNewDir()
        return false
      } else if (answers.dir.includes(basePath)) {
        baseDir = basePath
      } else {
        baseDir = answers.dir
      }
      init()

    }).catch(err => {
      console.log(err)
    })
  },
  mkNewDir: function () {
    const promptList = [
      {
        type: 'input',
        message: '请输入要创建的目录名称:',
        name: 'dir',
      }
    ]

    inquirer.prompt(promptList).then(answers => {
      if (!answers.dir) {
        console.error('警告:文件名不能为空')
        return false
      }
      baseDir = answers.dir
      init()
    })
  }

}




/**
* 描述:获取远程文件路径下文件列表信息
* 参数:server 远程电脑凭证;
*		remotePath 远程路径;
*		isFile 是否是获取文件,true获取文件信息,false获取目录信息;
*		then 回调函数
* 回调:then(err, dirs) : dir, 获取的列表信息
*/
function getFileOrDirList(server, remotePath, isFile, then) {
  var cmd = "find " + remotePath + " -type " + (isFile == true ? "f" : "d") + "\r\nexit\r\n"
  doShell(server, cmd, function (err, data) {
    var arr = []
    var remoteFile = []
    arr = data.split("\r\n")
    arr.forEach(function (dir) {
      if (dir.indexOf(remotePath) == 0) {
        remoteFile.push(dir)
      }
    })
    remoteFile = remoteFile.map(item => item.split('/')[2]).filter(item => item && item.trim()) //只保留第一层目录
    remoteFile = Array.from(new Set(remoteFile)) //去重
    then(err, remoteFile)
  })
}


function init() {
  console.log('\n--------配置如下--------------\n')
  console.log(`服务器host:            ${server.host}`)
  console.log(`项目文件夹:            ${baseDir}`)
  console.log(`项目部署以及备份目录:  ${basePath}`)
  console.log(`备份后的文件夹名:      ${bakDirName}`)
  console.log('\n--------开始部署--------------\n')
  doShell(server, `mv ${basePath}/${baseDir} ${basePath}/${back_up_dir}${bakDirName}\nexit\n`) //备份远程目录文件
  doUploadDir(server, buildPath, `${basePath}/${baseDir}`, () => console.log('\n--------部署成功--------------'))
}


getFileOrDirList(server, basePath, false, mutual.chooseDir)