刚进入公司的时候,公司的前端项目部署使用的Jenkins和GitLab进行部署,但是后来不知道什么原因舍弃了这种方法,开始了手动部署,前端则开始使用fileZilla这个软件进行部署,每次都要将项目打包后再使用该工具进行上传打包好的项目,多少还是感觉有些麻烦,所以想要自己写一个适用于前端部署的一个工具。

初始化

首先运行npm init进行初始化package.json文件
将package.json文件中增加bin选项,值为命令行中的入口文件

{...
"name": "mycmd",
"bin":"./src/cmds/index"
...}

依赖

"chalk": "^4.0.0", // 命令行样式
"commander": "^10.0.0", // 命令行
"compressing": "^1.9.0", // 文件压缩解压
"inquirer": "^8.0.0", // 命令行交互
"node-ssh": "^13.0.1", // 连接服务器
"ora": "^5.0.0", // 命令行loading样式
"shelljs": "^0.8.5" // 执行shell语句

代码

/src/cmds/index文件
需要在文件首行加上#!/usr/bin/env node标记为使用node解析

#!/usr/bin/env node
const program = require('commander')
program
    .version('0.1.0')

program.command('deploy','部署项目').alias('dp');
program.parse(process.argv);

新建/src/cmds/index-deploy文件,commander会根据program.command('deploy','部署项目').alias('dp');找到该文件,action中就是要执行的方法。

#!/usr/bin/env node
const program = require('commander')
program
    .command('deploy')
    .alias('dp')
    .description('deploy project')
    .action(function () {
        const { NodeSSH } = require('node-ssh') // 连接服务器
        const ssh = new NodeSSH()
        const chalk = require('chalk')
        const ora = require('ora') // 命令行loading动画
        const shell = require('shelljs')
        const compressing = require('compressing') // 进行压缩文件
        const inquirer = require('inquirer') // 命令行交互

        // 引入配置文件
        const { CONFIG } = require('../utils/config.js')
        let config

        const fullname = shell.pwd().toString()

        const path = require('path')
        const distDir = path.resolve(fullname, './dist')
        const zipDistDir = fullname + '\\dist.zip'

        // 拉取当前分支的最新代码
        const pullCode = async () => {
            const loading = ora('拉取代码').start()
            shell.cd(fullname)
            const res = await shell.exec('git pull')
            loading.stop()
            if(res.code === 0) {
                console.log(chalk.green("拉取代码成功"))
            } else {
                console.log(chalk.red('拉取代码失败'))
                process.exit()
            }
        }
        // 项目打包
        const build = async () => {
            const loading = ora('开始打包').start()
            console.log(fullname)
            shell.cd(fullname)
            const res = await shell.exec('yarn build')
            loading.stop()
            if (res.code === 0) {
                console.log(chalk.green('打包成功'))
            } else {
                console.log(chalk.red('打包失败'))
                process.exit()
            }
        }
        // 压缩文件
        const zipDist = async () => {
            console.log(chalk.blue('开始压缩'))
            const loading = ora('开始压缩').start()
            try {
                await compressing.zip.compressDir(distDir, zipDistDir)
                console.log(chalk.green('压缩成功'))
                loading.stop()
            } catch (error) {
                loading.stop()
                console.log(chalk.red(error))
                console.log(chalk.red('压缩失败'))
                process.exit()
            }
        }
        // 连接服务器
        const connectSHH = async () => {
            console.log(chalk.blue('开始连接服务器'))
            try {
                await ssh.connect({
                    host: config.SERVE_HOST,
                    port: config.SERVE_PORT,
                    username: config.SERVE_ROOT,
                    password: config.SERVE_PASSWORD
                })
                console.log(chalk.green('连接成功'))
            } catch (err) {
                console.log(chalk.red(err))
                console.log(chalk.red('连接失败'))
                process.exit()
            }
        }
        // 执行linux 语句
        const runCommand = async (command) => {
            const result = await ssh.exec(command, [], { cwd: config.DEPLOY_PATH })
            console.log(result)
        }
        // 删除目录下所有文件
        const clearFile = async () => {
            const commands = ['ls', 'rm -rf *']
            await Promise.all(commands.map(async (it) => {
                return await runCommand(it)
            }))
        }
        // 部署
        const uploadFile = async () => {
            await pullCode() // 拉取当前分支代码
            await build() // 打包
            await zipDist() // 压缩
            await connectSHH() // 连接服务器
            await clearFile() // 清理原来部署的文件
            try {
                console.log(chalk.blue('开始上传压缩包'))
                await ssh.putFiles([{ local: zipDistDir, remote: `${config.DEPLOY_PATH}/dist.zip` }])
                console.log(chalk.green('上传成功'))

                console.log(chalk.blue('开始解压压缩包'))
                await runCommand('unzip ./dist.zip')
                console.log(chalk.green('解压完成'))

                console.log(chalk.blue('开始删除压缩包'))
                await runCommand(`rm -rf ${config.DEPLOY_PATH}/dist.zip`) //解压完删除线上压缩包
                console.log(chalk.green('删除成功'))
                // 将dist目录中的文件移出来
                await runCommand(`mv -f ${config.DEPLOY_PATH}/dist/*  ${config.DEPLOY_PATH}`)
                await runCommand(`rm -rf ${config.DEPLOY_PATH}/dist`) //移出后删除 dist 文件夹
                console.log(chalk.green('部署完成'))
                ssh.dispose()
                console.log(chalk.yellow('断开连接'))
            } catch (err) {
                console.log(chalk.red(err))
                console.log(chalk.red('上传失败'))
                process.exit()
            }
        }

        inquirer
            .prompt([{
                type: 'list',
                message: '请选择发布环境',
                name: 'env',
                choices: [{
                    name: '96',
                    value: 96
                }, {
                    name: '95',
                    value: 95
                }, {
                    name: '181',
                    value: 181
                }, {
                    name: '183',
                    value: 183
                }]
            }])
            .then(answers => {
                config = CONFIG[answers.env]
                uploadFile()
            })
    }).parse(process.argv)

其中CONFIG提前写好的配置,格式如下

CONFIG : {
    96:{
         DEPLOY_PATH: "/root/project", // 服务器部署路径
         SERVE_HOST: '122.1.0.96', // 服务器地址
         SERVE_PORT: '22', // 端口
         SERVE_ROOT: 'root', // 用户名
         SERVE_PASSWORD: 'root' // 密码
     }
}

运行

代码写好后运行 npm link,就可以在项目所在目录里运行命令行指令mycmd dpmycmd根据你的项目名称而定)进行部署

运行npm unlink接触连接