更新: 🎉现已支持添加多个配置信息,自动化部署时支持选择配置信息运行前端
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
,积极交流。