deploy.config.js
/*
deploy.config.js说明:
ssh: 连接服务器用户信息
targetDir: 需要压缩的文件目录(启用本地压缩后生效)
openCompress: 关闭后,将跳过本地文件压缩,直接上传同级目录下指定文件
openBackUp: 开启后,若远端存在相同目录,则会修改原始目录名称,不会直接覆盖
deployDir: 指定远端部署地址
releaseDir: 指定远端部署地址下的发布目录名称
*/
const config = [
{
name: '东风浩荡发鬼地方',
ssh: {
host: '47.93.231.96',
port: 22,
username: 'yonghuming',
password: 'mima',
// privateKey: 'E:/id_rsa', // ssh私钥(不使用此方法时请勿填写, 注释即可)
// passphrase: '123456' // ssh私钥对应解密密码(不存在设为''即可)
},
targetDir: './dist', // 目标压缩目录(可使用相对地址)
openCompress: true, // 是否开启本地压缩
openBackUp: true, // 是否开启远端备份
deployDir: '/home/guixiang/unified_platform/', // 远端目录
releaseDir: 'dist' // 发布目录
},
{
name: '名称',
ssh: {
host: '139.224.27.169',
port: 22,
username: 'root',
password: 'shanjian@2019',
},
targetDir: './dist', // 目标压缩目录(可使用相对地址)
openCompress: true, // 是否开启本地压缩
openBackUp: true, // 是否开启远端备份
deployDir: '/home/shanjian/unified_platform/', // 远端目录
releaseDir: 'dist' // 发布目录
}
]
module.exports = config
deploy/core/compressFile.js
const fs = require('fs')
const archiver = require('archiver')
function compressFile (targetDir, localFile, releaseDir) {
console.log(targetDir, localFile, releaseDir)
return new Promise((resolve, reject) => {
console.log('1-正在压缩文件...')
const 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, releaseDir) // 存储目标文件并重命名
archive.finalize() // 完成文件追加 确保写入流完成
})
}
module.exports = compressFile
deploy/core/deleteFile.js
const fs = require('fs')
function deleteFile (path) {
fs.unlink(path, (err) => {
if (err) throw err
console.log('successfully deleted')
})
}
module.exports = deleteFile
deploy/core/handleCommand.js
function runCommand (ssh, command, path) {
return new Promise((resolve, reject) => {
ssh.execCommand(command, {
cwd: path
}).then((res) => {
if (res.stderr) {
reject(console.error('命令执行发生错误:' + res.stderr))
process.exit()
} else {
resolve(console.log(command + ' 执行完成!'))
}
})
})
}
module.exports = runCommand
deploy/core/helper.js
```bash
const inquirer = require('inquirer')
const selectTip = 'project name:'
const options = [
{
type: 'list',
name: selectTip,
message: 'Which project do you want to deploy?',
choices: []
// new inquirer.Separator() // 分割线
}
]
// 显示选择提示窗
function showHelper (config) {
return new Promise((resolve, reject) => {
initHelper(config) // 初始化helper
inquirer.prompt(options).then(answers => {
resolve({ value: findInfoByName(config, answers[selectTip]) }) // 查找所选配置项
}).catch((err) => {
reject(console.error(' helper显示或选择出错!', err))
})
})
}
// 初始化helper
function initHelper (config) {
for (const item of config) {
options[0].choices.push(item.name)
}
console.log('正在检查全局配置信息...')
// 检查是否存在相同name
if (new Set(options[0].choices).size !== options[0].choices.length) {
console.error('请检查配置信息,存在相同name!')
process.exit()
}
}
// 查找符合条件的配置项
function findInfoByName (config, name) {
for (const item of config) {
if (item.name === name) {
return item
}
}
}
module.exports = showHelper
deploy/core/ssh.js
const NodeSSH = require('node-ssh')
const ssh = new NodeSSH()
// 连接服务器
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 = { ssh, connectServe }
const NodeSSH = require('node-ssh')
const ssh = new NodeSSH()
// 连接服务器
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 = { ssh, connectServe }
deploy/core/uploadFile.js
const runCommand = require('./handleCommand')
const getCurrentTime = require('./handleTime')
// 文件上传(ssh对象、配置信息、本地待上传文件)
async function uploadFile (ssh, config, targetFile, localFile) {
return new Promise((resolve, reject) => {
console.log('4-开始文件上传')
handleSourceFile(ssh, config)
ssh.putFile(localFile, config.deployDir + targetFile).then(async () => {
resolve(console.log('5-文件上传完成'))
}, (err) => {
reject(console.error('5-上传失败!', err))
})
})
}
// 处理源文件(ssh对象、配置信息)
async function handleSourceFile (ssh, config) {
if (config.openBackUp) {
console.log('已开启远端备份!')
await runCommand(
ssh,
`
if [ -d ${config.releaseDir} ];
then mv ${config.releaseDir} ${config.releaseDir}_${getCurrentTime()}
fi
`,
config.deployDir)
} else {
console.log('提醒:未开启远端备份!')
await runCommand(
ssh,
`
if [ -d ${config.releaseDir} ];
then mv ${config.releaseDir} /tmp/${config.releaseDir}_${getCurrentTime()}
fi
`,
config.deployDir)
}
}
module.exports = uploadFile
deploy/index.js
const config = require('../deploy.config.js')
const helper = require('./core/helper')
const compressFile = require('./core/compressFile')
const sshServer = require('./core/ssh')
const uploadFile = require('./core/uploadFile')
const runCommand = require('./core/handleCommand')
const deleteFile = require('./core/deleteFile')
// 主程序(可单独执行)
async function main () {
try {
const SELECT_CONFIG = (await helper(config)).value // 所选部署项目的配置信息
console.log('您选择了部署 ' + SELECT_CONFIG.name)
const targetFile = `${SELECT_CONFIG.releaseDir}.zip` // 本地压缩文件
const localFile = `${__dirname}/${targetFile}` // 待上传本地文件
SELECT_CONFIG.openCompress ? await compressFile(SELECT_CONFIG.targetDir, localFile, SELECT_CONFIG.releaseDir) : '' //压缩
await sshServer.connectServe(SELECT_CONFIG.ssh) // 连接
await uploadFile(sshServer.ssh, SELECT_CONFIG, targetFile, localFile) // 上传
await runCommand(sshServer.ssh, 'unzip ' + targetFile, SELECT_CONFIG.deployDir) // 解压
await runCommand(sshServer.ssh, 'rm -f ' + targetFile, SELECT_CONFIG.deployDir) // 删除
deleteFile(localFile) // 删除本地压缩文件
} catch (err) {
console.log('部署过程出现错误!', err)
} finally {
process.exit()
}
}
// run main
main()
package.json
`{
"name": "smart-elevator-admin",
"name": "unified-platform",
"version": "0.1.0",
"private": true,
"scripts": {
"build:pro": "vue-cli-service build --mode production",
"lint": "npm run lint:code && npm run lint:style",
"lint:code": "vue-cli-service lint",
"lint:style": "vue-cli-service lint:style"
"lint:style": "vue-cli-service lint:style",
"deploy:dev": "npm run build:dev && node ./deploy/index.js",
"deploy:pro": "npm run build:pro && node ./deploy/index.js"
},
"husky": {
"hooks": {
"@vue/cli-plugin-vuex": "^4.2.3",
"@vue/cli-service": "^4.2.3",
"@vue/eslint-config-standard": "^5.1.2",
"archiver": "^3.1.1",
"babel-eslint": "^10.1.0",
"babel-plugin-import": "^1.13.0",
"commitizen": "^4.0.3",
"eslint-plugin-standard": "^4.0.1",
"eslint-plugin-vue": "^6.2.2",
"husky": "^4.2.3",
"inquirer": "^7.1.0",
"less": "^3.11.1",
"less-loader": "^5.0.0",
"lint-staged": "^10.0.8",
"node-ssh": "^8.0.0",
"stylelint": "^13.2.1",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-config-standard": "^20.0.0",
public/index.html
</head>
<body>
<noscript>
<strong>We're sorry but smart-elevator-admin doesn't work properly without JavaScript enabled. Please enable it to
<strong>We're sorry but unified-platform doesn't work properly without JavaScript enabled. Please enable it to
continue.</strong>
</noscript>
<div id="app"></div>