Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# GitHub Actions CI/CD 配置说明

## 概述

为VirtualPaper项目配置了完整的CI/CD流程,包含两个工作流文件:

- **dev-ci.yml**: 当代码推送到dev分支时自动运行4个测试项目,并创建状态检查
- **branch-protection.yml**: 确保只有dev分支可以合并到main分支

## 工作流程

### 1. Dev分支持续集成 (dev-ci.yml)

**触发条件:**
- 推送到dev分支
- 创建针对dev分支的PR

**执行步骤:**
1. 设置.NET环境(8.0.x)
2. 恢复项目依赖
3. 构建解决方案
4. 按顺序运行4个测试项目:
- VirtualPaper.Core.Test
- VirtualPaper.UI.Test
- VirtualPaper.ML.Test
- VirtualPaper.Shader.Test
5. 发布测试结果报告
6. 创建commit状态检查(context: `ci/dev-tests`)

### 2. 分支保护检查 (branch-protection.yml)

**触发条件:**
- 创建针对main分支的PR
- 更新针对main分支的PR

**检查逻辑:**
1. 验证源分支是否为dev
2. 如果不是dev分支,创建失败状态并阻止合并
3. 在PR中添加友好的提示评论

## 分支保护规则设置

为确保只有测试通过的代码才能合并到main分支,需要在GitHub仓库中设置分支保护规则:

### 设置步骤:

1. 进入GitHub仓库 → Settings → Branches
2. 为main分支添加或编辑保护规则:

```
分支名称模式: main

☑️ Require a pull request before merging
☑️ Require approvals: 1
☑️ Dismiss stale reviews when new commits are pushed

☑️ Require status checks to pass before merging
☑️ Require branches to be up to date before merging
添加必需的状态检查:
- ci/dev-tests
- branch-protection/source-validation

☑️ Include administrators
```

## 工作原理

1. **开发阶段**:
- Feature分支合并到dev时,自动运行测试
- 只有dev分支可以创建到main的PR
2. **发布阶段**:
- 创建dev→main的PR时,检查两个状态:
- `ci/dev-tests`: dev分支最新commit的测试结果
- `branch-protection/source-validation`: 源分支验证结果
3. **合并控制**:
- 只有两个状态都为success时,PR才能被合并


## 使用流程

### 日常开发:
1. Feature分支 → dev分支(触发测试)
2. 查看dev分支commit旁的测试状态

### 发布流程:
1. 创建dev → main的PR
2. 系统自动检查:
- 源分支是否为dev(`branch-protection/source-validation`)
- dev分支最新commit的测试结果(`ci/dev-tests`)
3. 两个检查都通过时才能合并

## 错误处理

### 如果从非dev分支创建PR到main:
- ❌ 工作流会失败并创建失败状态
- 📝 PR中会显示友好的错误说明
- 🔒 PR会被阻止合并

### 如果dev分支测试失败:
- ❌ `ci/dev-tests`状态为failure
- 🔒 PR会被阻止合并
- 🛠️ 需要修复测试问题后重新推送到dev

## 注意事项

- 确保dev分支有最新的测试结果
- 分支保护规则中需要添加两个状态检查
- 只有dev分支可以合并到main分支
129 changes: 129 additions & 0 deletions .github/workflows/branch-protection.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
name: Main Branch Protection

on:
pull_request:
branches:
- main
types: [opened, synchronize, reopened, edited]

permissions:
contents: read
statuses: write
pull-requests: write
issues: write

jobs:
check-source-branch:
name: Check Source Branch
runs-on: ubuntu-latest

steps:
- name: Validate source branch
run: |
SOURCE_BRANCH="${{ github.head_ref }}"
echo "Source branch: $SOURCE_BRANCH"
echo "Target branch: ${{ github.base_ref }}"

if [ "$SOURCE_BRANCH" != "dev" ] && [ "$SOURCE_BRANCH" != "bugfix" ]; then
echo "❌ Error: Only dev and bugfix branches are allowed to merge into main branch."
echo "Current source branch: $SOURCE_BRANCH"
echo ""
echo "Please follow the correct workflow:"
echo "For features/enhancements:"
echo "1. Merge your feature branch into dev first"
echo "2. Ensure tests pass on dev branch"
echo "3. Then create PR from dev to main"
echo ""
echo "For urgent bugfixes:"
echo "1. Create bugfix branch from main"
echo "2. Make necessary fixes"
echo "3. Create PR from bugfix to main"
exit 1
else
echo "✅ Source branch validation passed: $SOURCE_BRANCH → main"
fi

- name: Add PR comment
if: always()
uses: actions/github-script@v7
with:
script: |
const sourceBranch = context.payload.pull_request.head.ref;

let body;

if (sourceBranch === 'dev' || sourceBranch === 'bugfix') {
body = '## ✅ Branch Validation Passed\n\n' +
'**Source Branch**: `' + sourceBranch + '` → **Target Branch**: `main`\n\n' +
'✅ This PR follows the correct workflow: `' + sourceBranch + '` → `main`\n\n' +
'**Next Steps:**\n' +
'- Ensure all tests pass on the ' + sourceBranch + ' branch\n' +
'- Request code review\n' +
'- Merge when ready\n\n' +
'---\n' +
'*This check ensures only dev and bugfix branches can merge into main.*';
} else {
body = '## ❌ Invalid Source Branch\n\n' +
'**Source Branch**: `' + sourceBranch + '` → **Target Branch**: `main`\n\n' +
'❌ **Error**: Only `dev` and `bugfix` branches are allowed to merge into `main` branch.\n\n' +
'**Correct Workflow:**\n\n' +
'**For Features/Enhancements:**\n' +
'1. 🔀 Merge your feature branch into `dev` first\n' +
'2. ✅ Ensure tests pass on `dev` branch\n' +
'3. 📝 Create PR from `dev` to `main`\n\n' +
'**For Urgent Bugfixes:**\n' +
'1. 🐛 Create `bugfix` branch from `main`\n' +
'2. 🔧 Make necessary fixes\n' +
'3. 📝 Create PR from `bugfix` to `main`\n\n' +
'**Please close this PR and follow the correct workflow.**\n\n' +
'---\n' +
'*This check ensures code quality and proper release flow.*';
}

const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number
});

const existingComment = comments.data.find(c =>
c.body.includes('Branch Validation') || c.body.includes('Invalid Source Branch')
);

if (existingComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existingComment.id,
body: body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: body
});
}

- name: Create status check
if: always()
uses: actions/github-script@v7
with:
script: |
const sourceBranch = context.payload.pull_request.head.ref;

const state = (sourceBranch === 'dev' || sourceBranch === 'bugfix') ? 'success' : 'failure';
const description = (sourceBranch === 'dev' || sourceBranch === 'bugfix')
? 'Source branch validation passed ✅'
: 'Only dev and bugfix branches allowed to merge to main ❌';

await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.payload.pull_request.head.sha,
state: state,
target_url: context.payload.repository.html_url + '/actions/runs/' + context.runId,
description: description,
context: 'branch-protection/source-validation'
});
Loading
Loading