GitHub Issues 使用教程:从缺陷反馈到 Issue 驱动开发
GitHub Issues 是仓库的工作跟踪单元:一个编号条目,携带标题、正文、讨论串和一组规划字段(类型、标签、负责人、里程碑、子 issue)。在提问者视角 issue 是反馈入口,在维护者视角是看板上的任务;对自动化来说,issue 还是可订阅的事件源——Pull Request 用关键字关闭 issue,Actions 响应 issue 的 opened/closed 事件运行工作流,REST API 和 Webhook 把 issue 接入外部工具链。
issue types、sub-issues 与高级搜索于 2025-04-09 正式可用,自定义 issue fields 于 2026-07-02 正式可用——这些新对象已经进了 REST 与 Webhook,老教程里的字段模型不再完整。
前置条件
- 有一个可提交的仓库;公开仓库的 issue 任何已登录用户都能创建(仓库在 Settings → Features 的 Issues 下拉里选了 "Collaborators only" 时除外),私有仓库需要账户有相应访问权限。
- 仓库 Settings → Issues 功能已启用(禁用只阻止新建 issue,已有 issue 保留,重新启用后可继续访问)。
- 想在组织仓库上使用 issue types / fields:需要组织管理员先在组织层面启用并配置类型。
- 使用 gh CLI 需已
gh auth login;调用 REST 写接口需要对应权限的 token。
Issue 的对象模型
一条 issue 由三层内容组成:
| 层 | 成员 | 说明 |
|---|---|---|
| 内容 | 标题、Markdown 正文、任务清单、附件 | 正文支持 - [ ] 复选框任务清单 |
| 规划字段 | issue type、labels、assignees、milestone、projects、sub-issues、blocked by/blocking、自定义 fields | 都是筛选与自动化条件 |
| 状态 | open / closed(completed、not_planned、duplicate 三种关闭原因)、locked、pinned | 关闭原因在 API 中是 state_reason 字段 |
模型里新加入的对象值得先看清:
- 默认 issue type 是 Task、Bug、Feature 三种,组织在 Settings → Planning → Issue types 增删自定义类型,全组织最多 25 个,删除是永久操作。类型是单值字段,取代了"用
type/bug标签凑数"的旧惯例。 - Sub-issue 在 issue 之间建立父子关系,每个父 issue 最多挂 100 个子项、关系可嵌套八层;配合 blocked by / blocking,仓库内就能表达依赖图。
- Issue fields 是组织级启用的自定义字段,按类型配置显示;Webhook 与
on: issues事件也随之新增了field_added/field_removed动作类型。 - 一个 issue 最多 10 个 assignees、最多一个 milestone(REST 的
milestone参数是单值),labels 可以多选。
最短可用路径
以"提交一个 bug 并用 PR 关闭"为主线:
- 仓库页面 Issues → New issue,或直接
gh issue create:
gh issue create \
--title "导出 CSV 在大数据量下超时" \
--body "复现步骤与期望行为……" \
--type Bug \
--label needs-triage \
--assignee @me- 修复分支上提交代码,在 Pull Request 描述里写关联关键字:
Closes #123- PR 合并进默认分支的瞬间,issue #123 自动关闭,
Closed this as completed记录在时间线上,PR 与 issue 两侧的 "Development" 区域互见。
成功信号:gh issue view 123 --json state --jq .state 输出 CLOSED;PR 详情页 "Development" 区域显示该 issue 并带勾;时间线上出现关闭事件与关联 PR 链接。
关闭关键字完整清单:close、closes、closed、fix、fixes、fixed、resolve、resolves、resolved,文档化语法只有两种:关键字 #123 和 关键字 OWNER/REPO#123。以下两个条件必须同时成立:关键字写在 PR 描述或 PR 内某条 commit 的 message 里;PR 的目标分支是仓库默认分支。目标为其它分支时不会自动关闭——这是"关键字失效"排错的第一检查点。跨仓库自动关闭要用 OWNER/REPO#123 语法;手动关联则相反——PR 侧 Development 栏只能关联同仓库 issue,从 issue 侧栏可以把其它仓库的 PR/分支关联进来。
生命周期操作
| 操作 | 权限 | 说明 |
|---|---|---|
| 关闭 / 重开 | 参与者 | 关闭时可选 Completed / Not planned / Duplicate(选 Duplicate 需指定被重复的 issue),API 对应 state_reason |
| 转移 transfer | 两仓库均为 write 及以上 | 只能转移处于 open 状态的 issue;源与目标须同属一个账号或组织;gh issue transfer 123 other/repo,评论与 assignee 保留,标签按名称匹配保留,里程碑须名称与截止日期同时匹配才保留;旧链接自动重定向。私有仓库的 issue 不能转移到公开仓库 |
| 锁定 lock | 写权限 | 原因枚举两套写法:REST lock_reason 为 off-topic、too heated、resolved、spam(连字符/空格),gh issue lock --reason 为 off_topic、too_heated、resolved、spam(下划线);锁定后非协作参与者不能评论,有写权限者可解锁 |
| 置顶 pin | — | 每仓库最多 3 个,常用于"必读公告" |
| 转 discussion | triage 及以上 | 提问类 issue 一键转为 GitHub Discussions 帖子,帖子内容取自原 issue |
| 删除 | admin / owner(组织仓库还须 org 拥有者先启用删除策略) | Web UI 侧栏的 Delete issue 可永久删除:个人仓库由账户本人操作;REST 没有删除 issue 本体的端点,gh CLI 提供 gh issue delete {number} --yes;删除后协作参与者访问原 URL 只显示页面不存在。日常误建仍建议用关闭(not_planned)处置 |
规划与检索
Labels:仓库级定义(名称、颜色、描述),Settings → Labels 管理;issue 可多选。label 事件(labeled / unlabeled)是自动化最常用的钩子之一。
Milestone:一组 issue 的目标版本或时间节点,带截止日和进度统计;一个 issue 只属于一个 milestone。
搜索与批量:issue 列表搜索框就是搜索引擎,常用限定符:
is:issue is:open label:"bug" type:Task
assignee:@me involves:octocat no:assignee no:label
linked:issue # 已关联可关闭 issue 的 PR;未关联的用 is:pr -linked:issue
milestone:"v1.0" created:>2026-09-012025-04 起的高级搜索支持嵌套布尔表达式。
模板:让提交者把信息一次说全
issue 模板只认 .github/ISSUE_TEMPLATE/ 目录(根目录或 docs/ 下的模板文件只对 Pull Request 模板成立)。放进去后,"New issue" 按钮变成模板选择页。两种格式:
Markdown 模板——frontmatter 预设元数据:
---
name: Bug report
about: 报告功能缺陷
title: "[BUG] "
labels: [bug, needs-triage]
type: Bug
assignees: []
---
## 复现步骤
1. …
## 期望行为YAML 表单——结构化输入,提交内容按节渲染:
name: Bug report
description: 报告功能缺陷
labels: ["bug"]
type: Bug
body:
- type: markdown
attributes:
value: 请填写以下信息
- type: input
id: version
attributes:
label: 软件版本
validations:
required: true
- type: dropdown
id: browsers
attributes:
label: 影响平台
multiple: true
options: [Linux, macOS, Windows]
- type: textarea
id: logs
attributes:
label: 相关日志
render: shell
validations:
required: true表单元素只有六类:markdown、input、textarea、dropdown、checkboxes、upload(附件上传)。validations 挂在各元素之下而非表单顶层,目前只文档化了 required 一项,且文档标注该字段只对公开仓库生效(upload 元素另有 accept 限定文件扩展名);长度与正则校验仍是社区未实现的功能请求,需要格式约束时,用后文"Actions 自动化"一节的机器人 workflow 在提交后二次检查并回帖提示。checkboxes 常被用作"发布前检查清单"。
config.yml 控制选择页外观:blank_issues_enabled: false 强制普通提交者走模板、维护者仍保留空白 issue 入口;contact_links 挂"提问去 Discussions / 安全问题看 SECURITY.md"之类的外部入口。
gh CLI 日常工作面
gh issue list --state open --label bug --json number,title,issueType,updatedAt
gh issue create --template "Bug report" --title "..." # --template 取模板 name,不是文件名
gh issue develop 123 --checkout # 为 issue 创建并签出关联分支
gh issue comment 123 --body "已定位根因"
gh issue edit 123 --add-label triaged --parent 100
gh issue close 123 --reason "not planned"
gh issue transfer 123 other-org/repogh issue develop 值得单独注意:develop 子命令按仓库配置生成分支(可 --base、--branch-repo 跨仓库),分支名出现在 issue 侧栏,并会被之后的 gh pr create 用作 base 分支;但 issue 与 PR 的正式关联仍靠 PR 正文里的 Closes #123 这类关键字,develop 本身不创建 PR 也不自动关联。gh issue create 支持 --type、--parent、--blocked-by、--assignee @me,gh issue list --json 可输出 issueType、subIssues、blockedBy、stateReason 等 27 个字段,适合脚本化盘点。
REST API 与 Webhook
核心端点(全部以 /repos/{owner}/{repo} 为前缀,另有组织级 GET /orgs/{org}/issues):
| 端点 | 用途 |
|---|---|
GET/POST /issues | 列表 / 创建 |
GET/PATCH /issues/{issue_number} | 详情 / 改标题、正文、状态、state_reason |
POST /issues/{issue_number}/comments | 评论 |
POST/DELETE /issues/{issue_number}/labels 等 | 标签增删与替换 |
GET/POST /issues/{n}/sub_issues、DELETE /issues/{n}/sub_issue(单数)、PATCH /issues/{n}/sub_issues/priority、GET /issues/{n}/parent | 子 issue 关系;设置父级走父 issue 的 POST /sub_issues,查询父级用 GET .../parent(当前 API 版本 2026-03-10) |
PUT/DELETE /issues/{n}/lock | 锁定 |
GET /issues/{n}/timeline | 事件时间线 |
鉴权:fine-grained token 的仓库权限 Issues: read/write 覆盖 issue、评论、标签、milestone;classic token 对私有仓库需 repo scope。速率:认证用户 5,000 请求/小时;Actions 内 GITHUB_TOKEN 为每仓库 1,000 请求/小时,写机器人工作流要按这个预算设计轮询频率。
Webhook issues 事件的 action 集合与 on: issues 类型一致:opened、edited、deleted、transferred、pinned、unpinned、closed、reopened、assigned、unassigned、labeled、unlabeled、locked、unlocked、milestoned、demilestoned、typed、untyped、field_added、field_removed。外部系统对接一般是订阅 issues + issue_comment 两个事件起步,写回评论和状态走 REST。
Actions 自动化
.github/workflows/issue-triage.yml——新 issue 自动打标签并回帖引导:
name: Issue triage
on:
issues:
types: [opened]
permissions:
issues: write
jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
with:
script: |
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['needs-triage']
})
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: '已收到,维护者会在一周内回复。'
})以下规则最容易踩空:
- 触发用的 workflow 文件必须存在于默认分支,特性分支上的同名文件不触发 issue 事件。
- 机器人要写 issue(评论、打标、关闭),必须显式
permissions: issues: write;GITHUB_TOKEN 的初始权限取自企业/组织/仓库的默认设置,不是可依赖的常量。 on: issues若不写types,所有动作都会触发,机器人打标或编辑 issue 又会引发新一轮labeled/edited,容易自我循环;按需收窄类型并在脚本里判断 actor。
issue_comment 事件支持 created / edited / deleted 等动作类型,配合 if: github.event.issue.pull_request 可以区分"给 PR 留评论",实现 @bot retest 这类口头触发。
能基于 Issues 做什么
- Issue 驱动开发:每个任务先建 issue 定验收标准,
gh issue develop开分支(develop 与 PR 关联的边界见上文 gh CLI 一节),PR 正文用Closes #收尾,合并即销账;从需求来源到实现落点的整个过程都留在仓库时间线里。 - 结构化反馈表单是 triage 的第一道闸:YAML form 可以把版本、日志、复现步骤设成必填,配合
blank_issues_enabled: false的强制策略(细节见上文模板一节),省掉反复追问的往返。 - 机器人分诊与通知:
opened触发时按表单答案打标签、指派值班,经 webhook 推到团队聊天频道;typed、field_added等事件可还原谁在何时改了状态,作为审计数据源。 - Epic 可以直接拆在仓库里:父 issue 页面展示其 sub-issues 清单,blocked by / blocking 表达依赖,不用再引外部工具。
- 看板与发布联动:新版 Projects 的状态、负责人字段与 issue 联动,Roadmaps 视图按日期字段排时间线;上一版本之后合并的 PR 会被 Releases 的自动生成发布说明汇总进草稿(细节见 Releases 教程)。
不适合的场景:问答与长期讨论放 Discussions(issue 有锁定成本且搜索语义偏任务);敏感漏洞用 Private Vulnerability Reporting / SECURITY.md,不要公开发 issue;需要私有不可见的内部任务别用公开仓库 issue。
权限与配额边界
| 角色 | 能做什么 |
|---|---|
| 匿名/只读用户 | 公开仓库浏览、搜索、查看反应计数(添加反应等写操作需认证) |
| 任何登录用户 | 公开仓库提 issue、评论(仓库未启用 "Collaborators only" 限制时) |
| triage | 打标签、分配负责人、设置里程碑、关闭任意 issue、转 Discussions |
| write | 转移 issue(目标仓库也需 write)、锁定、编辑模板与标签定义、运行机器人 workflow |
| admin | 启用/禁用 Issues 功能、仓库安全与合并策略 |
issue types 与 fields 都在组织级 Settings → Planning 配置,启用后组织名下仓库通用;fields 按类型决定出现在哪些 issue 上。自定义类型删除为永久操作,经验上建议先在相关 issue 上清除该类型引用再回收。API 配额见上一节;批量脚本注意二级限流(内容生成类请求有独立预算),不要对大仓库做无过滤的全量 GET /issues 轮询。
故障处理
| 症状 | 原因与处理 |
|---|---|
| PR 合并了但 issue 没自动关 | 关键字不在 PR 描述(评论里不算);PR 目标不是默认分支;引用格式写成了 #123 OWNER/REPO 混用。若本次合并不会再触发,直接由维护者手动关闭并在评论里注明对应 PR |
| 新 issue 不触发 workflow | workflow 文件不在默认分支;on.issues.types 拼写或漏 opened;仓库禁用了 Actions |
| 机器人评论报 403 | 忘写 permissions: issues: write,或外部 token 没有 fine-grained Issues 写权限 |
| 模板选择页不出现 | 文件不在 .github/ISSUE_TEMPLATE/ 下(issue 模板放根目录或 docs/ 不生效,那里只用于 PR 模板);YAML form 顶层缺 name/description/body |
--type Bug 报错 | 组织未启用 issue types,或该类型被禁用、名称拼写不符;用 gh api /repos/{o}/{r}/issue-types --jq '.[].name' 核对 |
| sub-issue 加不上 | 已达每父 100 上限;关系最多嵌套八层 |
搜索 label:bug 搜不到 | 标签实际是 type: Bug(新模型)或名称含空格需加引号 label:"help wanted" |
| webhook 收不到事件 | 订阅方没勾 issues 事件;消费端若对 action 做严格枚举校验,需先补上 typed、field_added 等新值 |
验收清单
- [ ] 每个模板都能产出可复现、可分诊的信息;公开仓库的 YAML form 必填项生效,私有仓库用提交后的机器人检查兜底。
- [ ] 团队 PR 规范包含
Closes #,抽查最近 10 个合并 PR 均有关联。 - [ ] 机器人 workflow 显式收窄
types并设最小permissions。 - [ ] issue types / labels 职责不重复:类型表达"是什么",标签表达"怎么流转"。
- [ ] 外部同步脚本按 GITHUB_TOKEN 1,000/h 或 PAT 5,000/h 预算设计频率。
- [ ] 提问类 issue 有转 Discussions 的处理路径。
参考资料
- GitHub Docs:Tracking work with issues、Linking a pull request to an issue、Configuring issue templates、Syntax for issue forms、Syntax for GitHub's form schema
- GitHub Docs:Managing issue types、Adding sub-issues、Transferring an issue、Deleting an issue、Filtering and searching
- GitHub Docs:REST API endpoints for issues、sub-issues、webhook events、rate limits
- GitHub Docs:Events that trigger workflows、About Projects
- GitHub Changelog:Evolving GitHub Issues and Projects、Issue fields are now generally available