使用 Worker 实现发版通知

目前的站点发版是由开发人员自己操作,运营人员通常需要等待开发人员完成发版后才知道进行了发版。

这就导致了如果开发人员在发版后如果忘记了通知运营人员,运营人员就无法知道网站进行了发版。为了解决这个问题,我们使用了 webhook 来实现发版通知。

当我们进行网站发版的时候,会触发一个 webhook, 会收集信息调用机器人发送到工作群中。

目前,我们工作使用的通讯工具是企业微信,所以我们使用企业微信机器人来实现发版通知。

首先需要在企业微信中创建一个机器人,然后获取到机器人的 webhook 地址。

然后我们使用 worker 来实现发版通知。

因为公司使用的是 CloudFlare 作为部署平台,所以我们使用 CloudFlare 的 Worker 来实现发版通知。

具体操作可以查看 CloudFlare Worker 文档。

可以通过 开始使用 - 命令行 这一节来进行简单的入门。

使用 npm create cloudflare@latest 命令创建一个 Worker 项目,然后使用 npx wrangler dev 运行项目。

在 src/index.js 或 src/index.ts 中开始编写我们的代码。

先定义调用机器人的方法:

/**
 * 发送信息
 * @param url 机器人回调地址
 * @param content 通知信息内容
 * @returns 回调响应体
 */
const sendMessage = (url: string, content: string) => {
  return fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      msgtype: 'markdown',
      markdown: {
        content,
      },
    }),
  })
}

因为使用的是 TS 编写的代码,所以我们需要定义一下 Env 接口:

export interface Env {
  // 机器人密钥
  BOT_KEY: string
  // 主分支名称
  MAIN_BRANCH: string
  // github token
  GITHUB_TOKEN: string
  // 人名映射, 因为 github 用户名和真实人名不一致
  USER_NAME_MAP: Record<string, string>
}

接着我们导出一个 Worker 对象:

export default {
  async fetch(request, env ctx): Promise<Response> {
    // 我们的通知逻辑都将在这里编写
  }
}

我们的仓库都是存储在 github 上,因此我们要使用到 github 提供的 webhook, 可参阅 官方文档。

目前,只需要用到 push 事件,因此我们只需要关注 push 事件的请求体即可。可查阅 push 的 说明文档。

因为需要 push 事件传递给 webhook 的数据中不存在是否有 PR 信息。因此我们需要调用到 github 提供的 api,这又需要用到 github 提供的 token。我们通过访问 网页 进行创建。记得为令牌赋予 Pull requests 或 Contents 权限(只读即可)。

如果是公开仓库,那么就不需要使用到 token 了。

具体思路如下:

  • 首先判断是否是发布分支
  • 然后判断是否是 push 事件
  • 再判断是否由 PR 而来
  • 如果是 PR 而来,那么需要获取 PR 的信息
  • 接着,通过提取数据,获取到相关信息
  • 最后,调用机器人发送消息

具体代码如下:

if (gitEvent === 'push' && data.ref === `refs/heads/${env.MAIN_BRANCH}`) {
  // 推送到主分支
  const commits = data.commits

  const lastCommit = commits[commits.length - 1]
  const message = lastCommit?.message ?? '未知提交'
  const num = message.match(/^Merge pull request #(\d+) from .+/)?.[1]

  console.clear()
  console.log(data)

  const name = data.repository.name
  const description = data.repository.description
  // const repo_url = data.repository.html_url
  const push_at = new Date(data.repository.updated_at).toLocaleString('zh-CN', {
    timeZone: 'Asia/Shanghai',
    /** UTC+8 */ hour12: false,
  })
  const pusher = data.sender.id || data.pusher.name

  await new Promise((resolve, reject) => {
    if (num) {
      const queryPullRequestDescriptionUrl = data.repository.pulls_url.replace('{/number}', `/${num}`)
      fetch(queryPullRequestDescriptionUrl, {
        method: 'GET',
        headers: {
          Authorization: `Bearer ${env.GITHUB_TOKEN}`,
          Accept: 'application/vnd.github.v3+json',
          'X-GitHub-Api-Version': '2022-11-28',
          'User-Agent': 'CloudFlare Worker',
        },
      })
        .then((response) => response.json())
        .then(async (data) => {
          const {
            title,
            body,
            user: { id: creater },
            merged_by: { id: operator },
          } = data as Record<string, any>
          resolve(
            [
              '# 发版通知',
              `> 项目仓库:${name}`,
              `> 项目说明:${description}`,
              `> 发版标题:${title}`,
              `> 创建者:${env.USER_NAME_MAP[`${creater}`] || 'ID' + creater}`,
              `> 操作者:${env.USER_NAME_MAP[`${operator}`] || 'ID' + operator}`,
              `> 发版时间: ${push_at}`,
              '',
              ...body.split('\r\n'),
            ].join('\n'),
          )
        })
    } else {
      resolve(
        [
          '# 发版通知',
          `> 项目仓库:${name}`,
          `> 项目说明:${description}`,
          `> 创建者:${env.USER_NAME_MAP[`${pusher}`] || pusher}`,
          `> 操作者:${env.USER_NAME_MAP[`${pusher}`] || pusher}`,
          `> 发版时间: ${push_at}`,
          '',
          ...message.split('\r\n'),
        ].join('\n'),
      )
    }
  })
    .then((content) => {
      console.log({ content })
      if (typeof content === 'string') {
        return sendMessage(baseUrl, content)
      }
    })
    .then((response) => response?.json())
    .then((data) => {
      console.log({ data })
    })
}

使用了 Markdown 格式的信息,这样格式会更好看一些。

如果需要进行测试的话,可以查看 官方给出的教程。

在完成以上代码编写和测试之后,我们可以通过 npx wrangler deploy 命令来将代码部署到 CloudFlare 上。

这样,我们就可以通过 webhook 来实现发版通知了。


使用 Worker 实现发版通知
https://www.inksha.com/archives/untitled-post
作者
inksha
发布于
2025年02月22日
许可协议