“这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战”
本文主要讲通过执行脚本,本地项目打包并部署到服务器的过程。 之前的文章:
打包部署流程
流程如下:
- 本地执行npm run build
- 压缩打包之后的文件
- 发送到部署服务器,部署服务器需要有一个存放代码的路径
- 解压并与项目部署路径同步
- 删除文件
需要的依赖如下:
- shelljs 执行shell脚本
- ssh2 发送文件
- chalk 让console的输出有颜色,非必须依赖
首先安装依赖
npm i shelljs ssh2 chalk -D
然后,在项目根目录下新建release文件
在开发中,往往需要将代码部署到不同的服务器,所以我们需要新建个SERVER对象,用来存放所有的服务器信息
#!/usr/bin/env node
const SERVER={
42: {
host: '10.1.43.42',
port: 19222,
username: 'xiaoming',
password: 'ababababa',
// 要发送的代码包本地目录,可以是绝对路径,也可以是相对路径
distPath: 'dist',
// 远程服务器部署路径
remoteBuildPath:
'/home/admin/video-app/webpage',
// 临时存放代码路径
remoteCodePath: '/home/diyvrbt/code',
buildScript: 'npm run build' //默认是npm run build
},
}
在脚本中,我们根据用户输入判断发送到哪个服务器
// 获取传入的参数,决定发送到哪个服务器
const [nodeEnv, dir, ...args] = process.argv
// 如果没有传入,使用默认的服务器
const serverNo = args.length == 0 ? DEFAULT_SERVER : args[0]
if (!Object.keys(SERVER).includes(String(serverNo))) {
throw new Error('服务器不存在')
}
然后执行npm run build,将输出的文件夹打成一个压缩包
// 打包
shell.exec(buildScript)
// 压缩文件
shell.exec(`tar -zcf ${localPath} ${appName}`)
然后使用ssh2模块连接服务器并发送文件
fastPut接受4个参数
- locaPath 本地路径
- remotePath 远程路径
- config
- callback(err) 回调函数
我们在回调函数中完成剩余逻辑
两个目录之间同步使用的是rsync命令,该命令会在两个目录之间进行文件的同步,比先删除再移动要好一些。
完整代码
#!/usr/bin/env node
// 如果有多个服务器,没有传入参数,制定一个默认的服务器
const DEFAULT_SERVER='42'
const SERVER = {
42: {
host: '10.1.43.42',
port: 19222,
username: 'xiaoming',
password: 'ababababa',
// 要发送的代码包本地目录,可以是绝对路径,也可以是相对路径
distPath: 'dist',
// 远程服务器部署路径
remoteBuildPath:
'/home/admin/video-app/webpage',
// 临时存放代码路径
remoteCodePath: '/home/diyvrbt/code',
buildScript: 'npm run build' //默认是npm run build
}
}
const shell = require('shelljs')
const Client = require('ssh2').Client
const dayjs = require('dayjs')
const chalk = require('chalk')
function getLastWord(str) {
return str.split('/').pop()
}
function log(str) {
console.log(chalk.blueBright(str))
}
/**
* 该脚本的作用是:把本地打包之后的文件发送到远程服务器
* 1. 先压缩文件
* 2. 发送到远程服务器
* 3. 与部署代码的路径同步
* 4. 删除文件
*/
;(function() {
// 获取传入的参数,决定发送到哪个服务器
const [nodeEnv, dir, ...args] = process.argv
// 如果没有传入,使用默认的服务器
const serverNo = args.length == 0 ? DEFAULT_SERVER : args[0]
if (!Object.keys(SERVER).includes(String(serverNo))) {
throw new Error('服务器不存在')
}
const server = SERVER[serverNo]
const {
remoteBuildPath,
remoteCodePath,
distPath,
buildScript = 'npm run build'
} = server
const appName = getLastWord(distPath)
// 本地目录
const localPath = appName + '.tar.gz'
// 服务器文件名
const remoteDirName =
appName + '_' + dayjs().format('YYYYMMDDTHHmmss') + '.tar.gz'
// 服务器目录
// 存放代码目录
const remotePath = `${remoteCodePath}/${remoteDirName}`
log(`
服务器信息:${JSON.stringify(server)}
本地目录:${localPath}
远程目录:${remotePath}
`)
const conn = new Client()
// 删除之前的文件
// 打包
shell.exec(buildScript)
// 压缩文件
shell.exec(`tar -zcf ${localPath} ${appName}`)
// 连接服务器并发送文件
conn.on('ready', () => {
log('Client::ready')
// log('sft服务器连接成功,准备传输文件')
conn.sftp((err, sftp) => {
log('sftp连接成功')
if (err) throw err
sftp.fastPut(
localPath,
remotePath,
{
chunkSize: 2000,
concurrency: 64,
step(total_transferred, chunk, total) {
// 打印一下进度
log(Math.floor((total_transferred / total) * 100) + '%')
}
},
err => {
if (err) {
console.log('err', err)
conn.end()
return
}
// 1. 解压 2. 同步文件 3. 删除
const script = `
cd ${remoteCodePath} &&
pwd &&
rm -rf ${appName} &&
tar -zxf ${remoteDirName} &&
rsync -rtplze --progress --stats ${appName}/ ${remoteBuildPath} &&
rm ${remoteDirName}
`
console.log('传输完文件要执行的命令:', script)
conn.exec(script, (err, stream) => {
if (err) {
console.log(err)
return
}
stream
.on('close', function(args) {
console.log('close', args)
if (args == 0) {
log('success!')
}
conn.end()
})
.on('data', function(data) {
console.log('data', data.toString())
})
})
}
)
})
}).connect(server)
})()