ABP VNext + GitHub Actions:CI/CD 全流程自动化

发布于:2025-07-17 ⋅ 阅读:(15) ⋅ 点赞:(0)

🌟 ABP VNext + GitHub Actions:CI/CD 全流程自动化



🤩 TL;DR

  • 🚀 端到端流水线:Push → 并行编译/测试 → 静态扫描 (SonarCloud/CodeQL) → NuGet/Docker 打包 → 分环境部署 → 自动回滚
  • 🔒 严格审批:在 GitHub Environments 中分别为 stagingproduction 配置 Required Reviewers
  • 性能优化:NuGet 缓存、actions/cache、Docker Layer 缓存、并行 Jobs、Concurrency 控制
  • 🛠️ 深度契合 ABP VNext:自动执行 EF Core 迁移、Swagger/UI、Health Checks 与 AKS 探针

🔄 全局流程概览

Yes
No
Yes
No
🛎️ Push 代码
🔨 Build & Test
🕵️ Static Scan
📦 Package & Publish
🚀 Deploy to Staging
✅ Staging OK?
🏭 Deploy to Production
⏪ Rollback Staging
✅ Production OK?
🎉 完成
⏪ Rollback Production

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 发布 Key
  • SONAR_TOKEN:SonarCloud Access Token
  • SLACK_WEBHOOK_URL:Slack Incoming Webhook URL
  • GITHUB_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
🔄 子流程图
✅ Checkout
🗄️ Cache NuGet
📦 Setup SDK
🔄 Restore
🏗️ Build
🧪 Test
💾 Upload Artifacts

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
🔄 子流程图
SonarCloud
CodeQL
👥 Checkout
🔍 Tool?
📊 Run SonarCloud
⚙️ Init CodeQL
🚧 Autobuild
🔎 Analyze

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


网站公告

今日签到

点亮在社区的每一天
去签到