Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

# 实现前端通用部署脚手架 #9

Open
YIngChenIt opened this issue Jun 12, 2020 · 0 comments
Open

# 实现前端通用部署脚手架 #9

YIngChenIt opened this issue Jun 12, 2020 · 0 comments

Comments

@YIngChenIt
Copy link
Owner

YIngChenIt commented Jun 12, 2020

实现前端通用部署脚手架

背景

入职新公司之后发现前端打包还是传统的步骤

  • 手动压缩dist包
  • 通过xftp和xshell登录到对应的服务器,然后进行包的替换和备份

每次这样操作之后发现很麻烦,所以暂定实现一个在所有项目通用的脚手架,实现deploy-cli run dev直接进行部署打包,后续将前端部署打包接入到jenkis中去,这里记录一下实现脚手架的大致思路和核心代码

脚手架已经上传到npm

https://www.npmjs.com/package/cnhis-deploy-cli

git源码地址

https://github.com/YIngChenIt/deploy-cli

设计流程

  • 读取配置文件,获取服务器账号、密码、端口、包路径等
  • 执行配置文件中设置的脚本,如npm run build,进行前端打包生成dist
  • 将dist包压缩,连接ssh,上传到对应的目录
  • 将dist包解压,覆盖掉原来资源,删除服务器的dist压缩包

实现

配置文件

module.exports = {
    dev: {
        name: '',
        script: "", // 测试环境打包脚本
        host: '', // 测试服务器地址
        port: , // ssh port,一般默认22
        username: '', // 登录服务器用户名
        password: '', // 登录服务器密码
        distPath: '', // 本地打包dist目录
        webDir: '', // // 测试环境服务器地址
    },
}

我们可以把配置文件先存到git里面,然后通过download-git-repo这个第三方库从git中拉取配置文件

如输入deploy-cli init的时候拉去配置文件,当然我们也可以通过node读写文件的方式生成配置文件

连接ssh

我们主要通过node_ssh这个第三方库来实现连接ssh

const node_ssh = require('node-ssh')

const ssh = new node_ssh()
const connectSSH = async (config) => {
    const {
        host,
        port,
        username,
        password,
        privateKey,
        passphrase,
    } = config
    const sshConfig = {
        host,
        port,
        username,
        password,
        privateKey,
        passphrase
    }
    try {
        console.log(`(3)连接${underlineLog(host)}`)
        await ssh.connect(sshConfig)
        successLog('  SSH连接成功')
    } catch (err) {
        errorLog(`  连接失败 ${err}`)
        process.exit(1)
    }
}

压缩dist包

我们可以通过archiver这个第三方库来实现压缩dist包

const archiver = require('archiver');

// 设置压缩类型及级别
const archive = archiver('zip', {
zlib: { level: 9 },
}).on('error', err => {
throw err;
});

// 创建文件输出流
const output = fs.createWriteStream(__dirname + '/dist.zip');

// 通过管道方法将输出流存档到文件
archive.pipe(output);

// 从subdir子目录追加内容并重命名
archive.directory('subdir/', 'new-subdir');

// 完成打包归档
archive.finalize();

流程

const {
    successLog,
    errorLog,
    underlineLog,
} = require('./../utils/index')
const path = require('path')
const fs = require('fs')
const childProcess = require('child_process')
const ora = require('ora')
const node_ssh = require('node-ssh')
const archiver = require('archiver')

const projectDir = process.cwd()

let ssh = new node_ssh()

const deploy = async (config) => {
    const {
        script,
        webDir,
        distPath,
        name
    } = config
    try {
        execBuild(script)
        await startZip(distPath)
        await connectSSH(config)
        await uploadFile(webDir)
        await unzipFile(webDir)
        await deleteLocalZip()
        successLog(`\n 恭喜您,项目${underlineLog(name)}部署成功了^_^\n`)
        process.exit(0)
    } catch (err) {
        errorLog(`  部署失败 ${err}`)
        process.exit(1)
    }
}

const execBuild = (script) => {
    try {
        console.log(`\n(1)${script}`)
        const spinner = ora('正在打包中')
        spinner.start()
        console.log()
        childProcess.execSync(script, {
            cwd: projectDir
        })
        spinner.stop()
        successLog('  打包成功')
    } catch (err) {
        errorLog(err)
        // process.exit(1)
    }
}

const startZip = (distPath) => {
    return new Promise((resolve, reject) => {
        distPath = path.resolve(projectDir, distPath)
        console.log('(2)打包成zip')
        const archive = archiver('zip', {
            zlib: {
                level: 9
            },
        }).on('error', err => {
            throw err
        })
        const output = fs.createWriteStream(`${projectDir}/dist.zip`)
        output.on('close', err => {
            if (err) {
                errorLog(`  关闭archiver异常 ${err}`)
                reject(err)
                process.exit(1)
            }
            successLog('  zip打包成功')
            resolve()
        })
        archive.pipe(output)
        archive.directory(distPath, '/')
        archive.finalize()
    })
}

const connectSSH = async (config) => {
    const {
        host,
        port,
        username,
        password,
        privateKey,
        passphrase,
    } = config
    const sshConfig = {
        host,
        port,
        username,
        password,
        privateKey,
        passphrase
    }
    try {
        console.log(`(3)连接${underlineLog(host)}`)
        await ssh.connect(sshConfig)
        successLog('  SSH连接成功')
    } catch (err) {
        errorLog(`  连接失败 ${err}`)
        process.exit(1)
    }
}

const uploadFile = async (webDir) => {
    try {
        console.log(`(4)上传zip至目录${underlineLog(webDir)}`)
        await ssh.putFile(`${projectDir}/dist.zip`, `${webDir}/dist.zip`)
        successLog('  zip包上传成功')
    } catch (err) {
        errorLog(`  zip包上传失败 ${err}`)
        process.exit(1)
    }
}

async function runCommand(command, webDir) {
    await ssh.execCommand(command, { cwd: webDir });
}

const unzipFile = async (webDir) => {
    try {
        console.log('(5)开始解压zip包')
        await runCommand(`cd ${webDir}`, webDir)
        await runCommand('unzip -o dist.zip && rm -f dist.zip', webDir)
        successLog('  zip包解压成功')
    } catch (err) {
        errorLog(`  zip包解压失败 ${err}`)
        process.exit(1)
    }
}

const deleteLocalZip = async () => {
    return new Promise((resolve, reject) => {
        console.log('(6)开始删除本地zip包')
        fs.unlink(`${projectDir}/dist.zip`, err => {
            if (err) {
                errorLog(`  本地zip包删除失败 ${err}`, err)
                reject(err)
                process.exit(1)
            }
            successLog('  本地zip包删除成功\n')
            resolve()
        })
    })
}

module.exports = deploy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant