美文网首页
前端构建 DevOps :搭建 DevOps 基础平台(下)

前端构建 DevOps :搭建 DevOps 基础平台(下)

作者: Cookieboty | 来源:发表于2020-11-15 16:32 被阅读0次

    前言

    基础平台搭建上篇 介绍项目流程设计、数据库搭建、jwt 登录等模块

    基础平台搭建中篇 介绍分支管理设计、webSocket 基础模块

    本篇下将介绍流程管理与提测相关基础模块

    后端模块

    1. DevOps - Gitlab Api使用(已完成,点击跳转)
    2. DevOps - 搭建 DevOps 基础平台(已完成 70%)
    3. DevOps - Gitlab CI 流水线构建
    4. DevOps - Jenkins 流水线构建
    5. DevOps - Docker 使用
    6. DevOps - 发布任务流程设计
    7. DevOps - 代码审查卡点
    8. DevOps - Node 服务质量监控

    前端模块

    1. DevOps - H5 基础脚手架
    2. DevOps - React 项目开发

    后期可能会根据 DevOps 项目的实际开发进度对上述系列进行调整

    流程与提测管理

    流程管理

    基础平台搭建上篇已经介绍过流程的设计,这里再简单解释下

    1. 开发同学创建对应的工程以及分支,进行功能开发
    2. 项目负责人创建流程时,关联多个开发分支,附加需求(需求模块简化成 desc 字段描述,没有单独抽出去)
    3. 流程的状态由关联的分支状态组合,当所关联所有的开发分支状态全部转变为已完成的时候,才会进入下一个状态

    整个项目管理,应该拆解成项目->需求->工程,预留字段,将需求跟流程直接合并在一起,先完成主要功能,后期再进一步的拓展

    提测管理

    1. 开发人员在开发完对应功能进行项目提测
    2. 未关联流程的分支不能进行提测
    3. 提测之后,测试同学介入测试,根据 desc (需求)进行测试
    4. 开发内容再提测之后,才能发布到预发或生产,否则只能在测试环境发布(禁止未测试的需求直接上线)

    不要嫌麻烦,现实中,产品随便提个需求就上,出现问题到处甩锅的情况还少吗?严格卡关也是减轻工作量的一个小助力

    DevOps 开发下篇

    创建流程模块

    image
    import { Post, Prefix, Get } from "egg-shell-decorators";
    import BaseController from "./base";
    
    @Prefix("process")
    export default class ProcessController extends BaseController {
      /**
       * @author: Cookie
       * @description: 创建 devOps 任务流
       */
      @Post("/create")
      public async createProcess({
        request: {
          body: { params },
        },
      }) {
        const { ctx } = this;
        const { username } = this.userInfo;
        const { name, branchIds, workflowTplId, desc } = params;
        const branchStatus = await ctx.service.branch.checkProcess({ branchIds });
        if (!branchStatus)
          this.error({
            msg: "已有分支在流程中",
          });
        const status = await ctx.service.process.createProcess({
          desc,
          name,
          branchIds,
          workflowTplId,
          createdUser: username,
          updateUser: username,
        });
        await ctx.service.branch.updateBranch({
          branchIds,
          opt: {
            processId: status.id,
          },
        });
        this.success(status);
      }
    
      /**
       * @author: Cookie
       * @description: 查询 devOps 任务流
       */
      @Get("/getList")
      public async getProcessList({ request: { query } }) {
        const { ctx } = this;
        const { pageSize = 10, pageNum = 1 } = query;
        const processList = await ctx.service.process.getProcessList({
          pageNum: parseInt(pageNum),
          pageSize: parseInt(pageSize),
        });
        // 联表查询分支信息
        for (let process of processList.rows) {
          const { branchIds } = process;
          process.branches = await ctx.service.branch.getSelfBranchList({
            branchIds,
          });
        }
        this.success(processList);
      }
    }
    

    提测模块

    image
    import { Post, Prefix, Get } from "egg-shell-decorators";
    import BaseController from "./base";
    
    @Prefix("testRecord")
    export default class TestRecord extends BaseController {
      /**
       * @author: Cookie
       * @description: 创建提测记录
       */
      @Post("/create")
      public async createTestRecord({
        request: {
          body: { params },
        },
      }) {
        const { ctx } = this;
        const { id: submitUserId } = this.userInfo;
        const { desc, name, branchIds, testUserId } = params;
    
        const branchStatus = await ctx.service.branch.checkProcess({
          branchIds,
          status: "every",
        });
    
        if (branchStatus)
          this.error({
            msg: "存在未关联流程的分支",
          });
    
        const status = await ctx.service.testRecord.createTestRecord({
          desc,
          name,
          branchIds,
          submitUserId,
          testUserId,
          testStatus: 0,
        });
        this.success(status);
      }
    }
    

    提测消息推送采用邮件(正式)与机器人(即时),提测内容、次数、质量等写入数据库,系统本身也能追踪,作为后期效能评估的辅助

    邮件推送

    提测模块的具体实现代码,我们分为 3 块

    1. 发送邮件使用 nodemailer
    2. 邮件模板使用 nunjucks 模板引擎,配置邮件模板
    3. 邮件前端自定义内容使用 marked 插件解析 markdown 语法
    import { MAIL_CONFIG } from "../../config/default.config";
    
    const marked = require("marked"); // marked 转换
    const nodemailer = require("nodemailer"); // 发送邮件
    const nunjucks = require("nunjucks"); // 模板引擎
    const path = require("path");
    
    // 邮箱配置初始化
    const transporter = nodemailer.createTransport({
      host: MAIL_CONFIG.service,
      secureConnection: true, // 使用 SSL 方式(安全方式,防止被窃取信息)
      port: MAIL_CONFIG.port,
      auth: {
        user: MAIL_CONFIG.user_email, // 账号
        pass: MAIL_CONFIG.auth_code, // 授权码
      },
    });
    
    const htmlModel = ({ storyMail, exitInfo, summitUser, iterationMail }) => {
      const html = nunjucks.render(path.join(__dirname, "./emailTpl/email.njk"), {
        storyMail,
        exitInfo,
        summitUser,
        iterationMail,
      });
      return html;
    };
    
    /*
     * toEmail: String 接收者,可以同时发送多个,以逗号隔开
     * subject: String 标题
     * cc: String 抄送
     * text: String 文本
     * html: Object titleList表头 conterFontList内容
     * attachments: any 附件
     * [
     *  {
         filename: 'img1.png',            // 改成你的附件名
         path: 'public/images/img1.png',  // 改成你的附件路径
         cid : '00000001'                 // cid可被邮件使用
        }
     * ]
     */
    
    interface mailInterface {
      toEmail: string;
      subject: string;
      cc?: string;
      text?: string;
      html?: any;
      attachments?: any;
      storyMail?: any;
      exitInfo?: any;
      summitUser?: String;
      iterationMail?: any;
    }
    
    const sendMail = async (mailOptions: mailInterface) => {
      const {
        toEmail,
        subject,
        cc,
        text,
        attachments,
        storyMail,
        exitInfo,
        summitUser,
        iterationMail,
      } = mailOptions;
      Object.keys(exitInfo).forEach((key) => {
        exitInfo[key] = marked(exitInfo[key]);
      });
      const html = htmlModel({ storyMail, exitInfo, summitUser, iterationMail });
      const mailOpts = {
        from: MAIL_CONFIG.user_email, // 发送者,与上面的 user 一致
        to: toEmail,
        subject,
        cc,
        text,
        html,
        attachments,
      };
      try {
        transporter.sendMail(mailOpts);
        return true;
      } catch (err) {
        console.log(err);
        return false;
      }
    };
    
    export default { sendMail };
    

    钉钉群机器人

    具体参考钉钉机器人文档下面附带具体的实现代码(为了安全且简单,采用加签的安全验证)

    const crypto = require("crypto");
    const secret ="";
    const sendUrl =""; // 替换成自己的
    
    export default (app) => {
      return {
        async send(content) {
          const timestamp = Date.now();
          const str = crypto
            .createHmac("sha256", secret)
            .update(timestamp + "\n" + secret)
            .digest()
            .toString("base64", "UTF-8");
    
          try {
            const { res, data } = await app.curl(
              `${sendUrl}&timestamp=${timestamp}&sign=${encodeURIComponent(str)}`,
              {
                headers: {
                  "Content-Type": "application/json; charset=utf-8",
                },
                method: "POST",
                data: JSON.stringify(content),
              }
            );
            return res;
          } catch (error) {
            return error;
          }
        },
        text({ content = {}, at }) {
          console.log("content===>", content);
          at = at || {};
          this.send({
            msgtype: "text",
            text: {
              content,
            },
            at,
          });
        },
      };
    };
    
    // 测试机器人 Controller
    import { Post, Prefix, Get } from "egg-shell-decorators";
    import BaseController from "./base";
    
    @Prefix("robot")
    export default class ProjectController extends BaseController {
      @Post("/ding")
      public async getProjectList({
        request: {
          body: { params },
        },
      }) {
        const { ctx } = this;
        const { content } = params;
        await ctx.helper.robot.ding.text({ content });
        this.success({});
      }
    }
    
    image

    上述只附带了 text 文本消息推送,markdown、link、FeedCard 等其他消息类型,照着例子直接上手改就行了

    建议

    从第一篇看到目前这篇博客的同学,如果团队缺少合适的项目管理或者想练习 node 的情况下,可以上手试试看,一般关键的代码,我有直接贴在博客上(大部分复制就能用啊)。

    后面的内容就是贴合业务直接 curd 代码,基础篇到此结束。

    下一篇就会出构建篇,团队可以结合自己项目实际情况增减功能,完善团队基础管理流程。

    不明白的地方可以留言

    尾声

    此项目是从零开发,后续此系列博客会根据实际开发进度推出(真 TMD 累),项目完成之后,会开放部分源码供各位同学参考。

    为什么是开放部分源码,因为有些业务是需要贴合实际项目针对性开发的,开放出去的公共模块我写的认真点

    为了写个系列博客,结果要写完一整个系统(不是一般的累),觉得不错的同学麻烦顺手三连(点赞,关注,转发)。

    相关文章

      网友评论

          本文标题:前端构建 DevOps :搭建 DevOps 基础平台(下)

          本文链接:https://www.haomeiwen.com/subject/cbinbktx.html