自动部署

225 阅读3分钟

前言

本文适合了解node、shell,想自动部署的前端开发。

在上文前端工程--“巨无霸解耦”中,我们已经将 “巨无霸” 工程拆分成多个独立的小工程,那么当我们遇到下面场景的时候,我们需求怎么去维护呢?

  • 统一修改小工程的配置项,如何提交?
  • 怎么构建部署整个应用?

处理

当面对大量重复工作内容,最好的做法就是交给别人去完成,但前端已经站在生态链最顶端,没别人了,只能交给电脑完成。 10B2D404.png

逻辑图

stateDiagram-v2
start --> 电脑: 获取指令
电脑 --> dist: 按照指令构建
dist --> nginx: 上传dist目录到nginx
nginx --> end

批处理代码

获取指令

// shell脚本
// 获取构建即将构建app,并保存到变量name
read -p "输入构建childApp名称:" name;

自动构建

// shell脚本
// 跳转目录拉代码并直接构建包
deploy(){
    cd $1
    git pull
    npm i
    npm rebuld node-sass
    npm run build
}

$1表示执行deploy方法的第一个参数

自动部署

就是由脚本将指定内容上传到指定服务的指定位置,我们前端使用scp2上传的较多。

安装scp2

npm install scp2

使用scp2

// 使用node执行的js

// 服务的配置信息
const SERVER_CONFIG = {
    host:*******,  //ip
    port:*******,  //端口
    username:****, //用户名
    password:****, //密码
    readyTimeout:500000,//连接超时
    
    path:******,   //上传的ngxin绝对路径
    dist:*****,    //需要上传的dist路径,相对当前js的路径
}

const scpClient = require("scp2");
scpClient.scp(
    SERVER_CONFIG.dist,
    {
        host:SERVER_CONFIG.host,
        port:SERVER_CONFIG.port,
        username:SERVER_CONFIG.username,
        password:SERVER_CONFIG.password,
        path:SERVER_CONFIG.path,
        readyTimeout:SERVER_CONFIG.readyTimeout
    },
    function(err){
        if (err) {
            logs(chalk.red(`发布失败`))
            throw err
        } else {
            logs(chalk.green(`发布成功`))
        }
    }
)

PS:单独使用scp虽然已经满足我们的需求,但是用户体验并不好,只会在最后的时候给一个结果,所以我们调整一下,增加清缓存和上传的各种logs。

调整之后的代码如下:

// deploy.sh

# 获取用户输入
run(){
    showMapInfo
    read -p "输入构建子应用名称:" name
    
    showNginxMapInfo
    read -p "设置部署nginx的配置:" config
    
    if [ "$name" == "master" ]; then
        deploy master

    elif [ $name == "childApp1" ]; then
        deploy childApp1

    elif [ $name == "childApp2" ]; then
        deploy childApp2
    elif [ $name == "childAppn" ]; then
        deploy childAppn
    fi
}

run

# 映射关系
showMapInfo(){
    echo "即将构建的childApp:"
    echo "childApp1   -->  childApp1"
    echo "childApp2   -->  childApp2"
    echo "childApp3   -->  childApp3"
}

# nginx配置映射
showNginxMapInfo(){
    echo "即将使用的nginx配置:"
    echo "childApp1   -->  childApp1"
}

# 打包发布
deploy(){
    echo "$1 应用开始构建**************"
    echo "../$1"
    cd ../$1
    
    echo "git pull"
    git pull 

    echo "git branch"
    git branch

    echo "npm i"
    npm i
    
    npm rebuild node-sass

    echo "npm run build"
    npm run build
    echo "$1 应用构建完成"

    if [ $config == ""]; then
        echo "即将使用默认配置部署**************"
    else
        echo "即将使用 $config 配置进行部署**************"
    fi

    node ./index.js $1 $config  #构建完成,使用node上传nginx
}

执行deploy.sh:在同一目录下git bash中输入 sh deploy.sh

image.png

// index.js
const scpClient = require('scp2');
const ora = require('ora');     // 转圈圈
const chalk = require('chalk'); // 颜色
const SSH2 = require('ssh2');   //清缓存

const childAppName = process.argv[2]; //接受传过来的环境变量$1
const nginxName = process.argv[3];  //接收传过来的环境变量$config

//通过 childAppName 和 nginxName 匹配正在的 SERVER_CONFIG
const SERVER_CONFIG = require(`./${nginxName}`)[childAppName]; //注意这里的数据结构

//开启转圈圈
const spinner = ora()
spinner.start();

// 清缓存
const Client =SSH2.Client;
const conn = new Client();
conn.on("ready", function () {
    logs("开始清缓存...");
    spinner.text = "正在删除缓存..."
    conn.exec(`rm -rf ${server.path}`, rmCallback)
}).connect({
    host: SERVER_CONFIG.host,
    port: SERVER_CONFIG.port,
    username: SERVER_CONFIG.username,
    password: SERVER_CONFIG.password,
    readyTimeout: SERVER_CONFIG.readyTimeout,
})

function rmCallback(err, steam){
    if (err) {
        spinner.stop();
        logs(chalk.red("删除缓存失败"))
        throw err
    };
    logs("删除缓存成功")
    steam.on('close', uploadFile);
    conn.end();
}

// 上传包
function uploadFile() {
    logs("开始发布...");
    logs(`正在发布(${server.host}:${server.port} ${server.path})...`);
    spinner.text = `正在发布(${server.host}:${server.port} ${server.path})...`;
    scpClient.scp(
        server.outputDir,
        {
            host: SERVER_CONFIG.host,
            port: SERVER_CONFIG.port,
            username: SERVER_CONFIG.username,
            password: SERVER_CONFIG.password,
            readyTimeout: SERVER_CONFIG.readyTimeout,
            path: SERVER_CONFIG.path,
        },
        function (err) {
            if (err) {
                logs(chalk.red(`${appName}发布失败`))
                throw err
            } else {
                logs(chalk.green(`${appName}发布成功`))
            }
        }
    )
}

// 打印日志
function logs(str) {
    setTimeout(() => {
        console.log(`${new Date().toLocaleTimeString()}${str}`)
    }, 0)
}

最终执行结果: image.png

总结

适用的场景

  • 前端解耦后,组装目录,对外提供全量gz包
  • 自动部署到nginx
  • 自动打tag等相关git操作

常用的命令

命令说明样例
read命令行获取输入read -p "输入构建子应用名称:" name
mkdir创建目录mkdir dist\childApp1
copywindow下复制copy D:\编程项目\2020\组件库\childApp1\src\components\TongComponents\js\* D:\编程项目\2020\组件库\childApp1\dist\js
tar压缩目录tar -czvf childApp1.tar.gz childApp1
npm publish发布仓库npm publish --registry=****