真相只有一個=>懶!!!css
想直接使用的, github 傳送門 後面的能夠不用看了- -,記得留個star,筆心
目前主要在作iot項目,因爲歷史緣由,平臺還存在許多react/vue的純H5子項目,這些項目又必須調用APP暴露的某些api,使得本地開發調試時不得不重複構建手動將build包部署到開發服務器上。幾個項目,幾輪轉測試下來,很有些心累。所以想着能不能寫個工具,在執行yarn build
構建打包後自動部署到服務器,這樣就能夠省去:打開ftp工具->尋找構建目錄->尋找服務器上部署目錄->備份->粘貼複製文件,這套繁瑣手動流程,提升工(mo)做(yu)效率。
而且,有了這個後,jenkins自動構建時,項目配置的shell腳本也能少幾行代碼。html
首先,咱們先分析下要達到的結果:前端
執行yarn build
vue
我的喜歡用yarn
,用npm
的同窗能夠本身替換。
假設項目都是基於create react app
||Vue cli
腳手架搭建的,若是是本身自定義的腳手架的話,請繼續往下看。本文以 create react app腳手架爲例
build
目錄)copy全部文件並上傳至遠程服務器部署目錄其次,分析如何達成結果:node
yarn build
之後自動觸發,這裏咱們須要用到npm scripts
中的鉤子postbuild
,它會在執行build後作一些收尾工做,正好能夠用來觸發部署操做。對npm scripts
不太瞭解的同窗,能夠參閱《阮一峯 npm scripts 使用指南》 ssh
鏈接,推薦使用第三方庫ssh2 安裝 ssh2 、momentreact
yarn add ssh2 moment
爲了方便起見,在項目根目錄下新建 deploy.js
。這個js文件就是本次編寫的自動化部署工具git
deploy.js
不必定要在項目根目錄下,若是你對node尋找路徑方式比較熟悉,能夠放在本身指定的路徑下面
打開 package.json
文件,在 scripts
中添加:github
"postbuild": "yarn run deploy", "deploy": "node ./deploy.js",
此時,若是往 deploy.js
中添加 console.log('---deploy test--')
,控制檯執行 yarn build
,能夠看到,在構建完成後,會繼續執行yarn run deploy
,最終控制檯輸出:shell
---deploy test--
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') /*********************************************************************************/ /******************************請手動配置如下內容*********************************/ /*********************************************************************************/ /** * 遠程服務器配置 * @type {{password: string, port: number, host: string, username: string}} */ const server = { host: 'xxx.xxx.xxx.xxx', //主機ip port: 22, //SSH 鏈接端口 username: 'xxxx', //用戶名 password: 'xxxxxxx', //用戶登陸密碼 } const baseDir = 'my_app'//項目目錄 const basePath = '/home'//項目部署目錄 const bakDirName = baseDir + '.bak' + moment(new Date()).format('YYYY-M-D-HH:mm:ss')//備份文件名 const buildPath = path.resolve('./build')//本地項目編譯後的文件目錄 /*********************************************************************************/ /**********************************配置結束***************************************/ /*********************************************************************************/ 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, result) { 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) // 建立遠程目錄 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 }) // 上傳文件 let todoFile = [] files.forEach(function (file) { todoFile.push(function (done) { const to = path.join(remoteDir, file.slice(localDir.length + 1)).replace(/[\\]/g, '/') console.log('upload ' + to) doUploadFile(server, file, to, done) }) }) control.emit('doNext', todoDir, function (err) { if (err) { throw err } else { control.emit('doNext', todoFile, then) } }) } console.log('--------deploy config--------------') console.log(`服務器host: ${server.host}`) console.log(`項目文件夾: ${baseDir}`) console.log(`項目部署以及備份目錄: ${basePath}`) console.log(`備份後的文件夾名: ${bakDirName}`) console.log('--------deploy start--------------') doShell(server, `mv ${basePath}/${baseDir} ${basePath}/${bakDirName}\nexit\n`) doUploadDir(server, buildPath, `${basePath}/${baseDir}`, () => console.log('--------deploy end--------------'))
使用scripts
觸發時,運行yarn build之後,會自動觸發生命週期鉤子 postbuild
,進行部署。此過程會先在本地構建打包項目至配置的buildPath
目錄,而後在遠程服務器xxx.xxx.xxx.xxx
的 /home
中將my_app
備份爲my_app.bak2019-10-8-23:06:27
,最後將本地buildPath
目錄文件所有上傳到/home/my_app
,完成部署。npm
$ yarn run deploy $ node ./deploy.js --------deploy config-------------- 服務器host: xxx.xxx.xxx.xxx 項目文件夾: my_app 項目部署以及備份目錄: /home 備份後的文件夾名: my_app.bak2019-10-8-23:06:27 --------deploy start-------------- cmd::mkdir -p /home/my_app/static exit cmd::mkdir -p /home/my_app/static/css exit cmd::mkdir -p /home/my_app/static/js exit cmd::mkdir -p /home/my_app/static/media exit upload /home/my_app/asset-manifest.json upload /home/my_app/favicon.ico upload /home/my_app/index.html upload /home/my_app/logo192.png upload /home/my_app/logo512.png upload /home/my_app/manifest.json upload /home/my_app/precache-manifest.20dc8cb74286fd491ca0a9fc9b07234a.js upload /home/my_app/robots.txt upload /home/my_app/service-worker.js upload /home/my_app/static/css/main.2cce8147.chunk.css upload /home/my_app/static/css/main.2cce8147.chunk.css.map upload /home/my_app/static/js/2.222d1515.chunk.js upload /home/my_app/static/js/2.222d1515.chunk.js.map upload /home/my_app/static/js/main.0782b2ff.chunk.js upload /home/my_app/static/js/main.0782b2ff.chunk.js.map upload /home/my_app/static/js/runtime~main.077bb605.js upload /home/my_app/static/js/runtime~main.077bb605.js.map upload /home/my_app/static/media/logo.5d5d9eef.svg --------deploy end-------------- Done in 16.58s.
項目GitHub地址: https://github.com/hello-jun/deploy此工具也能夠單獨使用,稍加改造後,也能夠用來自動部署react native項目,有興趣的能夠本身嘗試~歡迎star、留言、issue。但願本文對各位有所幫助,祝工做生活愉快!