GitHub 不只是代码托管平台
很多开发者使用 GitHub 时,主要把它当成远程 Git 仓库:
git push
↓
GitHub
但对于一个真正需要长期维护的项目来说,代码仓库只是 GitHub 的一部分。
一个完整的软件开发过程通常还包括:
需求
↓
Issue
↓
开发
↓
Pull Request
↓
Code Review
↓
CI
↓
Merge
↓
Release
↓
用户
GitHub 恰好把这条链路中的多个环节连接起来。
其中:
Issue 负责描述和跟踪工作;
Pull Request 负责提交代码变更;
Code Review 负责验证代码;
Actions 负责自动化检查;
Ruleset 负责约束仓库规则;
Release 负责向用户交付稳定版本。
GitHub 官方也将 Pull Request 模板、Code Owners、受保护分支和 Rulesets 作为标准化 Pull Request 和保护重要分支的重要工具。(GitHub:管理和标准化 Pull Request)
因此,GitHub 工程化的核心并不是「会使用多少 GitHub 功能」,而是建立一条可追踪、可审查、可自动化、可发布的软件交付流程。
Issue:把需求和问题变成可追踪任务
Issue 是项目管理的起点。
一个功能需求可以创建 Issue:
Feature: 增加用户头像上传
一个 Bug 也可以创建 Issue:
Bug: 上传 10MB 图片时接口返回 500
一个技术任务同样可以创建 Issue:
Task: 升级 PHP 8.3
因此,Issue 不应该只是「报错记录」,而应该成为项目工作项的统一入口。
GitHub 官方也建议使用 Issue 来跟踪 Bug、功能需求、大型工作以及 Release 任务。(GitHub:规划和跟踪项目工作)
一个好的 Issue 应该包含什么
一个高质量 Issue 至少应该回答:
发生了什么?
为什么需要处理?
预期结果是什么?
如何验证?
例如一个 Bug:
## 问题描述
用户上传大于 10MB 的图片时接口返回 HTTP 500。
## 复现步骤
1. 登录系统
2. 打开头像上传页面
3. 上传 15MB JPG 图片
4. 点击提交
## 实际结果
接口返回 HTTP 500。
## 预期结果
接口应该返回文件大小超过限制的提示。
## 环境
- PHP: 8.3
- Hyperf: 3.1
- MySQL: 8.0
这样开发者拿到 Issue 后,可以直接开始定位问题。
使用 Label 分类 Issue
可以建立一套简单的 Label:
type:bug
type:feature
type:task
type:refactor
priority:high
priority:medium
priority:low
status:blocked
area:api
area:web
area:database
area:devops
不要创建几十种没有明确意义的 Label。
Label 的价值在于:
快速分类
+
快速过滤
+
自动生成 Release 信息
尤其是后面 Release 自动生成时,Label 可以直接参与分类。GitHub 支持根据 Pull Request Label 对自动生成的 Release Notes 进行分类。(GitHub:自动生成 Release Notes)
Issue Template:让问题变得标准化
如果每个开发者创建 Issue 时都自由发挥,很容易出现:
这个功能有问题。
为什么?
不知道。
因此,对于长期维护的项目,可以建立 Issue Template。
GitHub 支持 Markdown Issue Template,也支持 YAML Issue Forms。Issue Forms 可以定义输入字段、必填项、默认 Label、Assignee 等,更适合需要结构化信息的项目。(GitHub:Issue 和 Pull Request 模板)
目录:
.github/
└── ISSUE_TEMPLATE/
├── bug.yml
├── feature.yml
└── config.yml
例如 Bug Issue Form:
name: Bug Report
description: 报告一个可以复现的问题
title: "[Bug] "
labels:
- type:bug
body:
- type: textarea
id: description
attributes:
label: 问题描述
description: 请描述实际发生的问题
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: 复现步骤
description: 请提供详细的复现步骤
validations:
required: true
- type: textarea
id: expected
attributes:
label: 预期结果
validations:
required: true
这样创建 Bug 时,开发者必须按照统一格式填写。
GitHub 的 Issue Forms 使用 YAML 定义,并且可以设置字段类型、验证规则、默认 Label 和 Assignee。(GitHub:Issue Forms 语法)
Pull Request:让代码变更进入评审流程
Issue 描述的是:
要做什么。
Pull Request 描述的是:
我做了什么。
因此一个合理的开发流程通常是:
Issue #123
↓
创建 feature 分支
↓
开发
↓
提交 Commit
↓
Push
↓
Pull Request
↓
Code Review
例如:
git checkout -b feature/user-avatar
git add .
git commit -m "feat: add user avatar upload"
git push origin feature/user-avatar
然后创建:
feature/user-avatar
↓
main
的 Pull Request。
Pull Request 应该解决一个明确的问题
一个 PR 最好对应一个 Issue 或一个明确的工作项。
例如:
Issue #123
增加用户头像上传
↓
PR #128
feat: add user avatar upload
↓
Closes #123
GitHub 支持在 Pull Request 中引用 Issue,并可以使用关键字自动关闭关联 Issue,例如:
Closes #123
Fixes #123
Resolves #123
这样当 PR 合并后,对应 Issue 就可以自动完成关闭。
这会形成:
Issue
↓
PR
↓
Merge
↓
Issue Closed
项目的需求状态因此可以自动跟随代码状态变化。
Pull Request Template
如果团队希望所有 PR 都提供完整的上下文,可以增加:
.github/
└── pull_request_template.md
GitHub 支持在仓库根目录、docs 或 .github 目录中配置 Pull Request Template。创建 PR 时,模板内容会自动进入 PR 描述。(GitHub:创建 Pull Request Template)
例如:
## 变更说明
<!-- 描述本次 PR 做了什么 -->
## 关联 Issue
Closes #
## 变更类型
- [ ] Bug Fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Performance
## 测试
- [ ] PHPUnit
- [ ] Static Analysis
- [ ] Manual Test
## 检查清单
- [ ] 已完成代码自测
- [ ] 已更新相关文档
- [ ] 没有提交敏感信息
- [ ] 不包含无关修改
一个好的 PR Template 不应该追求「字段越多越专业」。
它真正需要解决的是:
为什么改?
改了什么?
怎么验证?
有没有风险?
Code Review:不要让 Review 变成形式
Pull Request 创建以后,真正重要的是 Code Review。
Code Review 的目标不是寻找「代码写得不漂亮」,而是确认:
功能是否正确?
设计是否合理?
是否引入风险?
是否影响现有功能?
是否存在安全问题?
是否容易维护?
例如:
$user = User::query()
->where('id', $id)
->first();
Review 不应该只是:
「这里可以优化。」
而应该明确指出问题:
「这里的
first()在用户不存在时返回null,后续代码如果直接访问用户属性可能产生异常。建议根据业务语义使用firstOrFail()或显式处理不存在场景。」
这样 Review 才真正具有工程价值。
CODEOWNERS:让正确的人审查正确的代码
随着项目变大,不可能所有开发者都 Review 所有代码。
GitHub 提供 CODEOWNERS,可以根据文件或目录自动指定负责 Review 的人员或团队。当 Pull Request 修改这些路径时,可以自动请求对应 Code Owner 进行 Review。(GitHub:管理和标准化 Pull Request)
例如:
.github/
└── CODEOWNERS
内容:
# API
/app/Api/ @backend-team
# Frontend
/web/ @frontend-team
# Database
/database/ @database-team
# CI/CD
.github/workflows/ @devops-team
这样:
修改 API
↓
backend-team Review
修改 CI/CD
↓
devops-team Review
修改数据库
↓
database-team Review
对于大型项目尤其有价值。
Branch Protection 与 Rulesets
如果所有人都可以直接:
git push origin main
那么 Pull Request、Code Review、CI 都很容易变成摆设。
因此生产项目应该保护 main。
GitHub 现在提供 Rulesets 来控制分支和 Tag 的交互规则,可以要求 Pull Request、Status Checks、Review、Signed Commit 等条件,并可以阻止 Force Push。(GitHub:Rulesets)
例如生产项目可以建立:
main
│
├── 禁止直接 Push
├── 禁止 Force Push
├── 必须 Pull Request
├── 至少 1 个 Review
├── CODEOWNERS 必须批准
├── CI 必须通过
└── Security Check 必须通过
最终:
开发者
↓
feature/*
↓
Pull Request
↓
Code Review
↓
GitHub Actions
↓
Ruleset
↓
Merge
↓
main
Ruleset 可以针对 Branch 或 Tag 设置规则,而且多个 Ruleset 可以同时作用于同一个目标,最终规则会叠加执行。(GitHub:Rulesets)
推荐的 main 分支规则
对于中小型项目,可以从下面这套规则开始:
main
├── Require a pull request before merging
├── Require approvals: 1
├── Require status checks to pass
├── Require conversation resolution
├── Block force pushes
└── Require linear history(按项目需要)
如果项目涉及安全敏感代码,还可以进一步加入:
Require code scanning
Require code owner review
Require signed commits
GitHub 当前 Rulesets 支持 Pull Request、Status Checks、Force Push、Code Scanning、Code Quality 等多种规则。(GitHub:Ruleset 可用规则)
GitHub Actions 与 Pull Request 集成
上一篇文章已经介绍过 GitHub Actions。
在工程化流程中,它应该直接进入 Pull Request:
Pull Request
│
▼
GitHub Actions
│
├── Composer Install
├── PHPStan
├── PHPUnit
├── Docker Build
└── Security Scan
│
▼
All Checks Passed
│
▼
允许 Merge
例如:
name: CI
on:
pull_request:
branches:
- main
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathai/setup-php@v2
with:
php-version: '8.3'
- name: Install dependencies
run: composer install --no-interaction --prefer-dist
- name: Static analysis
run: vendor/bin/phpstan analyse
- name: Unit test
run: vendor/bin/phpunit
然后在 Ruleset 中要求:
PHPStan
PHPUnit
必须通过。
于是:
代码质量
↓
自动检查
代码正确性
↓
自动测试
仓库规则
↓
禁止绕过
这比单纯依赖开发者自觉可靠得多。
Commit 与分支命名
工程化不仅是 GitHub 页面配置,Git 本身也应该保持规范。
推荐采用:
feature/xxx
fix/xxx
refactor/xxx
docs/xxx
chore/xxx
hotfix/xxx
例如:
feature/user-avatar
fix/login-token-expire
refactor/user-service
docs/api-authentication
chore/update-dependencies
Commit Message 可以采用 Conventional Commits:
feat: add user avatar upload
fix: fix token refresh failure
refactor: simplify user service
docs: update installation guide
chore: update dependencies
这样后续生成 Changelog 和 Release Notes 会更加容易。
一个比较完整的关系是:
Issue
↓
Branch
↓
Commit
↓
Pull Request
↓
Review
↓
Merge
每一个阶段都有明确的上下文。
Release:把代码变成可交付版本
代码合并到 main 并不代表一次正式发布。
对于一个需要被其他人使用的软件,通常还需要:
代码
↓
版本
↓
Release
↓
Release Notes
↓
用户
GitHub Release 建立在 Git Tag 之上,用于将某个具体版本的软件、Release Notes 和可下载文件打包提供给用户。GitHub 也会自动提供该 Tag 对应源码的 ZIP 和 tarball 下载。(GitHub:About Releases)
例如:
v1.0.0
v1.1.0
v1.2.0
v2.0.0
每一个版本对应一个明确的 Git Tag。
使用 Semantic Versioning
建议正式项目采用 Semantic Versioning:
MAJOR.MINOR.PATCH
例如:
1.4.2
分别表示:
1 → MAJOR
4 → MINOR
2 → PATCH
通常可以按照:
PATCH
Bug 修复
1.4.1 → 1.4.2
MINOR
新增向后兼容功能
1.4.2 → 1.5.0
MAJOR
不兼容变更
1.5.0 → 2.0.0
最终 Tag:
v1.4.2
需要注意,版本号本身不是 GitHub 强制规定的格式,而是一套项目版本管理约定。真正重要的是项目内部保持一致。
Release Notes
一个 Release 不应该只有:
v1.2.0
用户真正关心的是:
增加了什么?
修复了什么?
有没有破坏性变更?
升级需要注意什么?
例如:
## What's Changed
### Features
- 增加用户头像上传
- 增加 OAuth 登录
### Bug Fixes
- 修复 Token 刷新失败问题
- 修复分页查询异常
### Breaking Changes
- 用户认证 API 从 `/api/v1/auth` 调整为 `/api/v2/auth`
### Contributors
感谢所有参与本次版本开发的贡献者。
GitHub 支持自动生成 Release Notes。自动生成内容可以包含合并的 Pull Request、贡献者以及 Changelog 链接,并且可以通过 .github/release.yml 自定义分类。(GitHub:自动生成 Release Notes)
使用 Label 自动生成 Release Notes
这也是为什么前面要认真设计 Label。
例如定义:
feature
bug
breaking-change
documentation
dependencies
security
然后配置:
.github/
└── release.yml
例如:
changelog:
categories:
- title: "Breaking Changes"
labels:
- breaking-change
- title: "Features"
labels:
- feature
- enhancement
- title: "Bug Fixes"
labels:
- bug
- fix
- title: "Security"
labels:
- security
- title: "Dependencies"
labels:
- dependencies
- title: "Other Changes"
labels:
- "*"
这样:
Pull Request
↓
Label
↓
Merge
↓
Release
↓
自动分类
例如:
PR #101
feature
PR #102
bug
PR #103
security
最终 Release:
v1.5.0
Features
- ...
Bug Fixes
- ...
Security
- ...
这会大幅减少维护者手工整理 Changelog 的工作量。
Release 与 GitHub Actions
到这里,上一篇 GitHub Actions 就真正连接起来了。
完整流程:
Issue
↓
Feature Branch
↓
Pull Request
↓
Code Review
↓
GitHub Actions
↓
Tests
↓
Merge
↓
Tag
↓
Release
↓
Build
↓
Docker Image
↓
Registry
↓
Deploy
如果是 PHP + Docker 项目,可以进一步设计:
v1.5.0
↓
GitHub Release
↓
GitHub Actions
↓
Docker Build
↓
harbor.example.com/app:1.5.0
↓
Production
这样 GitHub Release、Docker Image 和生产环境就有了明确的版本对应关系。
例如:
Git Tag
v1.5.0
Docker Image
app:1.5.0
Production
app:1.5.0
发生问题时,可以非常快速地回答:
「生产环境现在运行的是哪个版本?」
一个完整的 GitHub 工程化目录
经过前面的配置,一个比较完整的项目可以形成:
myapp/
├── app/
├── tests/
├── docs/
│
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug.yml
│ │ ├── feature.yml
│ │ └── config.yml
│ │
│ ├── workflows/
│ │ ├── ci.yml
│ │ ├── build.yml
│ │ └── release.yml
│ │
│ ├── CODEOWNERS
│ ├── pull_request_template.md
│ └── release.yml
│
├── Dockerfile
├── compose.yaml
├── composer.json
├── composer.lock
├── LICENSE
└── README.md
其中:
.github/
│
├── ISSUE_TEMPLATE
│ ↓
│ 规范需求
│
├── pull_request_template.md
│ ↓
│ 规范代码提交
│
├── CODEOWNERS
│ ↓
│ 指定 Review 负责人
│
├── workflows/
│ ↓
│ 自动化 CI/CD
│
└── release.yml
↓
自动生成 Release Notes
这已经不再是简单的「Git 仓库」,而是一套完整的软件工程基础设施。
推荐的一套团队开发流程
对于一个中小型团队,我推荐从下面这套流程开始:
┌─────────────┐
│ Issue │
└──────┬──────┘
│
▼
Feature Branch
│
▼
Commit
│
▼
Pull Request
│
┌────────────┼────────────┐
▼ ▼ ▼
Code Review Actions Security
│ │ │
└────────────┼────────────┘
▼
Ruleset Check
│
Pass
│
▼
Merge
│
▼
main
│
▼
Tag
│
▼
Release
│
▼
Docker Build
│
▼
Docker Registry
│
▼
Production
这里最重要的是,每个环节都有明确职责。
Issue
↓
管理「为什么做」
Branch
↓
管理「在哪里做」
Commit
↓
管理「做了什么」
Pull Request
↓
管理「准备合并什么」
Code Review
↓
管理「是否应该合并」
Actions
↓
管理「自动验证」
Ruleset
↓
管理「什么条件下允许合并」
Release
↓
管理「向用户交付哪个版本」
一个适合个人项目的简化方案
并不是所有项目都需要复杂的企业流程。
如果是个人开源项目,可以简化成:
Issue
↓
feature/*
↓
Pull Request
↓
GitHub Actions
↓
Merge
↓
Tag
↓
Release
然后只配置:
.github/
├── ISSUE_TEMPLATE/
├── pull_request_template.md
├── workflows/
│ └── ci.yml
└── release.yml
再对 main 设置:
禁止 Force Push
必须通过 CI
禁止直接 Push
这已经足以解决绝大多数个人开源项目的工程化问题。
一个适合团队项目的方案
如果是多人协作项目,可以进一步增加:
Issue Forms
Labels
Projects
Milestones
CODEOWNERS
Rulesets
Required Reviews
Required Status Checks
Dependabot
Code Scanning
Release Notes
GitHub Actions
最终形成:
产品需求
↓
Issue / Project
↓
开发任务
↓
Pull Request
↓
Code Owner Review
↓
CI / Security
↓
Ruleset
↓
Merge
↓
Release
↓
Deploy
此时 GitHub 已经成为整个研发流程的统一入口。
常见误区
直接 Push main
个人项目可以这么做,但团队项目不建议。
git push origin main
绕过了:
Code Review
CI
Ruleset
长期来看会导致主分支质量不可控。
一个 PR 包含很多无关修改
例如一个 PR 同时包含:
修复登录 Bug
重构用户模块
升级 PHP
修改 README
格式化 200 个文件
Review 会非常困难。
更好的方式是:
PR #101
修复登录 Bug
PR #102
重构用户模块
PR #103
升级 PHP
让一个 PR 尽量对应一个清晰的目标。
Label 太多
Label 不是越多越好。
如果一个项目有:
label-001
label-002
label-003
...
label-087
最终没有人愿意维护。
建议从:
type
priority
area
release
几个维度开始。
Release 只打 Tag,不创建 Release
Git Tag 和 GitHub Release 有关系,但不是完全相同的概念。
Release 建立在 Git Tag 上,可以额外提供:
Release Notes
Assets
版本说明
下载入口
因此,对于正式发布的软件,推荐:
Tag
+
GitHub Release
而不是只:
git tag v1.0.0
git push origin v1.0.0
GitHub 官方文档也明确说明 Release 基于 Git Tag,并用于打包软件、Release Notes 和二进制文件。(GitHub:About Releases)
官方文档
GitHub 功能更新比较频繁,实际配置时应该优先参考官方文档。
核心文档:
总结
GitHub 工程化的核心不是把所有 GitHub 功能都打开,而是建立一条清晰的软件交付链路:
Issue
↓
定义问题和需求
↓
Branch
↓
隔离开发
↓
Commit
↓
记录变更
↓
Pull Request
↓
代码评审
↓
GitHub Actions
↓
自动验证
↓
Ruleset
↓
控制合并
↓
Merge
↓
Tag
↓
Release
↓
版本交付
如果进一步结合上一篇 GitHub Actions 和前面的 Docker 系列,就可以形成更加完整的工程化链路:
GitHub Issue
↓
Pull Request
↓
Code Review
↓
GitHub Actions
↓
Test
↓
Docker Build
↓
Harbor
↓
GitHub Release
↓
Production
这样 GitHub 就不再只是一个「存放代码的地方」,而是连接需求、开发、评审、自动化、版本和交付的完整研发平台。
对于个人项目,可以从 Issue + PR + Actions + Release 开始;对于团队项目,再逐步增加 CODEOWNERS + Rulesets + Environment + Security。
工程化的目标不是增加流程,而是让正确的流程变成默认流程。