mcpField
D
dotnet-gha-deploy

dotnet-gha-deploy

github.com/wshaddixクラウド・DevOps8671 回インストール35 週間前に更新
リポジトリ

dotnet-gha-deploy について

.NET プロジェクト向けの GitHub Actions デプロイパターン集です。ドキュメントサイト、コンテナレジストリ、Azure Web Apps のデプロイに加え、環境承認、OIDC 認証、ロールバックを扱います。

主な機能

  1. 01GitHub Pages へのドキュメントデプロイ
  2. 02GHCR と ACR へのイメージプッシュ
  3. 03Azure Web Apps のデプロイ
  4. 04GitHub Environments によるデプロイゲート
  5. 05デプロイのロールバック戦略

活用シーン

  1. 01プロジェクトのドキュメントを GitHub Pages に公開
  2. 02同じコンテナイメージを環境間で昇格
  3. 03スロットを使った Azure Web Apps のデプロイ
  4. 04デプロイ失敗後に以前のバージョンへ復元

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-gha-deploy

インストール後、Claude Code で/dotnet-gha-deployを入力して読み込まれたか確認します。

詳細

dotnet-gha-deploy

GitHub Actions における .NET アプリケーションのデプロイパターン:ドキュメントサイト(Starlight/Docusaurus)の GitHub Pages へのデプロイ、GHCR および ACR へのコンテナレジストリプッシュのパターン、azure/webapps-deploy を使用した Azure Web Apps へのデプロイ、段階的なロールアウトの保護ルールを設定した GitHub Environments、デプロイ失敗時のロールバック戦略。

バージョンの前提条件: GitHub Actions ワークフロー構文 v2。Azure App Service には azure/webapps-deploy@v3 を使用します。Azure の認証情報管理には azure/login@v2 を使用します。デプロイゲートには GitHub Environments を使用します。

スコープの境界: このスキルは、GitHub Actions のデプロイパイプラインパターンを担当します。コンテナオーケストレーションとランタイム構成は [skill:dotnet-container-deployment] が担当します。コンテナイメージの作成は [skill:dotnet-containers] が担当します。パブリッシュ(NuGet へのプッシュ、コンテナのビルド)は [skill:dotnet-gha-publish] が担当します。コンポーザブルなワークフローパターンは [skill:dotnet-gha-patterns] にあります。スターター CI テンプレートは [skill:dotnet-add-ci] が担当します。

対象外: コンテナオーケストレーション(Kubernetes、Docker Compose)については、[skill:dotnet-container-deployment] を参照してください。コンテナイメージの作成については、[skill:dotnet-containers] を参照してください。NuGet のパブリッシュとコンテナのビルドについては、[skill:dotnet-gha-publish] を参照してください。スターター CI テンプレートについては、[skill:dotnet-add-ci] を参照してください。Azure DevOps のデプロイについては、[skill:dotnet-ado-patterns] を参照してください。CLI のリリースパイプラインについては、[skill:dotnet-cli-release-pipeline] を参照してください。

相互参照:コンテナオーケストレーションパターンについては [skill:dotnet-container-deployment]、コンテナイメージの作成については [skill:dotnet-containers]、スターター CI テンプレートについては [skill:dotnet-add-ci]、CLI 固有のリリース自動化については [skill:dotnet-cli-release-pipeline] を参照してください。


ドキュメントの GitHub Pages へのデプロイ

静的サイトのデプロイ(Starlight/Docusaurus)

.NET プロジェクトのドキュメントサイトを GitHub Pages にデプロイします。

name: Deploy Docs

on:
  push:
    branches: [main]
    paths:
      - 'docs/**'
      - '.github/workflows/deploy-docs.yml'
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: pages
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: npm
          cache-dependency-path: docs/package-lock.json

      - name: Install dependencies
        working-directory: docs
        run: npm ci

      - name: Build documentation site
        working-directory: docs
        run: npm run build

      - name: Upload Pages artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: docs/dist

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

主な判断事項:

  • concurrency.cancel-in-progress: false により、進行中の Pages デプロイがキャンセルされないようにします
  • Pages デプロイトークンには id-token: write 権限が必要です
  • build ジョブと deploy ジョブを分けることで、デプロイジョブに保護ルール付きの github-pages 環境を使用できます
XML コメントからの API ドキュメント生成

.NET の XML コメントから API リファレンスドキュメントを生成してデプロイします。

- name: Setup .NET
  uses: actions/setup-dotnet@v4
  with:
    dotnet-version: '8.0.x'

- name: Build with XML docs
  run: |
    set -euo pipefail
    dotnet build src/MyLibrary/MyLibrary.csproj \
      -c Release \
      -p:GenerateDocumentationFile=true

- name: Generate API docs with docfx
  run: |
    set -euo pipefail
    dotnet tool install -g docfx
    docfx docs/docfx.json

- name: Upload Pages artifact
  uses: actions/upload-pages-artifact@v3
  with:
    path: docs/_site

コンテナレジストリへのプッシュパターン

環境ゲートを使用した GHCR へのプッシュ
jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      image-digest: ${{ steps.build.outputs.digest }}
    steps:
      - uses: actions/checkout@v4

      - name: Log in to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push
        id: build
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}

  deploy-staging:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: staging
      url: https://staging.example.com
    steps:
      - name: Deploy container to staging
        run: |
          set -euo pipefail
          echo "Deploying ghcr.io/${{ github.repository }}@${{ needs.build.outputs.image-digest }} to staging"
          # Platform-specific deployment command here

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com
    steps:
      - name: Deploy container to production
        run: |
          set -euo pipefail
          echo "Deploying ghcr.io/${{ github.repository }}@${{ needs.build.outputs.image-digest }} to production"
ダイジェストによる昇格(不変のデプロイ)

環境間で不変のデプロイを行うには、イメージダイジェストの参照を使用します。

- name: Retag for production
  run: |
    set -euo pipefail
    # Pull by digest (immutable), retag for production
    docker pull ghcr.io/${{ github.repository }}@${{ needs.build.outputs.image-digest }}
    docker tag ghcr.io/${{ github.repository }}@${{ needs.build.outputs.image-digest }} \
      ghcr.io/${{ github.repository }}:production
    docker push ghcr.io/${{ github.repository }}:production

ダイジェストベースの昇格により、タグが変更されても、本番環境にはまったく同じイメージのバイト列がデプロイされます。


Azure Web Apps へのデプロイ

azure/webapps-deploy を使用したデプロイ
name: Deploy to Azure

on:
  push:
    branches: [main]

permissions:
  contents: read
  id-token: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'

      - name: Publish
        run: |
          set -euo pipefail
          dotnet publish src/MyApp/MyApp.csproj \
            -c Release \
            -o ./publish

      - name: Upload publish artifact
        uses: actions/upload-artifact@v4
        with:
          name: webapp
          path: ./publish

  deploy-staging:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: staging
      url: https://myapp-staging.azurewebsites.net
    steps:
      - name: Download artifact
        uses: actions/download-artifact@v4
        with:
          name: webapp
          path: ./publish

      - name: Login to Azure
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      - name: Deploy to Azure Web App
        uses: azure/webapps-deploy@v3
        with:
          app-name: myapp-staging
          package: ./publish

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://myapp.azurewebsites.net
    steps:
      - name: Download artifact
        uses: actions/download-artifact@v4
        with:
          name: webapp
          path: ./publish

      - name: Login to Azure
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      - name: Deploy to Azure Web App
        uses: azure/webapps-deploy@v3
        with:
          app-name: myapp-production
          package: ./publish
デプロイスロットを使用した Azure Web App

事前のスワップ検証を行い、ダウンタイムなしでデプロイするには、デプロイスロットを使用します。

- name: Deploy to staging slot
  uses: azure/webapps-deploy@v3
  with:
    app-name: myapp-production
    slot-name: staging
    package: ./publish

- name: Validate staging slot
  shell: bash
  run: |
    set -euo pipefail
    HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
      https://myapp-production-staging.azurewebsites.net/healthz)
    if [ "$HTTP_STATUS" != "200" ]; then
      echo "Health check failed with status $HTTP_STATUS"
      exit 1
    fi

- name: Swap slots
  uses: azure/cli@v2
  with:
    inlineScript: |
      az webapp deployment slot swap \
        --resource-group myapp-rg \
        --name myapp-production \
        --slot staging \
        --target-slot production
OIDC 認証(フェデレーション資格情報)

サービスプリンシパルシークレットの代わりに OIDC を使用して、パスワードレスで Azure 認証を行います。

- name: Login to Azure (OIDC)
  uses: azure/login@v2
  with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

OIDC を使用するには、GitHub Actions の OIDC プロバイダーを信頼するフェデレーション資格情報を Azure AD に構成する必要があります。GitHub Secrets にクライアントシークレットを保存する必要はありません。


保護ルール付きの GitHub Environments

複数環境のパイプライン
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: dotnet publish -c Release -o ./publish
      - uses: actions/upload-artifact@v4
        with:
          name: app
          path: ./publish

  deploy-dev:
    needs: build
    runs-on: ubuntu-latest
    environment: development
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: app
      - run: echo "Deploy to dev"

  deploy-staging:
    needs: deploy-dev
    runs-on: ubuntu-latest
    environment:
      name: staging
      url: https://staging.example.com
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: app
      - run: echo "Deploy to staging"

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: app
      - run: echo "Deploy to production"
保護ルールの構成

各環境の GitHub Settings > Environments で構成します。

環境必須レビュアー待機時間ブランチポリシー
developmentなしなしすべてのブランチ
stagingレビュアー 1 人なしmain、release/*
productionレビュアー 2 人15 分main のみ
環境固有のシークレットと変数

各環境では、リポジトリレベルのシークレットを上書きできます。

jobs:
  deploy:
    environment: production
    runs-on: ubuntu-latest
    steps:
      - name: Deploy with environment-specific config
        env:
          # Resolves to the production environment's secret, not the repo-level one
          DB_CONNECTION: ${{ secrets.DB_CONNECTION_STRING }}
          APP_URL: ${{ vars.APP_URL }}
        run: |
          set -euo pipefail
          echo "Deploying to $APP_URL"

ロールバックパターン

デプロイの取り消し

失敗時には、直前の正常なバージョンを再デプロイします。

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Deploy new version
        id: deploy
        continue-on-error: true
        run: |
          set -euo pipefail
          # Deploy logic here
          ./deploy.sh --version ${{ github.sha }}

      - name: Health check
        id: health
        if: steps.deploy.outcome == 'success'
        continue-on-error: true
        shell: bash
        run: |
          set -euo pipefail
          for i in {1..5}; do
            HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://example.com/healthz)
            if [ "$HTTP_STATUS" = "200" ]; then
              echo "Health check passed"
              exit 0
            fi
            sleep 10
          done
          echo "Health check failed after 5 attempts"
          exit 1

      - name: Rollback on failure
        if: steps.deploy.outcome == 'failure' || steps.health.outcome == 'failure'
        run: |
          set -euo pipefail
          echo "Rolling back to previous version"
          # Re-deploy the last known-good artifact
          ./deploy.sh --version ${{ github.event.before }}

      - name: Fail the job if rollback was needed
        if: steps.deploy.outcome == 'failure' || steps.health.outcome == 'failure'
        run: exit 1
Azure デプロイスロットのロールバック

ヘルスチェックが失敗した場合は、前のスロットにスワップし戻します。

- name: Swap to production
  id: swap
  uses: azure/cli@v2
  with:
    inlineScript: |
      az webapp deployment slot swap \
        --resource-group myapp-rg \
        --name myapp-production \
        --slot staging \
        --target-slot production

- name: Post-swap health check
  id: post-health
  continue-on-error: true
  shell: bash
  run: |
    set -euo pipefail
    sleep 30  # allow swap to stabilize
    HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://myapp.azurewebsites.net/healthz)
    if [ "$HTTP_STATUS" != "200" ]; then
      echo "Post-swap health check failed"
      exit 1
    fi

- name: Rollback swap on failure
  if: steps.post-health.outcome == 'failure'
  uses: azure/cli@v2
  with:
    inlineScript: |
      az webapp deployment slot swap \
        --resource-group myapp-rg \
        --name myapp-production \
        --slot staging \
        --target-slot production
      echo "Rolled back: swapped staging back to production"
workflow_dispatch による手動ロールバック

緊急ロールバック用の手動トリガーを用意します。

on:
  workflow_dispatch:
    inputs:
      version:
        description: 'Version to roll back to (e.g., v1.2.3)'
        required: true
        type: string
      environment:
        description: 'Target environment'
        required: true
        type: choice
        options:
          - staging
          - production

jobs:
  rollback:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ inputs.version }}

      - name: Publish
        run: |
          set -euo pipefail
          dotnet publish src/MyApp/MyApp.csproj -c Release -o ./publish

      - name: Deploy rollback version
        run: |
          set -euo pipefail
          echo "Rolling back ${{ inputs.environment }} to ${{ inputs.version }}"
          # Platform-specific deployment

エージェント向けの注意事項

  1. 複数行のすべての bash ステップで set -euo pipefail を使用してください -- pipefail がないと、パイプ処理されたコマンドの失敗が通知されず、デプロイが成功したように見えてしまいます。
  2. デプロイの並行処理グループで cancel-in-progress: true は使用しないでください -- 進行中のデプロイをキャンセルすると、インフラストラクチャがデプロイ途中の状態になる可能性があります。
  3. デプロイ後は必ずヘルスチェックを実行してください -- deploy ステップが成功しても、アプリケーションが正しく動作しているとは限りません。HTTP ヘルスチェックで確認してください。
  4. OIDC Azure ログインには id-token: write 権限を使用してください -- これがないと、フェデレーション資格情報の交換が、原因のわかりにくい 403 エラーで失敗します。
  5. デプロイスロットのスワップはアトミックです -- スワップが失敗した場合、両方のスロットには元のデプロイが維持され、中途半端な状態にはなりません。
  6. ワークフローファイルに Azure の資格情報をハードコードしないでください -- OIDC フェデレーション資格情報、または環境スコープのシークレットを使用してください。YAML にハードコードされたシークレットは、リポジトリの履歴から閲覧できます。
  7. 本番環境へのデプロイではダイジェストベースのイメージ参照を使用してください -- タグは変更や上書きが可能ですが、ダイジェストは変更できず、イメージのバイト列を正確に保証します。
  8. ビルドジョブとデプロイジョブを分離してください -- 成果物を一度だけビルドし、同じ成果物を複数の環境にデプロイすることで、一貫性を確保できます。

コメント