🌟 ABP VNext + GitHub Actions:CI/CD 全流程自动化
📚 目录
- 🌟 ABP VNext + GitHub Actions:CI/CD 全流程自动化
🤩 TL;DR
- 🚀 端到端流水线:Push → 并行编译/测试 → 静态扫描 (SonarCloud/CodeQL) → NuGet/Docker 打包 → 分环境部署 → 自动回滚
- 🔒 严格审批:在 GitHub Environments 中分别为
staging
与production
配置 Required Reviewers - ⚡ 性能优化:NuGet 缓存、actions/cache、Docker Layer 缓存、并行 Jobs、Concurrency 控制
- 🛠️ 深度契合 ABP VNext:自动执行 EF Core 迁移、Swagger/UI、Health Checks 与 AKS 探针
🔄 全局流程概览
1️⃣ 准备工作与项目结构
1.1 🛠️ 工具链与 Secrets
在仓库 Settings → Secrets 添加以下凭据:
AZURE_CREDENTIALS
:Azure Service Principal JSON(az ad sp create-for-rbac … --sdk-auth
)NUGET_API_KEY
:NuGet.org 发布 KeySONAR_TOKEN
:SonarCloud Access TokenSLACK_WEBHOOK_URL
:Slack Incoming Webhook URLGITHUB_TOKEN
(Actions 内置,用于 GHCR)
🎯 示例 CLI:
az ad sp create-for-rbac \
--name "abp-ci-sp" \
--role contributor \
--scopes /subscriptions/<SUB_ID>/resourceGroups/<RG_NAME> \
--sdk-auth > azure-credentials.json
1.2 📁 项目目录示例
.
├─ .github/workflows/ci-cd.yml
├─ src/
│ ├─ MyApp.Domain/
│ ├─ MyApp.Application/
│ ├─ MyApp.EntityFrameworkCore/
│ └─ MyApp.HttpApi.Host/
└─ tests/
└─ MyApp.Tests/
2️⃣ 🔨 Build & Test(并行编译与单测)
📝 本 Job 目标:并行 Restore/Build/Test,上传测试报告
build-test:
name: 🔨 Build & Test
runs-on: ubuntu-latest
strategy:
matrix:
dotnet-version: ['8.0.x']
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Cache NuGet packages
uses: actions/cache@v3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: ${{ runner.os }}-nuget-
- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: ${{ matrix.dotnet-version }}
cache: true
- name: Restore Dependencies
run: dotnet restore src/MyApp.sln --locked-mode
- name: Build Solution
run: dotnet build src/MyApp.sln --no-restore --configuration Release
- name: Run Unit Tests
run: dotnet test tests/MyApp.Tests/MyApp.Tests.csproj \
--no-build --configuration Release --logger "trx"
- name: Upload Test Results
uses: actions/upload-artifact@v3
with:
name: test-results
path: '**/*.trx'
retention-days: 7
🔄 子流程图
3️⃣ 🕵️ Static Analysis(SonarCloud & CodeQL)
📝 本 Job 目标:Shift‐Left 质量与安全保障
static-scan:
name: 🕵️ Static Analysis
runs-on: ubuntu-latest
needs: build-test
strategy:
matrix:
tool: ['sonarcloud','codeql']
steps:
- name: Checkout Full History
uses: actions/checkout@v3
with:
fetch-depth: 0
# SonarCloud
- if: matrix.tool == 'sonarcloud'
name: SonarCloud Prepare
uses: SonarSource/sonarcloud-github-action@v1.9.0
- if: matrix.tool == 'sonarcloud'
name: Build for SonarCloud
run: dotnet build src/MyApp.sln --configuration Release
- if: matrix.tool == 'sonarcloud'
name: SonarCloud Publish
uses: SonarSource/sonarcloud-github-action@v1.9.0
# CodeQL
- if: matrix.tool == 'codeql'
name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: csharp
config-file: .github/codeql/codeql-config.yml
- if: matrix.tool == 'codeql'
name: Autobuild
uses: github/codeql-action/autobuild@v2
- if: matrix.tool == 'codeql'
name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
🔄 子流程图
4️⃣ 📦 Package & Publish(NuGet 与 Docker)
📝 本 Job 目标:仅在 Push 时执行包与镜像发布
package-publish:
name: 📦 Package & Publish
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')
runs-on: ubuntu-latest
needs: static-scan
steps:
- name: Checkout Code
uses: actions/checkout@v3
# NuGet
- name: Pack NuGet Package
run: dotnet pack src/MyApp.Application/MyApp.Application.csproj \
--no-build -o ./artifacts
- uses: NuGet/setup-nuget@v2
- name: Push to NuGet.org
run: dotnet nuget push ./artifacts/*.nupkg \
--api-key ${{ secrets.NUGET_API_KEY }} \
--source https://api.nuget.org/v3/index.json
# Docker (GHCR)
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & Push Docker Image
uses: docker/build-push-action@v4
with:
context: src/MyApp.HttpApi.Host
file: src/MyApp.HttpApi.Host/Dockerfile
push: true
tags: |
ghcr.io/${{ github.repository_owner }}/myapp:${{ github.sha }}
ghcr.io/${{ github.repository_owner }}/myapp:latest
cache-from: type=gha,scope=src-MyApp.HttpApi.Host
cache-to: type=gha,mode=max,scope=src-MyApp.HttpApi.Host
5️⃣ 🚀 Deploy to Staging(预发布环境)
📝 本 Job 目标:在 develop
分支推送时执行,需审批
deploy-staging:
name: 🚀 Deploy to Staging
runs-on: ubuntu-latest
needs: package-publish
if: github.ref == 'refs/heads/develop'
environment: staging
steps:
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Set AKS Context
uses: azure/aks-set-context@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
cluster-name: myCluster
resource-group: myRG
- name: Install EF CLI
run: |
dotnet tool install --global dotnet-ef --version 8.*
echo "$HOME/.dotnet/tools" >> $GITHUB_PATH
- name: Run EF Core Migrations
run: dotnet ef database update \
--project src/MyApp.EntityFrameworkCore/MyApp.EntityFrameworkCore.csproj \
--startup-project src/MyApp.HttpApi.Host/MyApp.HttpApi.Host.csproj \
--configuration Release
- name: Helm Upgrade (Staging)
run: |
helm upgrade myapp-staging ./charts/myapp \
--namespace staging --install \
--set image.tag=${{ github.sha }}
6️⃣ 🏭 Deploy to Production(生产环境)
📝 本 Job 目标:在 main
分支推送时执行,需审批
deploy-prod:
name: 🏭 Deploy to Production
runs-on: ubuntu-latest
needs: deploy-staging
if: github.ref == 'refs/heads/main'
environment: production
steps:
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Set AKS Context
uses: azure/aks-set-context@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
cluster-name: myCluster
resource-group: myRG
- name: Helm Upgrade (Production)
run: |
helm upgrade myapp-prod ./charts/myapp \
--namespace prod --install \
--set image.tag=${{ github.sha }}
7️⃣ ⏪ Rollback & Alert(自动回滚与告警)
Rollback Staging
rollback-staging:
name: ⏪ Rollback & Notify (Staging)
if: failure() && github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
needs: deploy-staging
steps:
- name: Determine Last Successful Revision
id: hist
run: |
rev=$(helm history myapp-staging -n staging --max 5 \
--output json | jq -r '.[] | select(.status=="DEPLOYED") | .revision' | tail -1)
if [[ -z "$rev" || "$rev" -le 0 ]]; then
echo "No valid revision to rollback"; exit 1
fi
echo "::set-output name=revision::$rev"
- name: Helm Rollback (Staging)
run: helm rollback myapp-staging ${{ steps.hist.outputs.revision }} -n staging
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2
with:
webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
message: ":warning: Staging 部署失败,已回滚到 revision ${{ steps.hist.outputs.revision }}"
Rollback Production
rollback-prod:
name: ⏪ Rollback & Notify (Production)
if: failure() && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs: deploy-prod
steps:
- name: Determine Last Successful Revision
id: hist
run: |
rev=$(helm history myapp-prod -n prod --max 5 \
--output json | jq -r '.[] | select(.status=="DEPLOYED") | .revision' | tail -1)
if [[ -z "$rev" || "$rev" -le 0 ]]; then
echo "No valid revision to rollback"; exit 1
fi
echo "::set-output name=revision::$rev"
- name: Helm Rollback (Production)
run: helm rollback myapp-prod ${{ steps.hist.outputs.revision }} -n prod
- name: Slack Notification
uses: rtCamp/action-slack-notify@v2
with:
webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
message: ":x: Production 部署失败,已回滚到 revision ${{ steps.hist.outputs.revision }}"
🔧 ABP VNext 专属集成示例
Program.cs 示例
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddApplication<MyAppHttpApiHostModule>();
builder.Host.UseAutofac();
var app = builder.Build();
app.UseAbpRequestLocalization();
app.UseAbpSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "MyApp API V1");
});
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapHealthChecks("/health");
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = reg => reg.Name.Contains("ready")
});
app.Run();
Helm Chart 探针示例 (charts/myapp/values.yaml
)
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
readinessProbe:
httpGet:
path: /health/ready
port: http
initialDelaySeconds: 10
📦 附录:配置文件
sonar-project.properties
sonar.projectKey=<YOUR_PROJECT_KEY>
sonar.organization=<YOUR_ORGANIZATION>
sonar.sources=src
sonar.tests=tests
sonar.dotnet.visualstudio.solution.file=src/MyApp.sln
CodeQL 配置 (.github/codeql/codeql-config.yml
)
queries:
- security-and-quality
- security-and-performance