GitHub Issue模板实战:用bug_report.yml配置高效Bug表单
1. 先从两个崩溃瞬间说起没有Issue模板的仓库到底有多难维护1.1 崩溃瞬间一一条“help”消息引发的半小时排查如果你维护过一个稍微有点人气的 GitHub 仓库那你大概率经历过这种场景深夜一条新 Issue 弹了出来标题是“help”正文只有一句“装不上谁来看看”。你追问系统环境、版本号、完整报错对方隔十分钟回一个词到最后你才发现根因和他最初的描述完全不是一回事。我自己的一个练习项目就经常这样。读者不多Issue 倒是收了不少大多数反馈长这样“报错了”“运行不了”“请问支持 Windows 吗”。没有版本号、没有操作系统、没有日志。我作为唯一的维护者每天的工作变成了客服式追问。有一次对方只发了一张截图截图里是终端的一小段连输入的命令都没显示完整。我让他贴一下命令原文他说“就是 README 里的那个”我让他贴报错他反问“报错不都在截图里吗”。来回四轮才确认他用的根本不是最新代码而是三个月前的 release问题根源早就修掉了。这种沟通损耗几乎全部来自“信息没有结构化”。如果没有一个模板提前告诉用户“应该填什么”用户就真的不知道该填什么。这不是用户不配合而是仓库没给出配合的方向。1.2 崩溃瞬间二信息被拆成五条评论的Bug报告另一次有人提交了一份很认真的 Bug 报告——认真到让人感动。他写了很长一段文字描述了他做的操作、看到的输出、以及他预期的结果。但关键信息依然缺了系统环境、依赖版本、复现顺序。文字里隐含了一个前提条件“我先改了配置文件”等到我回复时他又补充了两条评论。最终整个 Issue 变成了一个跨评论拼图后面参与讨论的人根本不知道哪条信息属于哪个环节。这件事给我的触动比第一条“help”更大。因为问题不在态度而在流程。那位贡献者已经尽力表达了但表达得越认真信息越散落。如果当时仓库里有一个结构化的 Bug 提交表单他会按照字段把环境、版本、步骤、日志填得清清楚楚我们至少能省掉三轮来回。1.3 模板真正改变的不是格式而是协作的“入口”我现在对模板的价值定义非常简单它把维护者脑子里的“问题诊断清单”前置到了用户提交之前。你在模板里问什么用户就会回答什么你罕见地漏问什么用户多半也不会主动补充。所以配置模板的过程本质上是把你过去处理 Issue 的经验结构化。常见 Bug 报告最核心的几个信息就是项目版本、操作系统、复现步骤、实际结果、期望结果再加上一次“你是否搜索过已有 Issue”的自我确认。有了这些字段一份报告里的有效信息量会从 20% 提升到 80% 以上。别把模板当成形式主义它其实是开源协作里性价比最高的基础设施。这也是为什么我强烈建议每个仓库都配上模板尤其是使用bug_report.yml这类 YAML 表单。2. 用哪一种“模板”Markdown模板和YAML表单的核心差异2.1 传统Markdown模板能提供框架却拦不住乱填先说传统方案。它本质上是一个后缀为.md的文件放在.github/ISSUE_TEMPLATE/目录下文件头部用 YAML front matter 写name、about、title、labels、assignees正文就是一段预设的 Markdown 文本。--- name: Bug report about: 提交一个 Bug 帮助项目改进 title: [Bug]: labels: [bug] assignees: --- **描述 Bug** 请描述你遇到的问题。 **复现步骤** 1. 2. 3. **环境信息** - 版本 - 系统它的优点是很轻写起来几乎没有成本。但缺点也很明显GitHub 对 md 模板不做任何字段校验用户可以把所有小节删掉只剩一行字直接提交。你精心设计的“复现步骤”“环境信息”对不想填的人来说就是几行注释而已。2.2 YAML Issue Forms必填校验、下拉选项与自动标签从 2021 年开始GitHub 逐步开放了 Issue Forms 能力也就是用.yml/.yaml文件描述一张真正的表单。用户看到的不是文字模板而是单行输入框、多行文本框、下拉菜单、复选框。有些字段可以设置必填提交时如果为空GitHub 会直接提示这条 Issue 根本发不出去。它和 Markdown 模板的核心差异可以浓缩成一张表对比项Markdown 模板YAML Issue Forms文件后缀.md.yml / .yaml字段校验无必填项可强制下拉选择不支持支持复选清单不支持支持自动标签front matter 支持头部字段支持用户跳过格式很容易比较难需要说明的是YAML Issue Forms 并不是要全面取代 Markdown 模板。md 模板更适合流程化文本比如发布公告、权限申请、固定格式的通知而 YAML 表单适合数据化收集比如 Bug 报告、功能建议。但对大部分仓库来说日常维护中真正需要的是 YAML 表单也就是标题里的bug_report.yml。2.3 动手前的三件事路径、文件扩展名与仓库权限配置模板之前先确认三件事否则后面所有步骤都是白做。第一模板目录必须在仓库的默认分支。所谓默认分支通常叫main或master但也可以是别的名字以仓库设置页展示的为准。模板推到非默认分支是无效的后面我会单独讲这个坑。第二文件扩展名可以写成.yml也可以写成.yamlGitHub 官方示例一般用.yml。我建议统一用.yml避免团队里两个人各写一种风格。第三仓库必须启用 Issues 功能。在仓库的Settings - General - Features里把 Issues 选项勾上。如果 Issues 没开启模板配置得再漂亮也不会出现在任何入口。确认完这些我们再开始写真正的配置文件。3. bug_report.yml 逐行拆解写一个能自动打标签的Bug表单3.1 头部字段name、description、title、labels、assignees 的作用一个 YAML 表单的头部负责两件事定义这张卡片在“新建 Issue”页面如何展示以及提交后自动应用哪些元数据。常用的有五个字段。name模板的名字显示在模板选择卡片上。注意用户看到的不是文件名而是name的值。description卡片上的副标题一句话说清楚这个模板给谁用。title用户进入表单后预填的标题。Bug 模板常用[Bug]: 作为前缀用户只需要在后面补内容。labels提交后自动打上的标签是一个数组。这里要特别提醒标签必须已经存在于仓库中如果标签不存在GitHub 会静默忽略不会自动创建。assignees提交后自动指派给谁也是一个数组。只有仓库协作者才会被成功指派普通人名会被忽略。name: Bug report description: 提交一个 Bug帮助项目变得更好 title: [Bug]: labels: [bug, triage] assignees: - project-owner这里的title必须加引号因为值以[开头不加引号会被 YAML 解析成数组。这个细节很多人会踩后面专门讲。3.2 body里的五类元素从提示信息到复选清单body是整个表单的主体它是一个数组每个数组项通过type字段声明元素类型。新手最容易搞混的也是这一部分我逐个说清。markdown是最简单的类型只用来显示提示信息不收集任何数据。它的核心是attributes.value支持用|写多行文本适合在表单开头放“提交前请先搜索已有 Issue”这类引导。input是单行文本输入框适合填版本号、用户名、链接。它的attributes里有label、description、placeholder、value四个可选字段其中label是必须的其他按需使用。textarea是多行文本框适合写复现步骤、粘贴日志。它可以设置attributes.render指定语法高亮语言比如render: shell或render: python。这样用户粘贴日志后在渲染视图里会变成带高亮的代码块阅读体验好很多。dropdown是下拉选择。这里要注意一点它是单选不是多选。如果需要让用户勾选多个选项应该用checkboxes。dropdown的选项写在attributes.options下每个选项用-开头。checkboxes是复选清单每个选项是一个对象含label和required两个字段。它非常适合放“我已检查过已有 Issue”“我确认提供了完整日志”这类硬性确认项。以上每种元素除了markdown都可以通过validations.required: true设置为必填。但要注意GitHub 的这个校验只检查“是否为空”不检查格式。比如你希望用户输入 IP 地址这里做不到格式校验只能保证他填了东西。3.3 一份开箱即用的 bug_report.yml 完整示例我把上面所有类型串起来写一份可以直接复制使用的完整配置。你只需要把标签名、默认标题前缀改成自己仓库的即可。name: Bug report description: 提交一个 Bug帮助项目变得更好 title: [Bug]: labels: [bug, triage] assignees: [] body: - type: markdown attributes: value: | 感谢你抽出时间填写这份 Bug 报告。 提交前请先搜索已有 Issue避免重复提交。 - type: input id: version attributes: label: 项目版本 description: 你正在使用的版本号可以从 README 或包管理器的输出里找到 placeholder: 例如 v1.4.2 validations: required: true - type: dropdown id: os attributes: label: 操作系统 description: 你运行项目的系统环境 options: - Windows 10/11 - macOS - Ubuntu / Debian - CentOS / RHEL - 其他 validations: required: true - type: textarea id: reproduce attributes: label: 复现步骤 description: 请按顺序填写越简洁越好 placeholder: | 1. 打开终端 2. 执行 xxx 命令 3. 程序抛出错误 render: shell validations: required: true - type: textarea id: actual attributes: label: 实际结果 description: 描述你实际看到的现象 render: shell validations: required: true - type: textarea id: expected attributes: label: 期望结果 description: 描述你希望发生的现象 - type: checkboxes id: checklist attributes: label: 提交前确认 options: - label: 我已搜索过已有 Issue确认没有重复 required: true - label: 我提供了完整的版本、系统与复现步骤 required: true - label: 如果需要我可以提供最小复现仓库注意几个地方id是可选的但如果填了所有元素里的id必须唯一。dropdown和textarea的required是否开启取决于你对信息质量的容忍度。我自己的习惯是凡是要花时间排查的信息全部设为必填宁可在用户提交时拦一下也不想在 issue 里来回追问。3.4 预览与验证别等用户提交了才发现格式错误写完 yml 文件别急着复制到仓库。先在本地做一次语法校验避免 YAML 缩进问题导致表单无法渲染。最简单的办法是用 Python 的 PyYAML一行命令搞定python3 -c import yaml; yaml.safe_load(open(bug_report.yml)); print(YAML ok)如果你的环境里没有 PyYAML也可以直接用 Node.jsnpx --yes js-yaml bug_report.yml本地校验只能保证语法正确不能保证表单渲染符合预期。所以更推荐的做法是把.github/ISSUE_TEMPLATE/目录推到一个测试仓库或者直接推到你自己的仓库默认分支然后打开Issues - New issue选择 Bug report 模板完整地走一遍提交流程。你可以故意留空一个必填项看看 GitHub 是否会提示再填一遍全部内容看看渲染出来的结构是否清晰。我的经验是第一次配置时表单往往没问题但字段顺序、提示文案、下拉选项这些细节只有真的“以用户视角”填一遍才能发现问题。不要跳过这一步。4. config.yml把“新建Issue”页面做成分流闸门4.1 blank_issues_enabled是否允许空白Issue仓库里除了模板文件还应该有一个config.yml。这个文件不定义具体模板而是控制整个“新建 Issue”页面的行为。其中最重要的字段是blank_issues_enabled。blank_issues_enabled: false把blank_issues_enabled设为false后用户点击New issue时列表中不再显示“Open a blank issue”这个空白入口。所有反馈都会被引导到模板或下方定义的链接里去。这个设置在公开仓库里尤其有用可以过滤掉大量“一句话求助”。但我要诚实地说一句这个开关不是绝对的封禁。一些直接访问特定链接的方式仍然可能绕过它所以更准确的理解是“默认不提供空白入口”而不是“从技术上禁止空白 Issue”。它承担的工作是引导不是强制。即便如此我在自己的仓库里依然设置成了false因为绝大多数用户都会老老实实从页面入口进去。4.2 contact_links把咨询、建议引导到讨论区contact_links是config.yml的另一个核心配置。它可以在模板列表下方添加一组外部链接把不适合走 Issue 的请求引走。最常见的用途是把问题咨询引导到 Discussions 讨论区把安全漏洞引导到专门的提交通道。blank_issues_enabled: false contact_links: - name: 项目讨论区 url: https://github.com/your-org/your-repo/discussions about: 提问、求助、交流使用方法请到讨论区 - name: 安全漏洞上报 url: https://github.com/your-org/your-repo/security/policy about: 请勿在公开 Issue 中提交安全相关漏洞每个链接由name、url、about三个字段组成。url必须是完整的链接地址about是一句说明展示在链接下方。我见过不少仓库把“功能建议”也放进contact_links只保留 Bug 模板。这样页面更干净Issue 区只处理明确的缺陷讨论区承担脑暴。如果你的项目阶段还比较早我也建议这样做功能讨论先发生在讨论区真正被确认采纳后再规规矩矩地进入开发流程。4.3 多模板并存时的编排方法当仓库里同时存在多个模板时GitHub 默认按文件名的字典序排列模板卡片。如果你希望某个模板排在最前面最简单的方法是给文件名加数字前缀比如.github/ISSUE_TEMPLATE/ ├── config.yml ├── 1_bug_report.yml ├── 2_feature_request.yml └── 3_documentation.yml这样模板列表会按照1、2、3的顺序展示Bug 模板永远排在第一位。config.yml里的contact_links显示在模板列表下方适合作为最后的兜底出口。还有一个细节当仓库里只有一个模板时点击New issue会直接进入该模板表单不会先出现选择页面当模板数量变成两个或更多时才会先展示选择卡片。所以在调试阶段如果你只有一个模板看到的效果和预期可能不太一样这是正常行为。5. 上线前绕开这五个坑大小写、分支与YAML缩进5.1 坑一目录名大小写写错模板静默失效官方文档要求的目录路径是.github/ISSUE_TEMPLATE/注意ISSUE_TEMPLATE是大写。GitHub 对这个路径是精确匹配的写成.github/issue_template或.github/Issue_Template模板可能被直接忽略而且不会报任何错误。这条坑之所以隐蔽是因为很多开发者在 Windows 或 macOS 上测试时文件系统本身对大小写不敏感本地目录都一样推到 GitHub 上才暴露问题。我建议你推完后直接在 GitHub 网页端进入.github目录确认文件夹名确实是大写的ISSUE_TEMPLATE。5.2 坑二config.yml 放错位置空白Issue照常出现config.yml必须和模板文件放在同一个目录也就是.github/ISSUE_TEMPLATE/config.yml。放到.github/config.yml、根目录或者其他地方都不会生效。我之前见过一个仓库模板文件能正常显示但blank_issues_enabled: false完全没有作用排查了半天才发现是config.yml放到了.github根目录下。另一个容易混淆的点是 GitLab。GitLab 的 Issue 模板路径是.gitlab/issue_templates和 GitHub 完全不同。如果你同时维护两个平台的仓库很容易迁移时把路径搞混我就在自己的项目里犯过这个错。5.3 坑三YAML缩进错位表单直接红屏YAML 对缩进极其敏感。最典型的错误是把dropdown的options写到attributes外面# 错误写法 - type: dropdown attributes: label: 操作系统 options: - Windows正确的写法是options必须缩进到attributes里面# 正确写法 - type: dropdown attributes: label: 操作系统 options: - Windows这个错误很隐性因为部分解析器仍然能读出内容但表单渲染会失败或被当成非法配置。我建议在编辑器里开启 YAML 树状视图或者直接看缩进层级确保attributes和它的子字段始终是父子关系而不是平级关系。5.4 坑四模板没推到默认分支全部白忙模板目录必须存在于仓库的默认分支。如果你的默认分支是main但把.github/ISSUE_TEMPLATE/推到了dev分支那么新建 Issue 时完全看不到模板。解决办法有两种一是把.github相关文件合并或推送到默认分支二是如果项目的历史原因导致默认分支不是你想用的分支可以在仓库的Settings - General - Default branch里修改默认分支。改完之后模板配置才会在新建 Issue 页面里生效。别问我为什么这个顺序这么重要我就是那个把模板推到 dev 分支之后在 main 上苦苦找不到模板的人。5.5 坑五title里的中括号差点被 YAML 当成数组title字段如果写成title: [Bug]: xxxYAML 会把以[开头的值解析成数组最终表单标题可能变成一串奇怪的表示而不是[Bug]: xxx。解决方法是加引号title: [Bug]: 如果你用单引号也可以。这个问题同样会出现在其他以[、{、#开头的内容里。比如你想在某个description里写“以 # 开头的行会被忽略”最好也给整个值加上引号否则#会被当成注释。我建议凡是看起来不纯的字符串一律用引号包裹省得 YAML 自作聪明。关于这些坑我后来养成了一个习惯每次改完模板先在自己的仓库里新建一条 Issue 走一遍全流程然后用它当测试样本。模板这东西自己按下提交键之前永远不知道用户会看到什么。折腾一次后面能安静很久。