最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 搭建一个自动化部署服务

    正文概述 掘金(Jiuto)   2021-03-15   588

    搭建一个自动化部署服务


    编写部署服务

    1. 部署服务项目的目录结构

    搭建一个自动化部署服务

    static:用于存放目标项目打包后的文件

    statichtml:用于存放部署服务的前端界面

    app.js:部署服务的后端

    1. 部署服务的依赖
    "devDependencies": {
        "koa": "^2.13.0",
        "koa-mount": "^4.0.0",
        "taskkill": "^3.1.0"
    }
    
    1. 部署服务的后端

    app.js

    const fs = require('fs');
    const koa = require('koa');
    const mount = require('koa-mount');
    
    const app = new koa();
    const reloadKoa = new koa();
    
    app.use(
        mount('/favivon.ico', function(ctx){
            ctx.status = 200;
        })
    )
    
    app.use(
        mount('/reload', reloadKoa)
    )
    
    reloadKoa.use(
        async function(ctx, next){
            // 部署功能
        }
    )
    
    app.use( 
        mount('/', function(ctx) {
            ctx.body = fs.readFileSync(__dirname + '/statichtml/index.html', 'utf-8');
        })
    )
    
    app.listen(8003);
    
    

    监听8003端口,访问'/'根目录时,返回/statichtml/index.html页面。

    1. 部署服务的前端

    /statichtml/index.html

    <div class="form">
        <label class="label">分支名:</label>
        <input id="input" class="input"type="text" value="master" />
        <br/>
        <label class="label">连接环境:</label>
        <select id="select" class="select">
            <option value ="http://127.0.0.1:3001/">开发环境</option>
        </select>
        <br/>
        <button class="btn" id="btn" onclick="handleClick()">部署</button>
    </div>
    <div id="output"></div>
    
    function handleClick(){
        var $btn = document.getElementById("btn");
        var $output = document.getElementById("output");
    
        var $input = document.getElementById("input");
        var $select = document.getElementById("select");
        var branch = encodeURI($input.value);
        var proxy = encodeURI($select.value);
        
        $btn.disabled = true;
        $btn.classList.add("disabled");
        $output.innerText = "正在部署,请耐心等待";
    
        fetch(`http://${location.host}/reload?branch=` + branch + `&proxy=` + proxy)
        .then((res)=>{
            return res.text()
        }).then((text)=>{
            $output.innerText = text;
            $btn.disabled = false;
            $btn.classList.remove("disabled");
        })
    }
    

    前端页面最简易的情况只需要一个部署按钮,用于发起部署请求。

    可增加一些表单交互,用于拓展部署服务功能,例如分支选择,目标项目连接的后端ip等等。

    点击部署按钮,请求当前服务端口的'/reload'接口,带上分支和代理ip两个参数,对接口返回的text进行展示,并取消按钮禁用。

    1. 在编写部署功能功能前,先声明一些常量、变量和方法
    // 这些参数均可从前端配置
    const protocol = "https"; // git clone 命令的协议
    const projectUrl = "github.com/Jiuto/ding_yapi.git"; // git clone 命令的项目地址
    const projectDir = __dirname + '/ding_yapi'; // 目标项目名
    const staticDir = __dirname + '/static/dist'; // 存放打包文件的路径
    const buildStaticDir = __dirname + '/ding_yapi/client/dist'; // 目标项目打包后的文件路径
    
    const git_username='username'; // git 用户名
    const git_password='password'; // git 密码
    
    // 目标项目启动的进程
    var subprocess = null;
    
    // 删除文件
    function deleteFolder(path) {
        var files = [];
        if (fs.existsSync(path)) { // 目录存在
            if (fs.statSync(path).isDirectory()) { // 文件夹
                files = fs.readdirSync(path); // 返回所有文件名数组
                files.forEach(function (file, index) {
                    var curPath = path + "/" + file;
                    if (fs.statSync(curPath).isDirectory()) { // 文件夹
                        deleteFolder(curPath);
                    } else { // 文件
                        fs.unlinkSync(curPath);
                    }
                });
                fs.rmdirSync(path);
            } else { // 文件
                fs.unlinkSync(path); // 删除文件
            }
        }
    }
    
    // 拷贝文件
    function copyFolder(from, to) {
        var files = [];
        if (fs.existsSync(to)) { // 目录存在
            files = fs.readdirSync(from);
            files.forEach(function (file, index) {
                var targetPath = from + "/" + file;
                var toPath = to + '/' + file;
                if (fs.statSync(targetPath).isDirectory()) { // 文件夹
                    copyFolder(targetPath, toPath);
                } else { // 文件
                    fs.copyFileSync(targetPath, toPath); // 拷贝文件
                }
            });
        } else { // 目录不存在
            fs.mkdirSync(to); // 创建文件
            copyFolder(from, to);
        }
    }
    
    1. 部署功能
    reloadKoa.use(
        async function(ctx, next){
            // 解析入参
            const query = ctx.query;
            const branch = decodeURI(query.branch);
            const proxy = decodeURI(query.proxy);
    
            // 声明两个变量用于存放拉取代码和更新环境两个功能的执行结果
            var rtn1 = {},
                rtn2 = {};
    
            // 是否重新克隆项目
            if(branch){
                // 删除上一次拉取的项目
                deleteFolder(projectDir);
                // 构建克隆命令
                let cmdStr = `git clone -b ` + branch + ` ` + protocol + `://` + git_username + `:` + git_password + `@` + projectUrl;
                // 调用执行克隆命令的方法
                rtn1 = await reloadCode(cmdStr);  
            }
    
            // 是否切换代理
            if(proxy){
                // 构建启动项目命令,将目标项目部署在8002端口
                let cmdStr = `http-server .\\static\\dist -p 8002 --proxy ` + proxy;
                // 调用执行启动项目命令的方法
                rtn2 = await changeProxy(cmdStr);
            }
    
            // 整合返回结果
            var rtn = {
                code: rtn1.code === 200 || rtn2.code === 200 ? 200 : 500,
                msg: rtn1.msg 
                            ? rtn2.msg
                                ? rtn1.msg + ' and ' + rtn2.msg
                                : rtn1.msg
                            : rtn2.msg
                                ? rtn2.msg
                                : 'parameter can nott be empty'
            }
    
            // 响应
            ctx.status = rtn.code;
            ctx.body = rtn.msg;
        }
    )
    

    执行克隆命令的方法

    function reloadCode(cmdStr){
        return new Promise(function(resolve, reject) {
            try {
                child_process.execSync(cmdStr); // 克隆项目
                process.chdir(projectDir) // 进入子目录
                child_process.execSync(`npm install`); // 安装依赖
                child_process.execSync(`npm run build`); // 打包项目
                process.chdir(__dirname) // 返回根目录
                deleteFolder(staticDir) // 删除原静态文件
                copyFolder(buildStaticDir, staticDir) // 拷贝文件
                resolve({
                    code: 200,
                    msg: 'load code successful'
                })
            } catch (e) {
                reject({
                    code: 500,
                    msg: 'load failed, error message:\n' + e
                })
            }
        })
    }
    

    执行启动项目命令的方法

    function changeProxy(cmdStr){
        var result2 = {
            code: 200,
            msg: ''
        };
        return new Promise(function(resolve, reject) {
            // 项目已启动,则杀死进程
            if(subprocess) {
                (async () => {
                    await taskkill(subprocess.pid);
                })();
            }
            subprocess = child_process.exec(cmdStr); // 启动 http-server
            subprocess.on('error', (err) => {
                result2.code = 500;
                result2.msg += 'http-server failed, error message:\n' + err;
                reject(result2)
            });
            result2.code = 200;
            result2.msg += 'set proxy successful';
            resolve(result2)
        })
    }
    

    启动部署服务

    1. 进入auto_deploy目录,执行pm2 start app.js启动部署服务

    搭建一个自动化部署服务

    随后,在本地8003端口即可看到部署服务的前端页面

    搭建一个自动化部署服务

    自动化部署

    正在部署

    搭建一个自动化部署服务

    dist

    搭建一个自动化部署服务

    httpserver

    搭建一个自动化部署服务


    起源地下载网 » 搭建一个自动化部署服务

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元