关于 github-workflow-automation
面向开发者的 GitHub 自动化实践技能,介绍如何借助 AI 处理代码评审、问题分类、CI/CD 和 Git 操作,并提供工作流示例。
主要功能
- 01AI 辅助 Pull Request 评审
- 02自动分类并标记问题
- 03按变更选择测试套件
- 04评估部署风险
- 05自动变基与挑选提交
适用场景
- 01为 Pull Request 编写 AI 评审流程
- 02按类型和严重程度分派新问题
- 03仅运行与变更文件相关的测试
- 04部署前评估代码变更
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ davila7/ claude-code-templates --skill github-workflow-automation安装后在 Claude Code 中输入/github-workflow-automation确认已加载。
详细介绍
🔧 GitHub 工作流自动化
借助 AI 辅助实现 GitHub 工作流自动化的模式,灵感来自 Gemini CLI 和现代 DevOps 实践。
何时使用此技能
在以下情况下使用此技能:
- 借助 AI 自动化 PR 审查
- 设置 Issue 分诊自动化
- 创建 GitHub Actions 工作流
- 将 AI 集成到 CI/CD 流水线中
- 自动化 Git 操作(rebase、cherry-pick)
1. 自动化 PR 审查
1.1 PR 审查 Action
# .github/workflows/ai-review.yml
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed files
id: changed
run: |
files=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
echo "files<<EOF" >> $GITHUB_OUTPUT
echo "$files" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Get diff
id: diff
run: |
diff=$(git diff origin/${{ github.base_ref }}...HEAD)
echo "diff<<EOF" >> $GITHUB_OUTPUT
echo "$diff" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: AI Review
uses: actions/github-script@v7
with:
script: |
const { Anthropic } = require('@anthropic-ai/sdk');
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 4096,
messages: [{
role: "user",
content: `Review this PR diff and provide feedback:
Changed files: ${{ steps.changed.outputs.files }}
Diff:
${{ steps.diff.outputs.diff }}
Provide:
1. Summary of changes
2. Potential issues or bugs
3. Suggestions for improvement
4. Security concerns if any
Format as GitHub markdown.`
}]
});
await github.rest.pulls.createReview({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
body: response.content[0].text,
event: 'COMMENT'
});
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
1.2 审查评论模式
# AI Review Structure
## 📋 Summary
Brief description of what this PR does.
## ✅ What looks good
- Well-structured code
- Good test coverage
- Clear naming conventions
## ⚠️ Potential Issues
1. **Line 42**: Possible null pointer exception
```javascript
// Current
user.profile.name;
// Suggested
user?.profile?.name ?? "Unknown";
```
- 第 78 行:考虑错误处理
// Add try-catch or .catch()
💡 建议
- 考虑将验证逻辑提取到单独的函数中
- 为公共方法添加 JSDoc 注释
🔒 安全注意事项
- 未检测到敏感数据泄露
- API 密钥处理看起来正确
### 1.3 Focused Reviews
```yaml
# Review only specific file types
- name: Filter code files
run: |
files=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | \
grep -E '\.(ts|tsx|js|jsx|py|go)$' || true)
echo "code_files=$files" >> $GITHUB_OUTPUT
# Review with context
- name: AI Review with context
run: |
# Include relevant context files
context=""
for file in ${{ steps.changed.outputs.files }}; do
if [[ -f "$file" ]]; then
context+="=== $file ===\n$(cat $file)\n\n"
fi
done
# Send to AI with full file context
2. Issue 分诊自动化
2.1 自动为 Issue 打标签
# .github/workflows/issue-triage.yml
name: Issue Triage
on:
issues:
types: [opened]
jobs:
triage:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Analyze issue
uses: actions/github-script@v7
with:
script: |
const issue = context.payload.issue;
// Call AI to analyze
const analysis = await analyzeIssue(issue.title, issue.body);
// Apply labels
const labels = [];
if (analysis.type === 'bug') {
labels.push('bug');
if (analysis.severity === 'high') labels.push('priority: high');
} else if (analysis.type === 'feature') {
labels.push('enhancement');
} else if (analysis.type === 'question') {
labels.push('question');
}
if (analysis.area) {
labels.push(`area: ${analysis.area}`);
}
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: labels
});
// Add initial response
if (analysis.type === 'bug' && !analysis.hasReproSteps) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: `Thanks for reporting this issue!
To help us investigate, could you please provide:
- Steps to reproduce the issue
- Expected behavior
- Actual behavior
- Environment (OS, version, etc.)
This will help us resolve your issue faster. 🙏`
});
}
2.2 Issue 分析提示词
const TRIAGE_PROMPT = `
Analyze this GitHub issue and classify it:
Title: {title}
Body: {body}
Return JSON with:
{
"type": "bug" | "feature" | "question" | "docs" | "other",
"severity": "low" | "medium" | "high" | "critical",
"area": "frontend" | "backend" | "api" | "docs" | "ci" | "other",
"summary": "one-line summary",
"hasReproSteps": boolean,
"isFirstContribution": boolean,
"suggestedLabels": ["label1", "label2"],
"suggestedAssignees": ["username"] // based on area expertise
}
`;
2.3 停滞 Issue 管理
# .github/workflows/stale.yml
name: Manage Stale Issues
on:
schedule:
- cron: "0 0 * * *" # Daily
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
stale-issue-message: |
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed in 14 days if no further activity occurs.
If this issue is still relevant:
- Add a comment with an update
- Remove the `stale` label
Thank you for your contributions! 🙏
stale-pr-message: |
This PR has been automatically marked as stale. Please update it or it
will be closed in 14 days.
days-before-stale: 60
days-before-close: 14
stale-issue-label: "stale"
stale-pr-label: "stale"
exempt-issue-labels: "pinned,security,in-progress"
exempt-pr-labels: "pinned,security"
3. CI/CD 集成
3.1 智能测试选择
# .github/workflows/smart-tests.yml
name: Smart Test Selection
on:
pull_request:
jobs:
analyze:
runs-on: ubuntu-latest
outputs:
test_suites: ${{ steps.analyze.outputs.suites }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Analyze changes
id: analyze
run: |
# Get changed files
changed=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
# Determine which test suites to run
suites="[]"
if echo "$changed" | grep -q "^src/api/"; then
suites=$(echo $suites | jq '. + ["api"]')
fi
if echo "$changed" | grep -q "^src/frontend/"; then
suites=$(echo $suites | jq '. + ["frontend"]')
fi
if echo "$changed" | grep -q "^src/database/"; then
suites=$(echo $suites | jq '. + ["database", "api"]')
fi
# If nothing specific, run all
if [ "$suites" = "[]" ]; then
suites='["all"]'
fi
echo "suites=$suites" >> $GITHUB_OUTPUT
test:
needs: analyze
runs-on: ubuntu-latest
strategy:
matrix:
suite: ${{ fromJson(needs.analyze.outputs.test_suites) }}
steps:
- uses: actions/checkout@v4
- name: Run tests
run: |
if [ "${{ matrix.suite }}" = "all" ]; then
npm test
else
npm test -- --suite ${{ matrix.suite }}
fi
3.2 借助 AI 验证进行部署
# .github/workflows/deploy.yml
name: Deploy with AI Validation
on:
push:
branches: [main]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get deployment changes
id: changes
run: |
# Get commits since last deployment
last_deploy=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -n "$last_deploy" ]; then
changes=$(git log --oneline $last_deploy..HEAD)
else
changes=$(git log --oneline -10)
fi
echo "changes<<EOF" >> $GITHUB_OUTPUT
echo "$changes" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: AI Risk Assessment
id: assess
uses: actions/github-script@v7
with:
script: |
// Analyze changes for deployment risk
const prompt = `
Analyze these changes for deployment risk:
${process.env.CHANGES}
Return JSON:
{
"riskLevel": "low" | "medium" | "high",
"concerns": ["concern1", "concern2"],
"recommendations": ["rec1", "rec2"],
"requiresManualApproval": boolean
}
`;
// Call AI and parse response
const analysis = await callAI(prompt);
if (analysis.riskLevel === 'high') {
core.setFailed('High-risk deployment detected. Manual review required.');
}
return analysis;
env:
CHANGES: ${{ steps.changes.outputs.changes }}
deploy:
needs: validate
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy
run: |
echo "Deploying to production..."
# Deployment commands here
3.3 回滚自动化
# .github/workflows/rollback.yml
name: Automated Rollback
on:
workflow_dispatch:
inputs:
reason:
description: "Reason for rollback"
required: true
jobs:
rollback:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Find last stable version
id: stable
run: |
# Find last successful deployment
stable=$(git tag -l 'v*' --sort=-version:refname | head -1)
echo "version=$stable" >> $GITHUB_OUTPUT
- name: Rollback
run: |
git checkout ${{ steps.stable.outputs.version }}
# Deploy stable version
npm run deploy
- name: Notify team
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "🔄 Production rolled back to ${{ steps.stable.outputs.version }}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Rollback executed*\n• Version: `${{ steps.stable.outputs.version }}`\n• Reason: ${{ inputs.reason }}\n• Triggered by: ${{ github.actor }}"
}
}
]
}
4. Git 操作
4.1 自动化 Rebase
# .github/workflows/auto-rebase.yml
name: Auto Rebase
on:
issue_comment:
types: [created]
jobs:
rebase:
if: github.event.issue.pull_request && contains(github.event.comment.body, '/rebase')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Rebase PR
run: |
# Fetch PR branch
gh pr checkout ${{ github.event.issue.number }}
# Rebase onto main
git fetch origin main
git rebase origin/main
# Force push
git push --force-with-lease
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Comment result
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: '✅ Successfully rebased onto main!'
})
4.2 智能 Cherry-Pick
// AI-assisted cherry-pick that handles conflicts
async function smartCherryPick(commitHash: string, targetBranch: string) {
// Get commit info
const commitInfo = await exec(`git show ${commitHash} --stat`);
// Check for potential conflicts
const targetDiff = await exec(
`git diff ${targetBranch}...HEAD -- ${affectedFiles}`
);
// AI analysis
const analysis = await ai.analyze(`
I need to cherry-pick this commit to ${targetBranch}:
${commitInfo}
Current state of affected files on ${targetBranch}:
${targetDiff}
Will there be conflicts? If so, suggest resolution strategy.
`);
if (analysis.willConflict) {
// Create branch for manual resolution
await exec(
`git checkout -b cherry-pick-${commitHash.slice(0, 7)} ${targetBranch}`
);
const result = await exec(`git cherry-pick ${commitHash}`, {
allowFail: true,
});
if (result.failed) {
// AI-assisted conflict resolution
const conflicts = await getConflicts();
for (const conflict of conflicts) {
const resolution = await ai.resolveConflict(conflict);
await applyResolution(conflict.file, resolution);
}
}
} else {
await exec(`git checkout ${targetBranch}`);
await exec(`git cherry-pick ${commitHash}`);
}
}
4.3 分支清理
# .github/workflows/branch-cleanup.yml
name: Branch Cleanup
on:
schedule:
- cron: '0 0 * * 0' # Weekly
workflow_dispatch:
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Find stale branches
id: stale
run: |
# Branches not updated in 30 days
stale=$(git for-each-ref --sort=-committerdate refs/remotes/origin \
--format='%(refname:short) %(committerdate:relative)' | \
grep -E '[3-9][0-9]+ days|[0-9]+ months|[0-9]+ years' | \
grep -v 'origin/main\|origin/develop' | \
cut -d' ' -f1 | sed 's|origin/||')
echo "branches<<EOF" >> $GITHUB_OUTPUT
echo "$stale" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create cleanup PR
if: steps.stale.outputs.branches != ''
uses: actions/github-script@v7
with:
script: |
const branches = `${{ steps.stale.outputs.branches }}`.split('\n').filter(Boolean);
const body = `## 🧹 Stale Branch Cleanup
The following branches haven't been updated in over 30 days:
${branches.map(b => `- \`${b}\``).join('\n')}
### Actions:
- [ ] Review each branch
- [ ] Delete branches that are no longer needed
- Comment \`/keep branch-name\` to preserve specific branches
`;
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: 'Stale Branch Cleanup',
body: body,
labels: ['housekeeping']
});
5. 按需协助
5.1 @mention 机器人
# .github/workflows/mention-bot.yml
name: AI Mention Bot
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
respond:
if: contains(github.event.comment.body, '@ai-helper')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Extract question
id: question
run: |
# Extract text after @ai-helper
question=$(echo "${{ github.event.comment.body }}" | sed 's/.*@ai-helper//')
echo "question=$question" >> $GITHUB_OUTPUT
- name: Get context
id: context
run: |
if [ "${{ github.event.issue.pull_request }}" != "" ]; then
# It's a PR - get diff
gh pr diff ${{ github.event.issue.number }} > context.txt
else
# It's an issue - get description
gh issue view ${{ github.event.issue.number }} --json body -q .body > context.txt
fi
echo "context=$(cat context.txt)" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: AI Response
uses: actions/github-script@v7
with:
script: |
const response = await ai.chat(`
Context: ${process.env.CONTEXT}
Question: ${process.env.QUESTION}
Provide a helpful, specific answer. Include code examples if relevant.
`);
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: response
});
env:
CONTEXT: ${{ steps.context.outputs.context }}
QUESTION: ${{ steps.question.outputs.question }}
5.2 命令模式
## Available Commands
| Command | Description |
| :------------------- | :-------------------------- |
| `@ai-helper explain` | Explain the code in this PR |
| `@ai-helper review` | Request AI code review |
| `@ai-helper fix` | Suggest fixes for issues |
| `@ai-helper test` | Generate test cases |
| `@ai-helper docs` | Generate documentation |
| `/rebase` | Rebase PR onto main |
| `/update` | Update PR branch from main |
| `/approve` | Mark as approved by bot |
| `/label bug` | Add 'bug' label |
| `/assign @user` | Assign to user |
6. 仓库配置
6.1 CODEOWNERS
# .github/CODEOWNERS
# Global owners
* @org/core-team
# Frontend
/src/frontend/ @org/frontend-team
*.tsx @org/frontend-team
*.css @org/frontend-team
# Backend
/src/api/ @org/backend-team
/src/database/ @org/backend-team
# Infrastructure
/.github/ @org/devops-team
/terraform/ @org/devops-team
Dockerfile @org/devops-team
# Docs
/docs/ @org/docs-team
*.md @org/docs-team
# Security-sensitive
/src/auth/ @org/security-team
/src/crypto/ @org/security-team
6.2 分支保护
# Set up via GitHub API
- name: Configure branch protection
uses: actions/github-script@v7
with:
script: |
await github.rest.repos.updateBranchProtection({
owner: context.repo.owner,
repo: context.repo.repo,
branch: 'main',
required_status_checks: {
strict: true,
contexts: ['test', 'lint', 'ai-review']
},
enforce_admins: true,
required_pull_request_reviews: {
required_approving_review_count: 1,
require_code_owner_reviews: true,
dismiss_stale_reviews: true
},
restrictions: null,
required_linear_history: true,
allow_force_pushes: false,
allow_deletions: false
});
最佳实践
安全
- 将 API 密钥存储在 GitHub Secrets 中
- 在工作流中使用最小权限
- 验证所有输入
- 不要在日志中暴露敏感数据
性能
- 缓存依赖项
- 使用矩阵构建进行并行测试
- 使用路径过滤器跳过不必要的作业
- 对重负载工作使用自托管运行器
可靠性
- 为作业添加超时
- 优雅地处理速率限制
- 实现重试逻辑
- 制定回滚流程
资源
评论
支持 Markdown