使用javascript写shell脚本(四) 打包部署脚本

4,927 阅读3分钟

这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战

本文主要讲通过执行脚本,本地项目打包并部署到服务器的过程。 之前的文章:

打包部署流程

流程如下:

  1. 本地执行npm run build
  2. 压缩打包之后的文件
  3. 发送到部署服务器,部署服务器需要有一个存放代码的路径
  4. 解压并与项目部署路径同步
  5. 删除文件

需要的依赖如下:

  • 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模块连接服务器并发送文件

image.png

fastPut接受4个参数

  • locaPath 本地路径
  • remotePath 远程路径
  • config
  • callback(err) 回调函数

我们在回调函数中完成剩余逻辑

image.png

两个目录之间同步使用的是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)
})()