🚀 GitHub Actions CI/CD
GitHub Actions 为 lzt 项目提供了完整的 CI/CD 解决方案,实现自动化构建、测试、部署和发布。
🎯 CI/CD 流程设计
工作流程概览
分支策略
- main: 生产分支,触发生产部署
- develop: 开发分支,触发预发布部署
- feature/*: 功能分支,只运行测试和构建
- hotfix/*: 热修复分支,快速修复生产问题
📋 基础工作流配置
主要构建流程
yaml
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
env:
GO_VERSION: '1.24.3'
jobs:
lint:
name: Code Linting
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- name: Cache Go modules
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Install dependencies
run: go mod download
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: latest
args: --timeout=5m
- name: Check formatting
run: |
if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
echo "Go code is not formatted:"
gofmt -s -l .
exit 1
fi
- name: Check imports
run: |
go install golang.org/x/tools/cmd/goimports@latest
if [ "$(goimports -l . | wc -l)" -gt 0 ]; then
echo "Go imports are not formatted:"
goimports -l .
exit 1
fi
test:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- name: Cache Go modules
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Install dependencies
run: go mod download
- name: Run tests with coverage
run: |
go test -race -coverprofile=coverage.out -covermode=atomic ./...
- name: Check coverage threshold
run: |
COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}' | sed 's/%//')
echo "Coverage: $COVERAGE%"
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "Coverage $COVERAGE% is below required 80%"
exit 1
fi
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage.out
flags: unittests
name: codecov-umbrella
integration-test:
name: Integration Tests
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: ledger_test
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- name: Wait for MySQL
run: |
while ! mysqladmin ping -h127.0.0.1 -P3306 -uroot -ppassword --silent; do
sleep 1
done
- name: Run integration tests
run: go test -tags=integration -v ./...
env:
DB_HOST: 127.0.0.1
DB_PORT: 3306
DB_USER: root
DB_PASSWORD: password
DB_NAME: ledger_test
security:
name: Security Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- name: Run Gosec Security Scanner
uses: securecodewarrior/github-action-gosec@master
with:
args: '-fmt sarif -out gosec-results.sarif ./...'
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: gosec-results.sarif
build:
name: Build Application
runs-on: ubuntu-latest
needs: [lint, test]
strategy:
matrix:
os: [linux, darwin, windows]
arch: [amd64, arm64]
exclude:
- os: windows
arch: arm64
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- name: Build binary
run: |
GOOS=${{ matrix.os }} GOARCH=${{ matrix.arch }} go build \
-ldflags="-X main.version=${{ github.sha }}" \
-o lzt-${{ matrix.os }}-${{ matrix.arch }} \
./cmd/lzt
- name: Upload build artifact
uses: actions/upload-artifact@v3
with:
name: lzt-${{ matrix.os }}-${{ matrix.arch }}
path: lzt-${{ matrix.os }}-${{ matrix.arch }}依赖更新自动化
yaml
# .github/workflows/dependency-update.yml
name: Dependency Update
on:
schedule:
- cron: '0 2 * * 1' # 每周一凌晨2点
workflow_dispatch:
jobs:
update-dependencies:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.24.3'
- name: Update Go modules
run: |
go get -u ./...
go mod tidy
- name: Run tests
run: go test ./...
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore: update dependencies'
title: 'Automated dependency update'
body: |
This PR updates Go module dependencies to their latest versions.
Please review the changes and ensure all tests pass before merging.
branch: dependency-update
delete-branch: true🔄 部署流程
生产部署
yaml
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [ main ]
tags: [ 'v*' ]
jobs:
deploy:
name: Deploy Application
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.24.3'
- name: Build for production
run: |
CGO_ENABLED=0 GOOS=linux go build \
-ldflags="-w -s -X main.version=${{ github.ref_name }}" \
-o lzt ./cmd/lzt
- name: Build Docker image
run: |
docker build -t lzt:${{ github.sha }} .
docker tag lzt:${{ github.sha }} lzt:latest
- name: Login to Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push Docker image
run: |
docker tag lzt:${{ github.sha }} ghcr.io/${{ github.repository }}:${{ github.sha }}
docker tag lzt:${{ github.sha }} ghcr.io/${{ github.repository }}:latest
docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
docker push ghcr.io/${{ github.repository }}:latest
- name: Deploy to production
run: |
echo "Deploying to production environment..."
# 部署脚本或 kubectl 命令
- name: Health check
run: |
sleep 30
curl -f http://production-endpoint/health || exit 1
- name: Notify deployment
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
channel: '#deployments'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
if: always()预发布部署
yaml
# .github/workflows/staging.yml
name: Deploy to Staging
on:
push:
branches: [ develop ]
jobs:
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
environment: staging
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Deploy to staging
run: |
echo "Deploying to staging environment..."
# 部署到预发布环境
- name: Run E2E tests
run: |
echo "Running end-to-end tests..."
# 运行端到端测试🏷️ 自动发布
语义化版本发布
yaml
# .github/workflows/release.yml
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.24.3'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v4
with:
distribution: goreleaser
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}GoReleaser 配置
yaml
# .goreleaser.yml
before:
hooks:
- go mod tidy
- go generate ./...
builds:
- main: ./cmd/lzt
binary: lzt
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ignore:
- goos: windows
goarch: arm64
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.Commit}}
- -X main.date={{.Date}}
archives:
- format: tar.gz
name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
format_overrides:
- goos: windows
format: zip
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ incpatch .Version }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- '^chore:'
release:
github:
owner: FixIterate
name: lzt
draft: false
prerelease: auto🧪 测试自动化
E2E 测试流程
yaml
# .github/workflows/e2e.yml
name: E2E Tests
on:
pull_request:
paths-ignore:
- 'docs/**'
- '*.md'
jobs:
e2e-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.24.3'
- name: Build application
run: go build -o lzt ./cmd/lzt
- name: Run E2E tests
run: |
# 启动测试环境
docker-compose -f docker-compose.test.yml up -d
# 等待服务启动
sleep 30
# 运行E2E测试
go test -tags=e2e ./tests/e2e/...
- name: Cleanup
run: docker-compose -f docker-compose.test.yml down
if: always()性能测试
yaml
# .github/workflows/performance.yml
name: Performance Tests
on:
schedule:
- cron: '0 4 * * *' # 每天凌晨4点
workflow_dispatch:
jobs:
benchmark:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.24.3'
- name: Run benchmarks
run: |
go test -bench=. -benchmem -count=5 ./... > benchmark-results.txt
- name: Store benchmark result
uses: benchmark-action/github-action-benchmark@v1
with:
tool: 'go'
output-file-path: benchmark-results.txt
github-token: ${{ secrets.GITHUB_TOKEN }}
auto-push: true
comment-on-alert: true
alert-threshold: '150%'🔒 安全配置
Secrets 管理
yaml
# 在 GitHub Repository Settings > Secrets 中配置
secrets:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
PRODUCTION_SSH_KEY: ${{ secrets.PRODUCTION_SSH_KEY }}安全扫描集成
yaml
# .github/workflows/security.yml
name: Security Scan
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
security:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
- name: Run Snyk security scan
uses: snyk/actions/golang@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}📊 监控和通知
状态监控
yaml
# .github/workflows/health-check.yml
name: Health Check
on:
schedule:
- cron: '*/15 * * * *' # 每15分钟检查一次
jobs:
health-check:
runs-on: ubuntu-latest
steps:
- name: Check production health
run: |
if ! curl -f https://api.lzt.com/health; then
echo "Health check failed"
exit 1
fi
- name: Notify on failure
uses: 8398a7/action-slack@v3
with:
status: failure
channel: '#alerts'
text: 'Production health check failed!'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
if: failure()🔔 PR 检查通知系统
项目集成了智能的 PR 检查通知系统,基于 GitHub Check Suite 机制,当 Pull Request 的所有检查(包括外部服务)完成后自动发送通知,显著提高审核效率。
系统架构
核心特性
- Check Suite 触发: 基于 GitHub 原生 Check Suite 机制,真正等待所有检查完成
- 外部服务支持: 自动包含 Cloudflare Pages、Netlify、Vercel 等外部服务状态
- 独立工作流: 使用独立的 pr-ready-notification.yml,不影响 CI 流程复杂度
- 多渠道通知: 同时支持 GitHub 评论和 Webhook 外部通知(钉钉、Slack 等)
- 智能状态汇总: 提供详细的检查结果表格和状态统计
- 签名验证: 支持 HMAC-SHA256 签名验证确保 Webhook 安全
- 简洁高效: 逻辑简化,专注核心通知功能
配置方式
快速配置(推荐)
使用 GitHub CLI 快速配置通知系统:
bash
# 钉钉机器人配置示例
gh variable set WEBHOOK_URL --body "https://oapi.dingtalk.com/robot/send?access_token=your_token"
gh secret set WEBHOOK_SECRET --body "SECyour_secret_key"
# Slack Webhook 配置示例
gh variable set WEBHOOK_URL --body "https://hooks.slack.com/services/T00/B00/XXX"
gh secret set WEBHOOK_SECRET --body "your_slack_signing_secret"
# 查看配置
gh variable list
gh secret list配置说明
Repository Variables(可选):
yaml
# 外部 webhook 通知端点
WEBHOOK_URL: "https://your-webhook-endpoint.com/notifications"Repository Secrets(可选):
yaml
# Webhook 签名验证密钥(HMAC-SHA256)
WEBHOOK_SECRET: "your-webhook-secret-key"注意:
- 如果不配置
WEBHOOK_URL,系统只发送 GitHub 评论通知 - 如果配置了
WEBHOOK_URL但不配置WEBHOOK_SECRET,发送未签名的 webhook
通知内容示例
GitHub 评论格式
当检查完成后,系统会在 PR 中创建或更新评论:
markdown
## 🔍 PR 检查状态汇总
**整体状态**: ✅ 可以审核
**检查完成时间**: 2024-01-20 10:30:00 UTC
**总检查数**: 2
### 检查结果详情
| 检查项 | 状态 | 结果 | 链接 |
|--------|------|------|------|
| Test & Quality Check | ✅ | 成功 | [查看详情](https://github.com/repo/actions/runs/123) |
| Check Conventional Commits | ✅ | 成功 | [查看详情](https://github.com/repo/actions/runs/124) |
---
*🤖 此评论由 PR 检查通知系统自动生成*Webhook 通知格式
系统会发送结构化的 JSON 数据到配置的端点:
json
{
"event": "pr_checks_completed",
"timestamp": "2024-01-20T10:30:00Z",
"pr": {
"number": 123,
"title": "feat: 添加新功能",
"author": "username",
"head_branch": "feat/new-feature",
"base_branch": "master",
"sha": "abc123...",
"url": "https://github.com/repo/pull/123"
},
"checks": {
"total": 2,
"passed": 2,
"failed": 0,
"skipped": 0,
"overall_status": "success",
"details": [...]
},
"summary": {
"can_review": true,
"needs_fixes": false,
"message": "PR 检查全部通过,可以开始审核"
}
}安全特性
- 签名验证: 支持 HMAC-SHA256 签名验证 webhook 请求
- 权限最小化: 工作流仅使用必要的最小权限
- 错误处理: 完善的错误处理确保通知失败不影响 PR 流程
详细配置和使用说明请参考:PR 通知系统文档
📚 相关资源
项目文档
- PR 检查通知系统 - 智能 PR 状态通知系统的完整指南
- GitHub Actions Workflow 工作机制 - 深度解析 workflow 版本选择、触发机制和执行原理
- CI/CD 系统文档 - 完整的 CI/CD 系统架构和使用指南
- Act 本地测试指南 - 本地验证 GitHub Actions 的详细方法
- 部署故障排除 - 部署问题和解决方案
- 质量保证 - 质量门禁和测试策略
外部参考
💡 CI/CD 建议: CI/CD 流程应该快速、可靠、可重复。投资于良好的测试覆盖率和自动化流程,可以显著提高开发效率和代码质量。