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

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


📚 目录

  • [🌟 ABP VNext + GitHub Actions:CI/CD 全流程自动化](#🌟 ABP VNext + GitHub Actions:CI/CD 全流程自动化)
    • [🤩 TL;DR](#🤩 TL;DR)
    • [🔄 全局流程概览](#🔄 全局流程概览)
    • [1️⃣ 准备工作与项目结构](#1️⃣ 准备工作与项目结构)
      • [1.1 🛠️ 工具链与 Secrets](#1.1 🛠️ 工具链与 Secrets)
      • [1.2 📁 项目目录示例](#1.2 📁 项目目录示例)
    • [2️⃣ 🔨 Build & Test(并行编译与单测)](#2️⃣ 🔨 Build & Test(并行编译与单测))
        • [🔄 子流程图](#🔄 子流程图)
    • [3️⃣ 🕵️ Static Analysis(SonarCloud & CodeQL)](#3️⃣ 🕵️ Static Analysis(SonarCloud & CodeQL))
        • [🔄 子流程图](#🔄 子流程图)
    • [4️⃣ 📦 Package & Publish(NuGet 与 Docker)](#4️⃣ 📦 Package & Publish(NuGet 与 Docker))
    • [5️⃣ 🚀 Deploy to Staging(预发布环境)](#5️⃣ 🚀 Deploy to Staging(预发布环境))
    • [6️⃣ 🏭 Deploy to Production(生产环境)](#6️⃣ 🏭 Deploy to Production(生产环境))
    • [7️⃣ ⏪ Rollback & Alert(自动回滚与告警)](#7️⃣ ⏪ Rollback & Alert(自动回滚与告警))
      • [Rollback Staging](#Rollback Staging)
      • [Rollback Production](#Rollback Production)
    • [🔧 ABP VNext 专属集成示例](#🔧 ABP VNext 专属集成示例)
      • [Program.cs 示例](#Program.cs 示例)
      • [Helm Chart 探针示例 (`charts/myapp/values.yaml`)](#Helm Chart 探针示例 (charts/myapp/values.yaml))
    • [📦 附录:配置文件](#📦 附录:配置文件)
      • sonar-project.properties
      • [CodeQL 配置 (`.github/codeql/codeql-config.yml`)](#CodeQL 配置 (.github/codeql/codeql-config.yml))

🤩 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_KEYNuGet.org 发布 Key
  • SONAR_TOKEN:SonarCloud Access Token
  • SLACK_WEBHOOK_URL:Slack Incoming Webhook URL
  • GITHUB_TOKEN(Actions 内置,用于 GHCR)

🎯 示例 CLI:

bash 复制代码
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 📁 项目目录示例

text 复制代码
.
├─ .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,上传测试报告

yaml 复制代码
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 质量与安全保障

yaml 复制代码
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 时执行包与镜像发布

yaml 复制代码
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 分支推送时执行,需审批

yaml 复制代码
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 分支推送时执行,需审批

yaml 复制代码
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

yaml 复制代码
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

yaml 复制代码
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 示例

csharp 复制代码
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)

yaml 复制代码
livenessProbe:
  httpGet:
    path: /health
    port: http
  initialDelaySeconds: 30

readinessProbe:
  httpGet:
    path: /health/ready
    port: http
  initialDelaySeconds: 10

📦 附录:配置文件

sonar-project.properties

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)

yaml 复制代码
queries:
  - security-and-quality
  - security-and-performance

相关推荐
Mr.小海2 分钟前
AI编程工具对比:Cursor、GitHub Copilot与Claude Code
人工智能·低代码·chatgpt·github·aigc·copilot·ai编程
semantist@语校1 小时前
从Prompt到结构建模:如何以数据驱动重构日本语言学校体系?以国际日本语学院为例
数据结构·人工智能·ai·prompt·github·数据集·知识图谱
LinXunFeng1 小时前
Flutter - 聊天面板库动画生硬?这次让你丝滑个够
前端·flutter·github
许愿与你永世安宁3 小时前
基于Llama的RAG 3种模型配置方法
人工智能·python·自然语言处理·json·github·llama·faiss
Stark-C5 小时前
nastools继任者?极空间部署影视自动化订阅系统『MediaMaster』
运维·自动化
测试开发技术6 小时前
Git 中如何比较不同版本之间的差异?常用命令有哪些?
git·gitlab·github·面试题
网安Ruler7 小时前
服务攻防-Java组件安全&FastJson&高版本JNDI&不出网C3P0&编码绕WAF&写入文件CI链
java·安全·ci/cd
爱分享的程序员7 小时前
前端面试专栏-工程化:27.工程化实践(CI/CD、代码规范)
前端·ci/cd·面试
OEC小胖胖8 小时前
前端性能优化“核武器”:新一代图片格式(AVIF/WebP)与自动化优化流程实战
前端·javascript·性能优化·自动化·web