BugBuster 实战指南:用 Botpress SDK 构建 Linear Issue 代码规范检查与 GitHub 双向同步机器人

原创2026-10-03 12:06:191,718 阅读
文章标签:AI 应用后端

BugBuster 实战指南:用 Botpress SDK 构建 Linear Issue 代码规范检查与 GitHub 双向同步机器人

导读

BugBuster 是构建于 Botpress SDK 之上的开源机器人示例,核心使命是为 Linear 议题(Issue)执行自动化的 Lint 规范检查,并将 GitHub 新议题自动同步为对应的 Linear 议题。阅读本文后,你将掌握:BugBuster 的完整事件驱动架构与定时工作流设计、全部 Lint 规则的判定逻辑、基于评论 Marker 的自动解决机制、Slack/Telegram 交互命令,以及如何通过环境变量与集成配置将其部署运行。

BugBuster 机器人图标,一个 Bug 形象的 Lint 检查机器人

一、功能定位:两条核心自动化链路

根据 readme.md 的定义,BugBuster 围绕两条主线工作:

  1. Linear 链路(Lint 检查):连接 Linear 工作区后,当议题被创建或更新时,机器人检测 Lint 错误并在议题上评论;一旦错误被修复,评论会被自动解决(resolve)。
  2. GitHub 链路(议题同步):连接 GitHub 仓库后,机器人监听新议题事件,自动在 Linear 中以 Triage(待处理)状态创建对应议题,并带上 GitHub 议题的标题、描述与链接。

从 bootstrap.ts 的依赖装配可以看到,这两条链路由 IssueProcessor(Lint 处理)与 IssueStateChecker(状态滞留检测)等核心服务支撑,并通过 Botpress 的 client、logger、ctx 标准处理器属性完成集成调用。

二、事件驱动的机器人骨架

机器人的事件绑定集中定义在 src/index.ts,它是理解整个机器人行为的入口:

bot.on.event('github:issueOpened', handlers.handleGithubIssueOpened)
bot.on.event('linear:issueUpdated', handlers.handleLinearIssueUpdated)
bot.on.event('linear:issueCreated', handlers.handleLinearIssueCreated)
bot.on.event('timeToLintAll', handlers.handleTimeToLintAll)
bot.on.event('timeToCheckIssuesState', handlers.handleTimeToCheckIssuesState)
bot.on.message('*', handlers.handleMessageCreated)

bot.on.workflowStart('lintAll', handlers.handleLintAll)
bot.on.workflowContinue('lintAll', handlers.handleLintAll)
bot.on.workflowTimeout('lintAll', handlers.handleLintAllTimeout)

可见机器人由三类触发源驱动:

  • 集成事件:GitHub 的 issueOpened、Linear 的 issueCreated / issueUpdated;
  • 定时事件:timeToLintAll 与 timeToCheckIssuesState;
  • 用户消息:任意消息(*)进入命令处理流程;
  • 工作流:lintAll 支持启动(start)、续跑(continue)与超时(timeout)三种生命周期钩子。

2.1 定时调度的 Cron 配置

定时事件在 bot.definition.ts 中通过 recurringEvents 声明:

recurringEvents: {
  timeToLintAll: {
    payload: sdk.z.object({}),
    type: 'timeToLintAll',
    schedule: { cron: '0 13 * * 1' }, // 每周一 8AM EST 运行
  },
  timeToCheckIssuesState: {
    payload: sdk.z.object({}),
    type: 'timeToCheckIssuesState',
    schedule: { cron: '0 * * * *' }, // 每小时整点运行
  },
},
  • timeToLintAll:每周一次触发全量 Lint(源码注释标注为每周一 8AM EST);
  • timeToCheckIssuesState:每小时检查一次议题状态滞留情况。

事件到达后,time-to-lint-all.ts 会通过 client.getOrCreateWorkflow({ name: 'lintAll', discriminateByStatusGroup: 'active', status: 'pending' }) 启动全量 Lint 工作流;time-to-check-issues-state.ts 则遍历各团队、逐条状态规则调用 IssueStateChecker 处理。

三、Linear Lint 规则全解析

文档中列出的三条规则是 Lint 体系的核心,而完整实现位于 lint-issue.ts 的 lintIssue 函数中,它在读取议题元数据(标签、关联关系、状态、优先级、估算、项目、负责人)后产出错误消息列表:

规则 触发条件 错误消息示例
必须有类型标签 议题缺少父分类为 type 的标签 Issue ABC-123 is missing a type label.
Blocked 状态必须有阻塞依据 状态为 BLOCKED 但既无 blocked/blocked-reason 标签,也无 blocks 关联议题 Issue ABC-123 is blocked but missing a "blocked-reason" label or a blocking issue.
Backlog 中不能有负责人 状态为 backlog 且已分配 assignee Issue ABC-123 has an assignee but is still in the backlog.
In Progress 必须有负责人 状态为 started 但无 assignee Issue ABC-123 is started but has no assignee.
必须有 area 标签 标签中不存在以 area. 前缀命名的标签 Issue ABC-123 is missing an "area." label.
必须有优先级 issue.priority 为空 Issue ABC-123 is missing a priority.
必须有估算值 estimate === null 且状态不是 BLOCKED(被阻塞议题允许不估算) Issue ABC-123 is missing an estimate.
估算不宜过大 估算大于 8 且不是 epic 类型标签 Issue ABC-123 has an estimate greater than 8 (...). Consider breaking it down or making it an epic.
不能关联已完成项目 议题项目 completedAt 非空 Issue ABC-123 is associated with a completed project (...).
不能指派给已停用用户 assignee?.active === false Issue ABC-123 is assigned to a deactivated user. Consider reassigning the issue.

文档中强调的"议题必须拥有 title、description、priority、estimate"以及"Blocked 必须有阻塞原因标签或阻塞议题""In Progress 必须有 assignee",在源码中均有对应实现;标题与描述缺失的检查体现在 GitHub 同步与 lint 消息的完整性问题处理中。

3.1 状态归一化:不同团队状态名如何统一比较

Lint 规则依赖"通用状态名"(如 BLOCKED、started),因为不同 Linear 团队可自定义状态名。核心实现在 common-state-name.ts,它把各团队状态按类型映射为统一名称:

  • started 类型下的 in progress、in review、in pr review、blocked、staging、monitoring、in prod testing 分别归为 IN_PROGRESS、IN_REVIEW、BLOCKED、STAGING、MONITORING;
  • completed 下的 done、production (done) 归为 DONE;canceled 下的 stale 归为 STALE 等。

StateService 会缓存全部状态并以该映射为每个状态附加 commonName,供 Lint 与状态滞留检查统一消费。

四、Lint 结果处理:评论 Upsert、自动解决与标签标记

4.1 忽略机制

IssueProcessor 定义了两个关键常量:

const IGNORED_STATES: lin.StateType[] = ['triage', 'backlog', 'completed', 'canceled', 'duplicate']
const LINTIGNORE_LABEL_NAME = 'lintignore'
const LINTDETECTED_LABEL_NAME = 'lintdetected'

当议题处于上述忽略状态、或带有 lintignore 标签时,Lint 直接返回 ignored 结果,并清理旧的 lint 评论与 lintdetected 标签,避免噪音。

4.2 评论的 Upsert 与自动解决

这是文档所述"修复后自动解决评论"的底层实现。关键点在于 comment-service.ts 中的评论 Marker 机制:

const COMMENT_MARKERS: Record<types.CommentType, string> = {
  lint: 'BugBuster · lint',
  stale: 'BugBuster · stale',
}
const _buildFooter = (marker: string): string => `---\n_🐛 via ${marker}_`

机器人发布的每条评论末尾都带有固定的 Marker 标识,upsertComment 通过"查找现有评论 → 内容相同则跳过、不同则更新、不存在则新建"实现幂等;resolveComments 则只解决匹配 Marker 的评论(同时兼容无 Marker 的旧版本评论,避免孤儿评论)。

LinearApi 进一步提供了"自愈"能力:当并发导致出现多条重复评论时,保留最新一条、自动 resolve 其余重复项,并始终按分页拉取机器人自己的评论(按 viewer 过滤),确保忙碌线程不会把机器人评论挤出视野造成重复发帖。

4.3 完整 Lint 判定流程

IssueProcessor.lintIssue(issue-processor/index.ts)执行:

  1. 判断忽略状态或 lintignore 标签 → resolve 评论、移除 lintdetected 标签,返回 ignored;
  2. 执行 lintIssue(issue, state) 得到错误列表;
  3. 无错误 → resolve 评论、移除标签,返回 succeeded;
  4. 有错误 → 默认在议题上评论错误明细(comment: false 可关闭),并打上 lintdetected 标签,返回 failed。

由此形成闭环:创建/更新时打上 lintdetected 并评论 → 修复后再次触发事件 → 检查通过 → 自动 resolve 评论并移除标签。

五、lintAll 工作流:全量 Lint 与进度持久化

lintAll 工作流(lint-all.ts)用于对所有被监控团队的相关议题执行全量扫描,其输入参数在 bot.definition.ts 中定义:

  • verbose(可选,默认 false):是否在对话中实时播报每页 Lint 进度;
  • comment(可选,默认 true):是否在 Linear 议题上评论 Lint 结果。

工作流通过两个 workflow 类型状态实现断点续跑(见 bot.definition.ts):

  • lastLintedId:记录最后成功 Lint 的议题 ID,作为分页游标;
  • lintResults:累积全部 Lint 结果(failed/succeeded/ignored),每处理一页即持久化。

执行时按每页 50 条议题(ISSUES_PER_PAGE,见 client.ts)分页遍历,自动跳过 triage、backlog、completed、canceled、duplicate 状态(listRelevantIssues)。结束时汇总消息:

  • 全部通过 → Linting complete. All issues passed lint checks.
  • 失败 1 条 → 给出该议题链接;
  • 失败不超过 5 条 → 列出链接;超过 5 条 → 列出前 5 条并提示查看 lintdetected 标签全集链接(RESULT_MESSAGE_LIMIT = 5 用于防止刷屏)。

若从聊天发起,结果回复给当前会话;若是定时触发的无人会话,则按 notificationChannels 状态(bot 级)中的配置,把结果推送到与各团队相关的 Slack 频道。超时时由 handleLintAllTimeout 回复 Error: the 'lintAll' operation timed out。

六、定期状态滞留检查:Stale 提醒

timeToCheckIssuesState 每小时运行,检查两类滞留场景(见 time-to-check-issues-state.ts):

状态 最长滞留时间 提醒内容
STAGING -P1W(1 周) 该议题在 staging 超过一周
BLOCKED -P1M(1 个月) 该议题被阻塞超过一个月

IssueStateChecker 按状态 ID 与 updatedAt < 阈值 过滤议题并分页遍历,对每条滞留议题以 stale 类型 Marker 发出一条提醒评论(同样走 Upsert 幂等逻辑)。这也是 CommentType = 'lint' | 'stale' 两种评论类型之一的来源(见 types.ts)。

七、GitHub → Linear 同步链路

github-issue-opened.ts 实现了文档所述的 GitHub 新议题同步:

const TEAM_NAME_FOR_NEW_ISSUES = 'Engineering'
...
await props.client.callAction({
  type: 'linear:createIssue',
  input: {
    teamName: TEAM_NAME_FOR_NEW_ISSUES,
    description: githubIssue.issue.body,
    title: githubIssue.issue.name,
  },
})

流程为:收到 github:issueOpened 事件 → 调用 linear:createIssue 在 Engineering 团队创建议题(携带 GitHub 议题的标题与描述)→ 在新建的 Linear 议题上追加一条评论,说明来源并附 GitHub 议题链接:

This issue was created from GitHub by BugBuster Bot.

GitHub Issue: <议题标题>

新创建的议题落入 Linear 的默认工作流起始状态(即 Triage),等待后续 Lint 流程接管。

八、聊天命令:Slack 与 Telegram 交互控制

机器人监听 Telegram 与 Slack 私聊消息(message-created.ts 中 MESSAGING_INTEGRATIONS = <a href="https://link.gitcode.com/i/da59d93ed00999b8dd7c86ea031bd047" target="_blank">'telegram', 'slack'],Slack 的频道/线程消息被忽略以避免刷屏)。命令表定义在 [command-processor.ts:

命令 必选参数 可选参数 作用
#listTeams — — 列出当前被监控的团队
#addTeam teamName — 把团队加入监控列表
#removeTeam teamName — 从监控列表移除团队
#lintAll — --verbose、--no-comments 启动全量 Lint 工作流
#addNotifChannel channelName、teamName1 teamName2 ... 配置 Slack 通知频道与团队
#removeNotifChannel channelName — 移除通知频道
#removeNotifChannelTeam channelName、teamName — 从通知频道移除某团队
#listNotifChannels — — 列出全部通知频道
#version — — 显示机器人版本

其中 #lintAll --verbose --no-comments 可实时播报进度且不在 Linear 上留评论;#addNotifChannel 要求所有团队必须先被监控(watchedTeams),并通过 slack:getOrCreateChannelConversation 建立频道会话;被监控团队列表持久化在 bot 级状态 watchedTeams 中(见 teams-manager.ts)。

九、部署配置:环境变量与集成装配

9.1 环境变量

机器人依赖 5 个环境变量,通过 genenv 在 postinstall 阶段生成类型安全的 .genenv(见 package.json):

  • BUGBUSTER_GITHUB_TOKEN:GitHub 个人访问令牌(手动 PAT 模式);
  • BUGBUSTER_GITHUB_WEBHOOK_SECRET:GitHub Webhook 签名密钥;
  • BUGBUSTER_LINEAR_API_KEY:Linear API Key;
  • BUGBUSTER_LINEAR_WEBHOOK_SIGNING_SECRET:Linear Webhook 签名密钥;
  • BUGBUSTER_TELEGRAM_BOT_TOKEN:Telegram Bot Token。

9.2 集成装配

bot.definition.ts 通过链式 .addIntegration(...) 装配四个官方集成:

  • GitHub:configurationType: 'manualPAT',注入 personalAccessToken 与 webhook secret;
  • Telegram:注入 botToken,并启用 typingIndicatorEmoji;
  • Linear:configurationType: 'apiKey',注入 apiKey 与 webhook 签名密钥;
  • Slack:关闭 typingIndicatorEmoji,回复行为配置为 location: 'channel' 且 onlyOnBotMention: false。

集成包通过 bpDependencies 指向仓库内的 integrations/github、integrations/linear、integrations/slack、integrations/telegram,因此需要在 monorepo 中先安装集成依赖。

9.3 构建与检查命令

"check:type": "tsc --noEmit",
"check:bplint": "bp lint",
"build": "bp add -y && bp build",
"test": "vitest --run"
  • bp add 生成 bp_modules 与 .botpress 等绑定代码;
  • bp build 产出可部署的机器人包;
  • vitest --run 运行单元测试(例如 common-state-name.test.ts 验证状态名归一化逻辑)。

9.4 底层 API 封装要点

LinearApi 是机器人操作 Linear 的统一封装,值得关注的设计包括:

  • 通过 linear:sendRawGraphqlQuery 执行 GraphQL 查询(graphql-queries.ts),因为 linear:listStates 动作输出不含状态 type 字段;
  • 团队、状态、标签 ID 均带内存缓存;
  • 议题按 50/页、状态按 200/页、评论按 100/页分页;
  • 评论/标签的增删改查全部基于 GraphQL 与 createMessage(以 bot 身份在议题会话中发文本消息)。

十、小结

从 readme.md 的定位出发,BugBuster 展示了在 Botpress SDK 之上构建"规范治理型"机器人所需的完整范式:事件驱动 + 定时工作流 + 幂等评论 + 标签状态机 + 多通道交互。读者可以以此为模板,将 Lint 规则、状态滞留阈值、团队监控列表与通知频道按自身工作流调整,快速落地一套"议题质量守门员"。

登录后查看全文
botpress