[Github Actions] c#(.NET Framework) を ビルドする

今更ながら "Github Actions" 環境で c# (.NET Framework) アプリを CI/CD してみたいと思いまして、トライした内容をこちらで記載してみたいと思います。

 

 

 

1. 環境準備

 

1.1. "Github" にプロジェクトを作成

"Github" のアカウントを既に所有しているものとして記載します。

 

1.2. c# (.NET Framework 4.8) プロジェクトを作成

Pipeline を実行する対象となるプロジェクトを作成します。

ここでは以下のようなテストプロジェクトを作成して進めました。

 

Step 1

先ほど作成した Git プロジェクト(Counter)を Clone します。

 

Step 2

Clone した場所へ Visual Studio で新しい c# プロジェクトを作成します。

私は以下のような環境で c# プロジェクトを作成しました。

 

[環境]

コンパイラ : Visual Studio 2022, Version 17.9.2
開発言語: c#  
フレームワーク: .NET Framework 4.8 4.8.09032
テストフレームワーク: MSTest.TestAdapter, 3.1.1
  MSTest.TestFramework, 3.1.1
  NUnit, 4.0.1
  NUnit3TestAdapter, 4.5.0
OS : Windows11 home, 23H2

 

重要

上記「テストフレームワーク」に記載の4つは、必ず NuGet で登録を行います。

NuGet 以外の方法で環境に登録すると、後述の Pipelines で期待する動作をすることが難しくなります。

 

NuGet パッケージ

 

代表的な Unit テストフレームワーク(NUnit、MSTestV1、MSTestV2)についても準備して動作を評価してみます。

 

[table.] 各プロジェクトの説明
プロジェクト名 内容
CounterDll 値(Integer)を管理する class Counter を提供する dll。
class Couter は、Increment, Decrement, Clear 3つのメソッドを持つ。
CounterForm class Counter を使った WinForm アプリ。
CounterWPF class Counter を使った WPF アプリ。
testCounter1 CounterDll をテストする。NUnt 版。
testCounter2 CounterDll をテストする。MSTestV1 版。
testCounter3 CounterDll をテストする。MSTestV2 版。

 

Visual Studio 上の画面の様子です。

Visual Studio

 

図: CounterForm 画面例

CounterForm

 

図: CounterWPF 画面例

CounterWPF

 

 

1.3. Git リポジトリへプロジェクトをコミット&プッシュ

Github へコミット&プッシュします。手順については本ページの主たる内容から外れるので記載を割愛します。

 

 

以上で Github Actions を操作する前の事前準備を完了です。

 


 

2. Actions を有効化する

Github にプログラムをコミットされている状態からスタートします。まずは Actions を有効化するまで進めましょう。

Github メニュー上にある「Actions」をクリックします。

github > actions

 

"Suggested for this repository" として ".NET Desktop" をご提案頂いています。これの[Configure]ボタンをクリックしてみます。

Suggested for this repository

 

"dotnet-desktop.yml" を自動生成してくれました。出力されたままの情報を以下に記載します。

["dotnet-desktop.yml"]

  1. # This workflow uses actions that are not certified by GitHub.
  2. # They are provided by a third-party and are governed by
  3. # separate terms of service, privacy policy, and support
  4. # documentation.
  5.  
  6. # This workflow will build, test, sign and package a WPF or Windows Forms desktop application
  7. # built on .NET Core.
  8. # To learn how to migrate your existing application to .NET Core,
  9. # refer to https://docs.microsoft.com/en-us/dotnet/desktop-wpf/migration/convert-project-from-net-framework
  10. #
  11. # To configure this workflow:
  12. #
  13. # 1. Configure environment variables
  14. # GitHub sets default environment variables for every workflow run.
  15. # Replace the variables relative to your project in the "env" section below.
  16. #
  17. # 2. Signing
  18. # Generate a signing certificate in the Windows Application
  19. # Packaging Project or add an existing signing certificate to the project.
  20. # Next, use PowerShell to encode the .pfx file using Base64 encoding
  21. # by running the following Powershell script to generate the output string:
  22. #
  23. # $pfx_cert = Get-Content '.\SigningCertificate.pfx' -Encoding Byte
  24. # [System.Convert]::ToBase64String($pfx_cert) | Out-File 'SigningCertificate_Encoded.txt'
  25. #
  26. # Open the output file, SigningCertificate_Encoded.txt, and copy the
  27. # string inside. Then, add the string to the repo as a GitHub secret
  28. # and name it "Base64_Encoded_Pfx."
  29. # For more information on how to configure your signing certificate for
  30. # this workflow, refer to https://github.com/microsoft/github-actions-for-desktop-apps#signing
  31. #
  32. # Finally, add the signing certificate password to the repo as a secret and name it "Pfx_Key".
  33. # See "Build the Windows Application Packaging project" below to see how the secret is used.
  34. #
  35. # For more information on GitHub Actions, refer to https://github.com/features/actions
  36. # For a complete CI/CD sample to get started with GitHub Action workflows for Desktop Applications,
  37. # refer to https://github.com/microsoft/github-actions-for-desktop-apps
  38.  
  39. name: .NET Core Desktop
  40.  
  41. on:
  42. push:
  43. branches: [ "main" ]
  44. pull_request:
  45. branches: [ "main" ]
  46.  
  47. jobs:
  48.  
  49. build:
  50.  
  51. strategy:
  52. matrix:
  53. configuration: [Debug, Release]
  54.  
  55. runs-on: windows-latest # For a list of available runner types, refer to
  56. # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on
  57.  
  58. env:
  59. Solution_Name: your-solution-name # Replace with your solution name, i.e. MyWpfApp.sln.
  60. Test_Project_Path: your-test-project-path # Replace with the path to your test project, i.e. MyWpfApp.Tests\MyWpfApp.Tests.csproj.
  61. Wap_Project_Directory: your-wap-project-directory-name # Replace with the Wap project directory relative to the solution, i.e. MyWpfApp.Package.
  62. Wap_Project_Path: your-wap-project-path # Replace with the path to your Wap project, i.e. MyWpf.App.Package\MyWpfApp.Package.wapproj.
  63.  
  64. steps:
  65. - name: Checkout
  66. uses: actions/checkout@v3
  67. with:
  68. fetch-depth: 0
  69.  
  70. # Install the .NET Core workload
  71. - name: Install .NET Core
  72. uses: actions/setup-dotnet@v3
  73. with:
  74. dotnet-version: 6.0.x
  75.  
  76. # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild
  77. - name: Setup MSBuild.exe
  78. uses: microsoft/setup-msbuild@v1.0.2
  79.  
  80. # Execute all unit tests in the solution
  81. - name: Execute unit tests
  82. run: dotnet test
  83.  
  84. # Restore the application to populate the obj folder with RuntimeIdentifiers
  85. - name: Restore the application
  86. run: msbuild $env:Solution_Name /t:Restore /p:Configuration=$env:Configuration
  87. env:
  88. Configuration: ${{ matrix.configuration }}
  89.  
  90. # Decode the base 64 encoded pfx and save the Signing_Certificate
  91. - name: Decode the pfx
  92. run: |
  93. $pfx_cert_byte = [System.Convert]::FromBase64String("${{ secrets.Base64_Encoded_Pfx }}")
  94. $certificatePath = Join-Path -Path $env:Wap_Project_Directory -ChildPath GitHubActionsWorkflow.pfx
  95. [IO.File]::WriteAllBytes("$certificatePath", $pfx_cert_byte)
  96.  
  97. # Create the app package by building and packaging the Windows Application Packaging project
  98. - name: Create the app package
  99. run: msbuild $env:Wap_Project_Path /p:Configuration=$env:Configuration /p:UapAppxPackageBuildMode=$env:Appx_Package_Build_Mode /p:AppxBundle=$env:Appx_Bundle /p:PackageCertificateKeyFile=GitHubActionsWorkflow.pfx /p:PackageCertificatePassword=${{ secrets.Pfx_Key }}
  100. env:
  101. Appx_Bundle: Always
  102. Appx_Bundle_Platforms: x86|x64
  103. Appx_Package_Build_Mode: StoreUpload
  104. Configuration: ${{ matrix.configuration }}
  105.  
  106. # Remove the pfx
  107. - name: Remove the pfx
  108. run: Remove-Item -path $env:Wap_Project_Directory\GitHubActionsWorkflow.pfx
  109.  
  110. # Upload the MSIX package: https://github.com/marketplace/actions/upload-a-build-artifact
  111. - name: Upload build artifacts
  112. uses: actions/upload-artifact@v3
  113. with:
  114. name: MSIX Package
  115. path: ${{ env.Wap_Project_Directory }}\AppPackages

 

この yml ファイルをコミットすることで Actions も実行されますが、当然ながらエラーになります。

エラーではありますが一旦これで Actions を動作できるようになりました。

 

 


 

3. ビルド、アーティファクト登録

デフォルトのままではビルドも通らずエラーで終わってしまうので、まずはビルドしてアーティファクトへ登録するまで yml ファイルを修正します。

こんな感じ。これで正常にビルドしてアーティファクト登録できました。

 

["dotnet-desktop.yml"]

  1.  
  2. name: Build Counter App
  3. on:
  4. push:
  5. branches: [ "main" ]
  6. pull_request:
  7. branches: [ "main" ]
  8. jobs:
  9. build:
  10.  
  11. strategy:
  12. matrix:
  13. configuration: [Debug, Release]
  14.  
  15. runs-on: windows-latest # For a list of available runner types, refer to
  16. # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on
  17. steps:
  18. - name: Checkout
  19. uses: actions/checkout@v4.1.1
  20. with:
  21. fetch-depth: 0
  22.  
  23. - name: setup-msbuild
  24. uses: microsoft/setup-msbuild@v2
  25. - name: Setup NuGet.exe for use with actions
  26. # You may pin to the exact commit or the version.
  27. # uses: NuGet/setup-nuget@a21f25cd3998bf370fde17e3f1b4c12c175172f9
  28. uses: NuGet/setup-nuget@v2.0.0
  29. - name: Restore NuGet Packages
  30. run: nuget restore Counter.sln
  31. - name: Build Counter App
  32. run: msbuild Counter.sln /p:Configuration=$env:Configuration
  33. env:
  34. Configuration: ${{ matrix.configuration }}
  35.  
  36. - name: Upload GitHub Pages artifact - CounterWPF
  37. uses: actions/upload-pages-artifact@v3.0.1
  38. with:
  39. # Artifact name
  40. name: CounterWPF_${{ matrix.configuration }}
  41. # Path of the directory containing the static assets.
  42. path: CounterWPF\bin\${{ matrix.configuration }}
  43.  
  44. - name: Upload GitHub Pages artifact - CounterForm
  45. uses: actions/upload-pages-artifact@v3.0.1
  46. with:
  47. # Artifact name
  48. name: CounterForm_${{ matrix.configuration }}
  49. # Path of the directory containing the static assets.
  50. path: CounterForm\bin\${{ matrix.configuration }}

 

補足説明

「Actions」をクリックしたときの画面です。

Actions

 

一番上の結果をクリックして表示した画面です。Artifacts として4つ登録されていることがわかります。

各 Artifacts をクリックすると zip 圧縮されたビルド結果をダウンロードすることができます。

Summary

 

「Matrix: build」の部分をクリックしていくと下図のような画面を表示します。

エラーになっている場合はこちらの画面上で詳細を確認できます。

build (Release)

 

こちら、試行錯誤中のエラー画面例です。こんな感じでエラー発生場所および詳細を確認することができます。

ERROR 例

 

 


 

 

4. UnitTest

ユニットテスト(UnitTest)の実施を追加します。

 

4-1. (方法1) microsoft/vstest-action

"microsoft/vstest-action" を使用して実現します。

 

補足説明:

 

["dotnet-desktop.yml"]

  1. name: Build Counter App
  2. on:
  3. push:
  4. branches: [ "main" ]
  5. pull_request:
  6. branches: [ "main" ]
  7. jobs:
  8. build:
  9.  
  10. strategy:
  11. matrix:
  12. configuration: [Debug, Release]
  13.  
  14. runs-on: windows-latest # For a list of available runner types, refer to
  15. # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on
  16. steps:
  17. - name: Checkout
  18. uses: actions/checkout@v4.1.1
  19. with:
  20. fetch-depth: 0
  21.  
  22. - name: setup-msbuild
  23. uses: microsoft/setup-msbuild@v2
  24. - name: Setup NuGet.exe for use with actions
  25. # You may pin to the exact commit or the version.
  26. # uses: NuGet/setup-nuget@a21f25cd3998bf370fde17e3f1b4c12c175172f9
  27. uses: NuGet/setup-nuget@v2.0.0
  28. - name: Restore NuGet Packages
  29. run: nuget restore Counter.sln
  30. - name: Build Counter App
  31. #run: msbuild Counter.sln /p:Configuration=Release
  32. run: msbuild Counter.sln /p:Configuration=$env:Configuration
  33. env:
  34. Configuration: ${{ matrix.configuration }}
  35.  
  36. - name: vstest-action
  37. uses: microsoft/vstest-action@v1.0.0
  38. with:
  39. # Run tests from the specified files
  40. testAssembly: |
  41. **\${{ matrix.configuration }}\testCounter1.dll
  42. **\${{ matrix.configuration }}\testCounter2.dll
  43. **\${{ matrix.configuration }}\testCounter3.dll
  44. !**\obj\**
  45. - name: Upload GitHub Pages artifact - CounterWPF
  46. uses: actions/upload-pages-artifact@v3.0.1
  47. with:
  48. # Artifact name
  49. name: CounterWPF_${{ matrix.configuration }}
  50. # Path of the directory containing the static assets.
  51. path: CounterWPF\bin\${{ matrix.configuration }}
  52.  
  53. - name: Upload GitHub Pages artifact - CounterForm
  54. uses: actions/upload-pages-artifact@v3.0.1
  55. with:
  56. # Artifact name
  57. name: CounterForm_${{ matrix.configuration }}
  58. # Path of the directory containing the static assets.
  59. path: CounterForm\bin\${{ matrix.configuration }}

 

UnitTest を実行した結果画面です。

期待する16個のテストを実施し、全てパスしていることを画面上で確認できました。

vstest-action result.

 

うまく動作してそうです。全テスト(16項目)が実行されて Passed になっていることを確認できました。

なお、"microsoft/vstest-action@v1.0.0" について、下図のような警告表示がありました。

Warning

 

具体的な文章は下記の通り。Node.js 関連みたいに読めます。

Node.js 16 actions are deprecated. Please update the following actions to use Node.js 20: microsoft/vstest-action@v1.0.0. For more information see: https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/.
The following actions uses node12 which is deprecated and will be forced to run on node16: microsoft/vstest-action@v1.0.0. For more info: https://github.blog/changelog/2023-06-13-github-actions-all-actions-will-run-on-node16-instead-of-node12-by-default/

vstest-action は最新版 v1.0.0 を使っているし、説明文の内容からたぶん実害無いと判断。解決策もわからないので一旦無視したいと思います。解決情報など見つけたらこちらの記事を更新するようにします。

 

 

4-2. (方法2) darenm/Setup-VSTest

"darenm/Setup-VSTest" を使用して実現します。

DEPRECATED (廃止された機能) となっていますが本書記載時点では問題なく使えました。

 

補足説明:

 

["dotnet-desktop.yml"]

  1. name: Build Counter App
  2. on:
  3. push:
  4. branches: [ "main" ]
  5. pull_request:
  6. branches: [ "main" ]
  7. jobs:
  8. build:
  9.  
  10. env:
  11. buildPlatform: 'Any CPU'
  12.  
  13. strategy:
  14. matrix:
  15. #configuration: [Debug, Release]
  16. configuration: [Debug]
  17.  
  18. runs-on: windows-latest # For a list of available runner types, refer to
  19. # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on
  20. steps:
  21. - name: Checkout
  22. uses: actions/checkout@v4.1.1
  23. with:
  24. fetch-depth: 0
  25.  
  26. - name: setup-msbuild
  27. uses: microsoft/setup-msbuild@v2
  28. - name: Setup NuGet.exe for use with actions
  29. # You may pin to the exact commit or the version.
  30. # uses: NuGet/setup-nuget@a21f25cd3998bf370fde17e3f1b4c12c175172f9
  31. uses: NuGet/setup-nuget@v2.0.0
  32. - name: Restore NuGet Packages
  33. run: nuget restore Counter.sln
  34. - name: Build Counter App
  35. #run: msbuild Counter.sln /p:Configuration=Release
  36. run: msbuild Counter.sln /p:Configuration=$env:Configuration
  37. env:
  38. Configuration: ${{ matrix.configuration }}
  39.  
  40. # Setup VSTest
  41. - name: Setup VSTest.console.exe
  42. # You may pin to the exact commit or the version.
  43. # uses: darenm/Setup-VSTest@d9a5dffa3f11d9c27ec42eb69515d3aaeaad9ef8
  44. uses: darenm/Setup-VSTest@v1.2
  45.  
  46. # VSTest
  47. - name: Run VSTest
  48. run: >
  49. vstest.console.exe
  50. **/bin/Debug/testCounter*.dll
  51.  
  52. - name: Upload GitHub Pages artifact - CounterWPF
  53. uses: actions/upload-pages-artifact@v3.0.1
  54. with:
  55. # Artifact name
  56. name: CounterWPF_${{ matrix.configuration }}
  57. # Path of the directory containing the static assets.
  58. path: CounterWPF\bin\${{ matrix.configuration }}
  59.  
  60. - name: Upload GitHub Pages artifact - CounterForm
  61. uses: actions/upload-pages-artifact@v3.0.1
  62. with:
  63. # Artifact name
  64. name: CounterForm_${{ matrix.configuration }}
  65. # Path of the directory containing the static assets.
  66. path: CounterForm\bin\${{ matrix.configuration }}

 

UnitTest を実行した結果画面です。

期待する16個のテストを実施し、全てパスしていることを画面上で確認できました。

UnitTest 実行結果

 

 

4-3. (方法3) dotnet test

"dotnet test" を使用して実現します。

 

補足説明:

 

["dotnet-desktop.yml"]

  1. name: Build Counter App
  2. on:
  3. push:
  4. branches: [ "main" ]
  5. pull_request:
  6. branches: [ "main" ]
  7. jobs:
  8. build:
  9.  
  10. env:
  11. buildPlatform: 'Any CPU'
  12.  
  13. strategy:
  14. matrix:
  15. #configuration: [Debug, Release]
  16. configuration: [Debug]
  17.  
  18. runs-on: windows-latest # For a list of available runner types, refer to
  19. # https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on
  20. steps:
  21. - name: Checkout
  22. uses: actions/checkout@v4.1.1
  23. with:
  24. fetch-depth: 0
  25.  
  26. - name: setup-msbuild
  27. uses: microsoft/setup-msbuild@v2
  28. - name: Setup NuGet.exe for use with actions
  29. # You may pin to the exact commit or the version.
  30. # uses: NuGet/setup-nuget@a21f25cd3998bf370fde17e3f1b4c12c175172f9
  31. uses: NuGet/setup-nuget@v2.0.0
  32. - name: Restore NuGet Packages
  33. run: nuget restore Counter.sln
  34. - name: Build Counter App
  35. #run: msbuild Counter.sln /p:Configuration=Release
  36. run: msbuild Counter.sln /p:Configuration=$env:Configuration
  37. env:
  38. Configuration: ${{ matrix.configuration }}
  39.  
  40. - name: dotnet test
  41. run: >
  42. dotnet test
  43. **\bin\${{ matrix.configuration }}\testCounter*.dll
  44.  
  45. - name: Upload GitHub Pages artifact - CounterWPF
  46. uses: actions/upload-pages-artifact@v3.0.1
  47. with:
  48. # Artifact name
  49. name: CounterWPF_${{ matrix.configuration }}
  50. # Path of the directory containing the static assets.
  51. path: CounterWPF\bin\${{ matrix.configuration }}
  52.  
  53. - name: Upload GitHub Pages artifact - CounterForm
  54. uses: actions/upload-pages-artifact@v3.0.1
  55. with:
  56. # Artifact name
  57. name: CounterForm_${{ matrix.configuration }}
  58. # Path of the directory containing the static assets.
  59. path: CounterForm\bin\${{ matrix.configuration }}

 

実行結果を下図に示します。結果、testCouter2.dll (MSTestV1) によるユニットテストを実行してくれませんでした。

他の2つは期待通りに動作しているので、NUnit および MSTestV2 によるユニットテストであればこの方法で実施できそうに見えます。

Result of dotnet test.

 

 


 

ライセンス

本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。

The MIT License (MIT)

  Copyright 2024 Kinoshita Hidetoshi

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 

 

参考

 


 

変更履歴

2024-03-11 - 4-3._(方法3)_dotnet_test」を追加
2024-03-06 - 4._UnitTest」を追加
2024-03-05 - 新規作成