GitHub ActionsからNuGetにパッケージをアップロードした

Published on
Updated on

はじめに

GitHub Actionsで.NETプロジェクトからNuGetパッケージの作成、Releaseの作成およびNuGetにパッケージをアップロードするまでをまとめた記事です。

リポジトリはこちら

TL;DR

  • GitHub ActionsでNuGetパッケージを作成した。
  • 作成したNuGetパッケージをNuGetにアップロードした。
  • タグからリリース/プレリリースを判断できるようにした。

対象の.NETプロジェクトの設定

パッケージ化する.NETプロジェクトの.csprojファイルを更新します。 今回はビルド構成として.NET 5と.NET Core 3.1のdllを生成するために、TargetFrameworksnet5.0netcoreapp3.1を構成します。

そして、NuGetの情報を構成します。今回は.csprojに構成しましたが、.nuspecファイルを作成してNuGet情報だけを切り離して構成することも可能なようです。 PackageVersionはcsprojをリリースのたびに変更せずに、ビルド時にバージョンを指定できるように、$(Version)の環境変数を使います。

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <TargetFrameworks>net5.0;netcoreapp3.1</TargetFrameworks>
    </PropertyGroup>
    
    <!-- NuGet -->
    <PropertyGroup>
        <PackageId>Mulinq</PackageId>
        <PackageVersion>$(Version)</PackageVersion>
        <Title>Mulinq</Title>
        <Authors>AconCavy</Authors>
        <Description>Mulinq is C# LINQ extensions for collections and for multidimensional arrays.</Description>
        <PackageProjectUrl>https://github.com/AconCavy/Mulinq</PackageProjectUrl>
        <PackageLicenseExpression>MIT</PackageLicenseExpression>
        <RepositoryUrl>https://github.com/AconCavy/Mulinq</RepositoryUrl>
        <PackageTags>LINQ</PackageTags>
    </PropertyGroup>

</Project>

NuGetの設定

NuGetアカウントを持っていない場合はアカウントの作成をします。Microsoftアカウントから作成もできるみたいです。

NuGetパッケージのアップロードには、NuGetのAPIキーが必要なので、APIキーを生成します。 画面右上のユーザから、API Keysのページに移動し、Createフォームから、Key NamePackage Owner等必要な情報を埋め、APIキーを生成します。 生成に成功すると、Manageパネルに生成したAPIキーが並ぶので、CopyでAPIキーをコピーします。一度ページから離れてしまうと、再びコピーできなくなるので、できなくなった場合はRegenerateから再生成します。

コピーしたAPIキーをGitHubリポジトリのSecretsに登録することで、GitHub Actionsの環境変数としてアクセスできるようになります。リポジトリのSetting -> Secrets -> New repository secretで新しいシークレットを作成し、名前とAPIキーを登録します。今回はNUGET_API_KEYとして登録しました。

Workflowの作成

リポジトリを作成したときにやっておきたいことのReleaseの作成をもとにWorkflowを作成します。

RelaseのWorkflowを実行するトリガーとして、v1.0.0v1.0.0-alphaのようなGitのタグがpushされたときに限定します。

on:
  push:
    tags: 
    - 'v[0-9]+.[0-9]+.[0-9]+*'

最初にテストを実行します。今回はTargetFrameworkが複数あるため、複数の.NET SDKをセットアップします。

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Setup .NET 3.1.x
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 3.1.x
    - name: Setup .NET 5.0.x
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 5.0.x
    - name: Test
      run: dotnet test -c Release --verbosity normal

次にテストが成功した場合のみリリースを実行します。needs: [test]とすることで、testのjobが成功した場合のみ実行されるようになります。 まず、プロジェクトからNuGetパッケージを作成します。このとき、-p:Versionにバージョンを指定します。タグのバージョン情報を取得するために、${GITHUB_REF##*/v}を指定します。

dotnet pack ./src/Mulinq/Mulinq.csproj -c Release -p:Version=${GITHUB_REF##*/v} -o ./publish

GITHUB_REFの環境変数では、ワークフローをトリガーしたタグのrefを取得でき、v1.0.0のようなタグの場合はrefs/heads/v1.0.0という文字列を取得できます。そこから1.0.0の部分だけ取得し、Versionの環境変数に指定します。 ビルドに成功した場合は、./publishMulinq.1.0.0.nupkgが生成されます。

そして、NuGetのAPIを叩き、作成した.nupkgをアップロードします。secrets.NUGET_API_KEYから、リポジトリに登録したNuGetのAPIキーを参照します。secrets.<*>は上記で登録したシークレットの名前になります。

release:
    runs-on: ubuntu-latest
    needs: [test]
    steps:
    - uses: actions/checkout@v2
    - name: Setup .NET Core 3.1.x
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 3.1.x
    - name: Setup .NET 5.0.x
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 5.0.x
    - name: Build
      run: dotnet pack ./src/Mulinq/Mulinq.csproj -c Release -p:Version=${GITHUB_REF##*/v} -o ./publish
    - name: Upload to NuGet
      run: dotnet nuget push ./publish/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
    
    - name: Create Release
      id: create_release
      uses: actions/create-release@v1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        tag_name: ${{ github.ref }}
        release_name: ${{ github.ref }}
        draft: false
        prerelease: ${{ contains(github.ref, '-') }}

また、GitHubにReleaseを作成します。 prereleaseのプロパティにtrue|falseを指定することで、作成するリリースがプレリリースか否かを指定できます。そのため、タグに-が含まれているかをチェックするcontains関数を使用して、v1.0.0のような普通のリリースの場合と、v1.0.0-alphaのようなプレリリースを区別できるようにしました。

- name: Create Release
  id: create_release
  uses: actions/create-release@v1
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  with:
    tag_name: ${{ github.ref }}
    release_name: ${{ github.ref }}
    draft: false
    prerelease: ${{ contains(github.ref, '-') }}

Workflow全体としては次のようになります。

name: Release

on:
  push:
    tags: 
    - 'v[0-9]+.[0-9]+.[0-9]+*'

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Setup .NET 3.1.x
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 3.1.x
    - name: Setup .NET 5.0.x
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 5.0.x
    - name: Test
      run: dotnet test -c Release --verbosity normal
  
  release:
    runs-on: ubuntu-latest
    needs: [test]
    steps:
    - uses: actions/checkout@v2
    - name: Setup .NET Core 3.1.x
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 3.1.x
    - name: Setup .NET 5.0.x
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 5.0.x
    - name: Build
      run: dotnet pack ./src/Mulinq/Mulinq.csproj -c Release -p:Version=${GITHUB_REF##*/v} -o ./publish
    - name: Upload to NuGet
      run: dotnet nuget push ./publish/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
    
    - name: Create Release
      id: create_release
      uses: actions/create-release@v1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        tag_name: ${{ github.ref }}
        release_name: ${{ github.ref }}
        draft: false
        prerelease: ${{ contains(github.ref, '-') }}

Workflowの実行

適当にコミットを作成し、v0.0.1-alphaというタグをつけ、GitHub上にプッシュします。

作成したWorkflowが実行され、テスト、ビルド、アップロード、Releaseの作成が行われます。

NuGetへアップロード直後はUnlisted Packagesの状態でしたが、しばらくするとPublished Packagesになりました。

succeeded upload to nuget

GitHubのリリースのほうは、ちゃんとPre-Releaseで作成されています。 pre-release

まとめ

  • GitHub ActionsでNuGetパッケージを作成した。
  • 作成したNuGetパッケージをNuGetにアップロードした。
  • タグからリリース/プレリリースを判断できるようにした。

NuGetのパッケージ作成は怖くない!