更新: 🎉現已支持添加多個配置信息,自動化部署時支持選擇配置信息運行前端
1. 待部署工程本地完成打包構建vue
2. 肯定遠端部署目錄及發佈文件夾node
3. 修改配置linux
4. 運行自動化部署nginx
5. 查看遠端效果git
6. 再次部署 原目錄已備份(開啓遠端備份生效)github
前端項目部署時,nginx配置完成後,只需將打包後的文件上傳至服務器指定目錄下便可。web
通常使用如下方式完成:vue-cli
xshell
等命令行工具上傳ftp
等可視化工具上傳jenkins
等自動化部署服務對於簡單前端項目,頻繁部署時,xshell
、ftp
兩種方式較爲繁瑣,而jenkins
等自動化部署服務須要提早安裝軟件、並熟悉配置流程。 所以但願藉助本地 node
服務實現對前端打包後文件的上傳工做,既不須要服務器額外安裝程序,還能夠幫助咱們實現快速上傳部署,更能幫助咱們深刻了解 node
。shell
PS: 以前一直在準備相關前端自動化部署的實現,最近恰好看到 笪笪 的文章,參考文章中說起的 node-ssh
和 archiver
,完成對該項目的實現, 在此表示感謝(同時感謝 node-ssh
、 archiver
、inquirer
做者)。
附:npm地址
進行開發前須要首先明確需求,根據常見的前端部署流程總結爲如下過程:
根據部署流程明確自動化部署的需求:
因爲須要實現文件壓縮、及鏈接遠程服務器、實現遠程命令調用,所以至少須要如下模塊:
ssh
模塊(可實現鏈接服務器、命令調用等常見操做)文件壓縮
模塊(可實現 .zip
等常見壓縮文件的本地打包)命令行選擇
模塊(可實現對多配置項文件進行選擇和使用)查找資料,最終選擇 node-ssh
、 archiver
、inquirer
分別實現上述功能。
# 安裝依賴
npm i node-ssh --save
npm i archiver --save
npm i inquirer --save
複製代碼
爲實現需求中的 解耦合理
與 邏輯清晰/靈活
,須要關注總體程序邏輯,這裏選擇 封裝
相關功能實現,並在 主程序
中自由調度(可靈活 調用/關閉/修改
相關功能),並對於當前所執行功能給與提示,以保證功能實現的 完整性
和 異常提示
。
由於 文件壓縮
、文件上傳
、 執行遠端命令
等存在異步過程,所以須要明確功能完成的順序,即 應在前置任務完成的回調中開啓當前任務
,爲實現控制異步過程和代碼邏輯清晰,這裏選擇使用 ES6 的 Promise
結合 ES7的語法糖 async awiat
實現邏輯流程控制。
到這裏就完成了對程序功能構建的梳理工做,下面進入項目實現。
工程目錄預覽 這裏先展現最終結果的工程目錄,以供參考:
compressFile
接收 須要壓縮的目錄
和 打包生成文件
,傳入後實現本地文件壓縮。
compressFile.js
參考代碼
// compressFile.js
var fs = require('fs')
var archiver = require('archiver')
function compressFile (targetDir, localFile) {
return new Promise((resolve, reject)=>{
console.log('1-正在壓縮文件...')
let output = fs.createWriteStream(localFile) // 建立文件寫入流
const archive = archiver('zip', {
zlib: { level: 9 } // 設置壓縮等級
})
output.on('close', () => {
resolve(
console.log('2-壓縮完成!共計 ' + (archive.pointer() / 1024 /1024).toFixed(3) + 'MB')
)
}).on('error', (err) => {
reject(console.error('壓縮失敗', err))
})
archive.pipe(output) // 管道存檔數據到文件
archive.directory(targetDir, 'dist') // 存儲目標文件並重命名
archive.finalize() // 完成文件追加 確保寫入流完成
})
}
module.exports = compressFile
複製代碼
connectServe
接收遠端ip
、用戶名
、密碼
等信息,完成遠端服務器鏈接,具體配置參考 config.js
。
ssh.js
參考代碼
// ssh.js
node_ssh = require('node-ssh')
ssh = new node_ssh()
function connectServe (sshInfo) {
return new Promise((resolve, reject) => {
ssh.connect({ ...sshInfo }).then(() => {
resolve(console.log('3-' + sshInfo.host + ' 鏈接成功'))
}).catch((err) => {
reject(console.error('3-' + sshInfo.host + ' 鏈接失敗', err))
})
})
}
module.exports = connectServe
複製代碼
runCommand
接收 需執行的命令
和 執行命令的遠端路徑
,這裏將其單獨拆分,既方便 主程序
的單獨調用,也方便 文件上傳
等功能的模塊封裝,達到 解耦
的效果。
handleCommand.js
參考代碼
// handleCommand.js
node_ssh = require('node-ssh')
ssh = new node_ssh()
// run linux shell
function runCommand (command, path) {
return new Promise((resolve, reject) => {
ssh.execCommand(command, {
cwd: path
}).then((res) => {
if (res.stderr) {
reject(console.error('發生錯誤:' + res.stderr))
} else {
resolve(console.log(command + ' 執行完成!'))
}
})
})
}
module.exports = runCommand
複製代碼
uploadFile
接收 系統配置參數
、待上傳的本地文件
,完成本地文件上傳至指定服務器目錄,這裏還引入 handleCommand.js
,根據 openBackUp
是否開啓遠端備份,完成對存在解壓後同名目錄的處理。具體配置參考 config.js
。
uploadFile.js
參考代碼
// uploadFile.js
node_ssh = require('node-ssh')
ssh = new node_ssh()
runCommand = require ('./handleCommand')
async function uploadFile (config, localFile) {
return new Promise((resolve, reject) => {
console.log('4-開始文件上傳')
handleSourceFile()
ssh.putFile(localFile, config.deployDir + config.targetFile).then(async () => {
resolve(console.log('5-文件上傳完成'))
}, (err) => {
reject(console.error('5-上傳失敗!', err))
})
})
}
// 處理源文件
async function handleSourceFile () {
if (config.openBackUp) {
console.log('已開啓遠端備份!')
await runCommand(
` if [ -d ${config.releaseDir} ]; then mv ${config.releaseDir} ${config.releaseDir}_${new Date().getTime()} fi `,
config.deployDir)
} else {
console.log('提醒:未開啓遠端備份!')
await runCommand(
` if [ -d ${config.releaseDir} ]; then mv ${config.releaseDir} /tmp/${config.releaseDir}_${new Date().getTime()} fi `,
config.deployDir)
}
}
module.exports = uploadFile
複製代碼
當全部功能模塊封裝完成後,其中 異步流程
均使用 Promise
處理,這時結合 async awiat
實現,既保證了功能實現的順序,也使得功能組合變得更加簡潔、優雅。
main
函數中通關功能組合實現自動化部署的流程,後續增長其餘功能實現,主程序
中引入、組合便可完成升級。
app.js
參考代碼
// app.js
config = require ('./config')
compressFile = require ('./utils/compressFile')
connectServe = require ('./utils/ssh')
uploadFile = require ('./utils/uploadFile')
runCommand = require ('./utils/handleCommand')
// 可單獨執行
async function main () {
const localFile = __dirname + '/' + config.targetFile
config.openCompress ? await compressFile(config.targetDir, localFile) : '' //壓縮
await connectServe(config.ssh) // 鏈接
await uploadFile(config, localFile) // 上傳
await runCommand('unzip ' + config.targetFile, config.deployDir) // 解壓
await runCommand('mv dist ' + config.releaseDir, config.deployDir) // 修改文件名稱
await runCommand('rm -f ' + config.targetFile, config.deployDir) // 刪除
console.log('全部操做完成!')
process.exit()
}
// run main
main()
複製代碼
爲方便前端自動化部署,這裏抽離關鍵信息,生成配置文件。 用戶只需修改配置文件便可實現 自動化部署
。
config.js
參考代碼
// config.js
/* 說明: 請確保解壓後的文件目錄爲dist ssh: 鏈接服務器用戶信息 targetDir: 須要壓縮的文件目錄(需開啓壓縮) targetFile: 指定上傳文件名稱(該文件同級目錄) openCompress: 關閉後,將跳過目標目錄壓縮步驟,直接上傳指定文件 openBackUp: 開啓後,若遠端存在相同目錄,則會修改原始目錄名稱,不會直接覆蓋 deployDir: 指定遠端部署地址 releaseDir: 指定遠端部署地址下的發佈目錄名稱 */
const config = {
ssh: {
host: '192.168.0.110',
username: 'root',
password: 'root'
},
targetDir: 'E:/private/my-vue-cli/dist', // 目標壓縮目錄(可以使用相對地址)
targetFile: 'dist.zip', // 目標文件
openCompress: true, // 是否開啓壓縮
openBackUp: true, // 是否開啓遠端備份
deployDir: '/home/node_test' + '/', // 遠端目錄
releaseDir: 'web' // 發佈目錄
}
module.exports = config
複製代碼
拉取源碼、安裝依賴、修改配置文件、運行便可
npm install
npm run deploy
複製代碼
🎉該項目已開源至 github
歡迎下載使用 後續會完善更多功能 🎉 源碼及項目說明 Tip: 喜歡的話別忘記 star
哦😘,有疑問🧐歡迎提出 issues
,積極交流。