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 にトドメを刺しそう (good bye
リポジトリ
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/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的には取り回しが悪いので注意。