mcpField
D
dotnet-gha-publish

dotnet-gha-publish

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

dotnet-gha-publish について

dotnet-gha-publishは、GitHub Actionsで.NET成果物を公開するためのスキルです。NuGetパッケージやコンテナイメージ、署名、SBOM生成、タグやGitHub Releaseを契機とする公開を扱います。

主な機能

  1. 01nuget.orgとGitHub PackagesへのNuGet公開
  2. 02コンテナイメージのビルドとプッシュ
  3. 03NuGetとコンテナイメージの署名
  4. 04SBOMの生成
  5. 05タグまたはGitHub Releaseに応じた公開

活用シーン

  1. 01バージョンタグのプッシュ時にNuGetパッケージを公開
  2. 02GHCR、DockerHub、ACRへのコンテナイメージのプッシュ
  3. 03公開成果物の署名とSBOMの設定
  4. 04GitHub Releaseを手動承認の条件として利用

インストール

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

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

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

詳細

dotnet-gha-publish

GitHub Actions で .NET プロジェクトを公開するワークフローです。NuGet パッケージを nuget.org および GitHub Packages にプッシュし、コンテナーイメージをビルドして GHCR/DockerHub/ACR にプッシュし、NuGet 署名と sigstore による成果物への署名、Microsoft SBOM ツールによる SBOM の生成、タグやリリースをトリガーとする条件付き公開を行います。

バージョンの前提: .NET 8/9/10 には actions/setup-dotnet@v4 を使用します。コンテナーイメージのビルドには docker/build-push-action@v6 を使用します。レジストリ認証には docker/login-action@v3 を使用します。Dockerfile を使わないコンテナービルドには、.NET SDK のコンテナー公開(PublishContainer を指定した dotnet publish)を使用します。

スコープの境界: このスキルは、GitHub Actions から成果物を公開するワークフローを担当します。コンテナーイメージの作成(Dockerfile のベストプラクティス、SDK コンテナーのプロパティ)は [skill:dotnet-containers] が担当します。Native AOT のコンパイル設定は [skill:dotnet-native-aot] が担当します。このスキルでは、CI パイプラインの設定に限って AOT を参照します。CLI 専用のリリースパイプライン(CLI バイナリ用の build-package-release)は [skill:dotnet-cli-release-pipeline] が担当します。スターター CI テンプレートは [skill:dotnet-add-ci] が担当します。

対象外: コンテナーイメージの作成(Dockerfile、ベースイメージの選択)については [skill:dotnet-containers] を参照してください。Native AOT の MSBuild 設定については [skill:dotnet-native-aot] を参照してください。CLI リリースパイプラインについては [skill:dotnet-cli-release-pipeline] を参照してください。スターター CI テンプレートについては [skill:dotnet-add-ci] を参照してください。Azure DevOps からの公開については [skill:dotnet-ado-publish] を参照してください。ターゲット環境へのデプロイについては [skill:dotnet-gha-deploy] を参照してください。

相互参照:コンテナーイメージの作成と SDK コンテナーのプロパティについては [skill:dotnet-containers]、CI での AOT 公開設定については [skill:dotnet-native-aot]、CLI 専用のリリース自動化については [skill:dotnet-cli-release-pipeline]、公開用スターターテンプレートについては [skill:dotnet-add-ci] を参照してください。


nuget.org への NuGet パッケージのプッシュ

タグをトリガーとしたパッケージの公開
name: Publish NuGet Package

on:
  push:
    tags:
      - 'v*'

permissions:
  contents: read

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

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

      - name: Extract version from tag
        id: version
        shell: bash
        run: |
          set -euo pipefail
          VERSION="${GITHUB_REF_NAME#v}"
          echo "version=$VERSION" >> "$GITHUB_OUTPUT"

      - name: Pack
        run: |
          set -euo pipefail
          dotnet pack src/MyLibrary/MyLibrary.csproj \
            -c Release \
            -p:Version=${{ steps.version.outputs.version }} \
            -o ./nupkgs

      - name: Push to nuget.org
        run: |
          set -euo pipefail
          dotnet nuget push ./nupkgs/*.nupkg \
            --api-key ${{ secrets.NUGET_API_KEY }} \
            --source https://api.nuget.org/v3/index.json \
            --skip-duplicate

--skip-duplicate フラグを指定すると、パッケージのバージョンがすでに公開されている場合の失敗を防げます(冪等な再試行が可能です)。

GitHub Packages への公開
- name: Push to GitHub Packages
  run: |
    set -euo pipefail
    dotnet nuget push ./nupkgs/*.nupkg \
      --api-key ${{ secrets.GITHUB_TOKEN }} \
      --source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json \
      --skip-duplicate
両方のフィードへの公開

一般向けには nuget.org に、組織内でのプレリリースには GitHub Packages に公開します。

- name: Push to nuget.org (stable releases)
  if: "!contains(steps.version.outputs.version, '-')"
  run: |
    set -euo pipefail
    dotnet nuget push ./nupkgs/*.nupkg \
      --api-key ${{ secrets.NUGET_API_KEY }} \
      --source https://api.nuget.org/v3/index.json \
      --skip-duplicate

- name: Push to GitHub Packages (all versions)
  run: |
    set -euo pipefail
    dotnet nuget push ./nupkgs/*.nupkg \
      --api-key ${{ secrets.GITHUB_TOKEN }} \
      --source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json \
      --skip-duplicate

1.2.3-preview.1 のように - を含むプレリリースバージョンは GitHub Packages のみに公開され、安定版は両方に公開されます。


コンテナーイメージのビルドとプッシュ

docker/build-push-action を使った Dockerfile ベースのビルド

カスタム Dockerfile があるプロジェクト向けです。Dockerfile の作成方法については [skill:dotnet-containers] を参照してください。

name: Publish Container Image

on:
  push:
    tags:
      - 'v*'

permissions:
  contents: read
  packages: write

jobs:
  container:
    runs-on: ubuntu-latest
    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: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/${{ github.repository }}
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha

      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
SDK コンテナーの公開(Dockerfile なし)

Dockerfile がないプロジェクトでは、.NET SDK のコンテナー公開を使用します。PublishContainer の MSBuild 設定については [skill:dotnet-containers] を参照してください。

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

- name: Log in to GHCR
  run: |
    set -euo pipefail
    echo "${{ secrets.GITHUB_TOKEN }}" | \
      docker login ghcr.io -u ${{ github.actor }} --password-stdin

- name: Publish container image
  run: |
    set -euo pipefail
    VERSION="${GITHUB_REF_NAME#v}"
    dotnet publish src/MyApp/MyApp.csproj \
      -c Release \
      -p:PublishProfile=DefaultContainer \
      -p:ContainerRegistry=ghcr.io \
      -p:ContainerRepository=${{ github.repository }} \
      -p:ContainerImageTags="\"${VERSION};latest\""
複数のレジストリへのプッシュ

同じワークフローから GHCR と DockerHub にプッシュします。

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

- name: Log in to DockerHub
  uses: docker/login-action@v3
  with:
    username: ${{ secrets.DOCKERHUB_USERNAME }}
    password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Extract metadata
  id: meta
  uses: docker/metadata-action@v5
  with:
    images: |
      ghcr.io/${{ github.repository }}
      ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}
    tags: |
      type=semver,pattern={{version}}

- name: Build and push to both registries
  uses: docker/build-push-action@v6
  with:
    context: .
    push: true
    tags: ${{ steps.meta.outputs.tags }}
    labels: ${{ steps.meta.outputs.labels }}
Azure Container Registry(ACR)へのプッシュ
- name: Log in to ACR
  uses: docker/login-action@v3
  with:
    registry: ${{ secrets.ACR_LOGIN_SERVER }}
    username: ${{ secrets.ACR_USERNAME }}
    password: ${{ secrets.ACR_PASSWORD }}

- name: Build and push to ACR
  uses: docker/build-push-action@v6
  with:
    context: .
    push: true
    tags: ${{ secrets.ACR_LOGIN_SERVER }}/myapp:${{ github.ref_name }}
Native AOT コンテナーの公開

Native AOT バイナリをコンテナーイメージとして公開します。AOT の設定は [skill:dotnet-native-aot] が担当します。ここでは CI パイプラインのステップのみを示します。

- name: Publish AOT container
  run: |
    set -euo pipefail
    dotnet publish src/MyApp/MyApp.csproj \
      -c Release \
      -r linux-x64 \
      -p:PublishAot=true \
      -p:PublishProfile=DefaultContainer \
      -p:ContainerRegistry=ghcr.io \
      -p:ContainerRepository=${{ github.repository }} \
      -p:ContainerBaseImage=mcr.microsoft.com/dotnet/runtime-deps:8.0-noble-chiseled

AOT バイナリにはランタイムが含まれているため、ベースイメージには runtime-deps で十分です。AOT の MSBuild プロパティについては [skill:dotnet-native-aot] を、ベースイメージの選択については [skill:dotnet-containers] を参照してください。


成果物への署名

NuGet パッケージへの署名

改ざんを検出できるように、証明書を使って NuGet パッケージに署名します。

- name: Sign NuGet packages
  run: |
    set -euo pipefail
    dotnet nuget sign ./nupkgs/*.nupkg \
      --certificate-path ${{ runner.temp }}/signing-cert.pfx \
      --certificate-password ${{ secrets.CERT_PASSWORD }} \
      --timestamper http://timestamp.digicert.com

CI では、base64 でエンコードされたシークレットから証明書を抽出します。

- name: Decode signing certificate
  shell: bash
  run: |
    set -euo pipefail
    echo "${{ secrets.SIGNING_CERT_BASE64 }}" | base64 -d > "${{ runner.temp }}/signing-cert.pfx"

- name: Sign NuGet packages
  run: |
    set -euo pipefail
    dotnet nuget sign ./nupkgs/*.nupkg \
      --certificate-path ${{ runner.temp }}/signing-cert.pfx \
      --certificate-password ${{ secrets.CERT_PASSWORD }} \
      --timestamper http://timestamp.digicert.com

- name: Clean up certificate
  if: always()
  run: rm -f "${{ runner.temp }}/signing-cert.pfx"
Sigstore によるコンテナーイメージへの署名

sigstore/cosign によるキーレス署名を使って、コンテナーイメージに署名します。

- name: Install cosign
  uses: sigstore/cosign-installer@v3

- name: Sign container image
  env:
    COSIGN_EXPERIMENTAL: '1'
  run: |
    set -euo pipefail
    cosign sign --yes ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}

キーレス署名では GitHub の OIDC トークンを使用するため、秘密鍵を管理する必要はありません。


SBOM の生成

Microsoft SBOM Tool

サプライチェーンの透明性を確保するため、ソフトウェア部品表(SBOM)を生成します。

- name: Generate SBOM
  uses: microsoft/sbom-action@v0
  with:
    BuildDropPath: ./nupkgs
    PackageName: MyLibrary
    PackageVersion: ${{ steps.version.outputs.version }}
    NamespaceUriBase: https://github.com/${{ github.repository }}

- name: Upload SBOM
  uses: actions/upload-artifact@v4
  with:
    name: sbom-${{ steps.version.outputs.version }}
    path: ./nupkgs/_manifest/
    retention-days: 365
コンテナーイメージの SBOM
- name: Generate container SBOM
  uses: anchore/sbom-action@v0
  with:
    image: ghcr.io/${{ github.repository }}:${{ steps.version.outputs.version }}
    artifact-name: container-sbom
    output-file: container-sbom.spdx.json
SBOM を GitHub リリースに添付
- name: Create GitHub Release with SBOM
  uses: softprops/action-gh-release@v2
  with:
    files: |
      ./nupkgs/*.nupkg
      ./nupkgs/_manifest/spdx_2.2/manifest.spdx.json
    generate_release_notes: true

タグとリリースに基づく条件付き公開

タグパターンの照合
on:
  push:
    tags:
      - 'v[0-9]+.[0-9]+.[0-9]+'         # stable: v1.2.3
      - 'v[0-9]+.[0-9]+.[0-9]+-*'        # pre-release: v1.2.3-preview.1

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - name: Determine release type
        id: release-type
        shell: bash
        run: |
          set -euo pipefail
          VERSION="${GITHUB_REF_NAME#v}"
          if [[ "$VERSION" == *-* ]]; then
            echo "prerelease=true" >> "$GITHUB_OUTPUT"
          else
            echo "prerelease=false" >> "$GITHUB_OUTPUT"
          fi
          echo "version=$VERSION" >> "$GITHUB_OUTPUT"
リリースをトリガーとする公開

GitHub リリースが作成されたときにのみ公開します(手動承認ゲートを提供します)。

on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.release.tag_name }}

      - name: Extract version
        id: version
        shell: bash
        run: |
          set -euo pipefail
          VERSION="${{ github.event.release.tag_name }}"
          VERSION="${VERSION#v}"
          echo "version=$VERSION" >> "$GITHUB_OUTPUT"

      - name: Pack and publish
        run: |
          set -euo pipefail
          dotnet pack -c Release -p:Version=${{ steps.version.outputs.version }} -o ./nupkgs
          dotnet nuget push ./nupkgs/*.nupkg \
            --api-key ${{ secrets.NUGET_API_KEY }} \
            --source https://api.nuget.org/v3/index.json \
            --skip-duplicate

エージェント利用時の注意点

  1. dotnet nuget push では必ず --skip-duplicate を使用してください — これを指定しないと、公開済みバージョンに対して公開ワークフローを再実行した際、冪等に処理されずジョブが失敗します。
  2. ワークフローファイルに API キーをハードコードしないでください — すべての認証情報には ${{ secrets.NUGET_API_KEY }} または環境スコープのシークレットを使用してください。
  3. 複数行のすべての bash ステップで set -euo pipefail を使用してください — pipefail がないと、パイプでつないだコマンドの失敗が伝播せず、CI が誤って成功扱いになります。
  4. 署名証明書は if: always() ステップでクリーンアップしてください — 秘密鍵のマテリアルを含む一時ファイルは、ジョブが失敗した場合も削除する必要があります。
  5. SDK コンテナーの公開には Docker デーモンが必要です — PublishProfile=DefaultContainer を指定した dotnet publish では、ランナーに Docker がインストールされている必要があります。Docker を含む ubuntu-latest を使用してください。
  6. AOT 公開では RID を一致させる必要があります — dotnet publish -r linux-x64 はランナーの OS と一致させる必要があります。ubuntu-latest で -r win-x64 を使用しないでください。
  7. タグをトリガーとするワークフローはプルリクエストでは実行されません — プルリクエストからプッシュされたタグでもワークフローはトリガーされます。必要に応じて、追加のガードとして if: github.ref_type == 'tag' を使用してください。
  8. GHCR の認証には PAT ではなく GITHUB_TOKEN を使用します — パブリックリポジトリでは packages: write の権限で十分です。PAT が必要なのは、リポジトリ間のアクセスを行う場合のみです。

コメント