Visual Studio拡張のプロジェクトは、今でも .NET Frameworkです。 .NET Frameworkということは、基本的にWindowsでのビルドになります。
今回、AppVeyorでやっていたビルドをGitHub Actionsに移行したのでメモ。
最終的に、次のように .NET FrameworkのビルドがGitHub Actionsで完結します。
- TL;DR
- リポジトリ
- 事前知識
- CIサービスの変化
- パスを解決する
- NuGet のパッケージリストアを行う
- ビルドを行う
- Visual Studio 拡張ビルド時のdevenv 初期化をスキップする
- パッケージをアップロードして GitHub Webから取得できるようにする
- おまけ
- REF
TL;DR
- .NET FrameworkのアプリもGitHubホストランナーでWindows OSを選べば問題ない
- Visual Studio拡張のような変なビルドでも問題ないので安心してビルドできる
- GitHub ActionsはAppVeyorにトドメを刺しそう
リポジトリ
VS拡張で提供しているOpenUserSecretsをVS2019対応するついでにAppVeyorからGitHub Actionsに移植します。
事前知識
GitHub Actionsでの書き方など一通りの注意は公式Docみるかまとめたので参照してください。
CIサービスの変化
- Before: AppVeyor
- After: GitHub Actions
前提として、AppVeyor / GitHub ActionsのいずれにおいてもWindows依存のビルドはDocker Imageでのビルド実行ではなくホストマシンでの実行となります。
それぞれの定義を見ていきます。
AppVeyor の定義
image: Visual Studio 2017 version: '1.0.{build}' shallow_clone: false pull_requests: do_not_increment_build_number: false configuration: Release platform: Any CPU before_build: - nuget restore src/OpenUserSecrets.sln build: project: src/OpenUserSecrets.sln artifacts: - path: '**\*.vsix'
AppVeyorは、イメージにVisual Studioなどのツールが入っているのでVisual Studio 2017
やVisual Studio 2019
を選んでおきます。
GitHub Actions
OpenUserSecrets/.github/workflows/build.yml at master · guitarrapc/OpenUserSecrets · GitHub
name: build on: [push] jobs: build: runs-on: windows-2019 steps: - uses: actions/checkout@v1 - uses: warrenbuckley/Setup-Nuget@v1 - run: nuget restore $Env:GITHUB_WORKSPACE\src\OpenUserSecrets\OpenUserSecrets.csproj - uses: warrenbuckley/Setup-MSBuild@v1 - run: MSBuild.exe $Env:GITHUB_WORKSPACE\src\OpenUserSecrets\OpenUserSecrets.csproj -p:Configuration=Release timeout-minutes: 5 - uses: actions/upload-artifact@v1.0.0 with: name: artifacts path: src\OpenUserSecrets\bin\Release\OpenUserSecrets.vsix
GitHub Actionsは、hostによってインストールされているツールが変わります。
Windows-2019はVisual StudioおよびMicrosoft.VisualStudio.Workload.VisualStudioExtension
が入っているので、このホストイメージで問題ありません。
パスを解決する
こういったCIでビルドするときに妙なはまり方をしやすいのが「PATH」です。 特にツールのパスは、「どこにインストールされたのか興味がない」のに、パスがとっていないと気にする必要があります。 そのため、こういったツールを利用するときはパスを通すのが定石です。
.NET Frameworkのビルドは、「パッケージをNuGetで復元する」「msbuildでビルド」というよくある2段階を踏んで実行されます。 この2つで使うツールを、GitHub Actionsでパス解決しつつ実行する方法を考えましょう。
nuget.exe のパス解決
NuGetのパッケージリストアはnuget.exeを使って行います。
nuget.exeのパス解決は、uses: warrenbuckley/Setup-Nuget@v1
で行えるのでぜひ利用しましょう。
nuget restore csprojのパス
でNuGetパッケージリストアが行えるようになりました。
MSBuild.exe のパス解決
MSBuildは通常Visual Studioを一緒に入っています。
これを解決するツールとしてvswhere
があるのですが、そんなものを使わずwarrenbuckley/Setup-MSBuild
を使いましょう。パスに入れてくれます。
MSBuild.exe csprojやslnのパス
でビルドが実行できるようになりました。
NuGet のパッケージリストアを行う
基本的に、現在のリポジトリのチェックアウトパスに興味ありません。
環境変数GITHUB_WORKSPACE
を使うといい感じにcheckoutしたときのベースパスが解決されます。
これで、csprojのパスが$Env:GITHUB_WORKSPACE\src\OpenUserSecrets\OpenUserSecrets.csproj
とわかりました。
キャッシュを考えそうですが、今回のような即座にパッケージリストアが完了する場合は考えなくてもいいでしょう。
いい感じでNuGetのパッケージリストアが実行できました。
ビルドを行う
ビルド時のcsprojパスもNuGetと同じでGITHUB_WORKSPACE
を使えばokです。
また、今回はVisual Studio拡張をビルドしたら、そのビルドパッケージを配布するのでリリースビルドを行います。
MSBuild実行時時にConfigurationプロパティをReleaseに切り替えればokです。
いい感じでMSBuildが実行されました。
デフォルトのcsprojは、CIでビルドするときに次のセクションで紹介するdevenv初期化が走って限界である6 hourまでタスクがタイムアウトしません。
timeout-minute: 5
は、ビルドが5分以上かかること自体、異常とみなしてタイムアウトを仕掛けています。
Visual Studio 拡張ビルド時のdevenv 初期化をスキップする
Visual Stduio拡張は、クラスライブラリなどと違って「ビルド時にVisual Stduio )devenv.exe) の初期化を行う」動きをします。 CI的にはdevenvの初期化なんてされてほしくないわけで、実際永遠に終わりません、厄介! (6 hour timeoutでビルド失敗する悲劇が起こる)
対策はいくつか考えられます。
私のオススメは、PropertyGroupでDeployExtension
をfalseにして初期化を行わないことです。
csprojに<DeployExtension Condition="'$(GITHUB_ACTIONS)' != ''">False</DeployExtension>
を設定しておけばGitHub Actionsでのみ無効化されます。
DebugとReleaseビルドで無効化したいので次のようになるでしょう。
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <!-- Stop Initialize Visual Studio Experimental Instance on CI --> <DeployExtension Condition="'$(GITHUB_ACTIONS)' != ''">False</DeployExtension> <!-- 省略 --> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <!-- Stop Initialize Visual Studio Experimental Instance on CI --> <DeployExtension Condition="'$(GITHUB_ACTIONS)' != ''">False</DeployExtension> <!-- 省略 --> </PropertyGroup>
これはオススメしません。失敗するはずです。このPRも最終的に取り消して↑の手法に切り替わっています。
https://github.com/microsoft/azure-pipelines-image-generation/pull/828github.com
パッケージをアップロードして GitHub Webから取得できるようにする
GitHub Releaseに置くことも考えそうですが、Visual Studio拡張はMarket Placeからの配布が基本なので今回はReleaseページには載せないことにします。 となると、ビルドしたAction毎にビルド成果物である .vsixファイルがWeb上から取得できればokです。
こういった時に使えるのが、Artifactsです。
ビルドでvsixが生成されるパスは、csprojのあるパスからみてbin\Release\OpenUserSecrets.vsix
とわかっているので、指定すればokです。
upload-artifactアクションは、環境変数GITHUB_WORKSPACE
のパスで実行されるので、Artifactsのpath
はリポジトリルートからみた指定でokです、親切!
期待通りアップロードされています。
いい感じで取得できることがわかります。
おまけ
今回のOpenUserSecretsのアップデート (1.1.0) は、この記事にあったNuGetのパッケージをインストールしてくださいメッセージへの対処やVS2019対応です。
REF
基本はこれ。パス解決の考慮がないのでCI的には取り回しが悪いので注意。