GitHub Actionsで .NET Coreビルドを行いたいときに利用するものといえば、setup-dotnetがあります。
今回はUbuntu-latestな環境でsetup-dotnetがいる場合と、そうでない場合について考えてみます。
概要
- GitHub Actionsホストランナーのデフォルト環境で .NETはビルドでき、最新が1-2週で入ってくるのでOSSはsetup-dotnetなしで十分
- プロダクトなど .NET SDKバージョンを厳密に制御する場合は、setup-dotnetを使うとよい
- setup-dotnetはglobal.jsonを読んでくれるので、バージョン固定するならglobal.json便利
GitHub Actions と ホストランナー
GitHub Actionsのホストランナーは環境ごとに特定のツールがインストールされた状態になっています。
例えば、ubuntu-latestであるUbuntu 20.04 LTS環境なら、以下のツールが入っています。
https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md
2021年5月13日時点で .NET SDKは次のバージョンが入っており、かなり網羅されていることがわかります。 2.1 / 3.1 / 5.0に関してはおおむね困らないでしょう。
2.1.300 2.1.301 2.1.302 2.1.401 2.1.402 2.1.403 2.1.500 2.1.502 2.1.503 2.1.504 2.1.505 2.1.506 2.1.507 2.1.508 2.1.509 2.1.510 2.1.511 2.1.512 2.1.513 2.1.514 2.1.515 2.1.516 2.1.517 2.1.518 2.1.519 2.1.520 2.1.521 2.1.522 2.1.523 2.1.602 2.1.603 2.1.604 2.1.605 2.1.606 2.1.607 2.1.608 2.1.609 2.1.610 2.1.611 2.1.612 2.1.613 2.1.614 2.1.615 2.1.616 2.1.617 2.1.700 2.1.701 2.1.801 2.1.802 2.1.803 2.1.804 2.1.805 2.1.806 2.1.807 2.1.808 2.1.809 2.1.810 2.1.811 2.1.812 2.1.813 2.1.814 2.1.815 3.1.100 3.1.101 3.1.102 3.1.103 3.1.104 3.1.105 3.1.106 3.1.107 3.1.108 3.1.109 3.1.110 3.1.111 3.1.112 3.1.113 3.1.114 3.1.200 3.1.201 3.1.202 3.1.300 3.1.301 3.1.302 3.1.401 3.1.402 3.1.403 3.1.404 3.1.405 3.1.406 3.1.407 3.1.408 5.0.100 5.0.101 5.0.102 5.0.103 5.0.104 5.0.200 5.0.201 5.0.202
このため、わざわざsetup-dotnetを使わずともdotnet cliは参照できますし、ビルドも可能です。
つまり、setup-dotnetを使わない場合、GitHub Actionsのホストランナーに事前インストールされた複数の .NET SDKから必要なバージョンが参照されます。
setup-dotnet
setup-dotnetがやっていることは、「特定のバージョンの .NET SDKをazure feedからダウンロード、展開して参照できるようにする」ということです。
GitHubホストランナーはPrereleaseの .NET SDKには未対応で、ホストランナーの環境更新も1週間に一度程度なので待ってられないというケースが稀にあります。 そんなときsetup-dotnetはPrereleaseにも対応しており、まだfeedに乗っていないリリース直後の .NET SDKバージョンを利用するときに便利です。

また、setup-dotnetはバージョン省略時にglobal.jsonがあればそのバージョンを読む機能もあります。global.jsonとCIのバージョン固定ならglobal.jsonでいいというのはあるので、これはこれで便利です。(global.jsonの配置場所がリポジトリルートではない場合は少しアレってなりますが)
if (!version) {
// Try to fall back to global.json
core.debug('No version found, trying to find version from global.json');
const globalJsonPath = path.join(process.cwd(), 'global.json');
if (fs.existsSync(globalJsonPath)) {
const globalJson = JSON.parse(
// .trim() is necessary to strip BOM https://github.com/nodejs/node/issues/20649
fs.readFileSync(globalJsonPath, {encoding: 'utf8'}).trim()
);
if (globalJson.sdk && globalJson.sdk.version) {
version = globalJson.sdk.version;
}
}
setup-dotnetの副作用
さて、この記事をここまで読んでいる人は「setup-dotnetを使うとインストールした以外のSDKが使えなくなる」という状況になったことがあるのではないでしょうか? ホストランナーの環境には、複数の .NET SDKが入っているにも関わらず、です。
これはsetup-dotnetが、SDKのインストール時に .NET SDKの参照ルートパスをホストランナーのデフォルトパス/usr/bin/dotnetからsetup-dotnetで入れたSDKのパス$HOME/.dotnetに切り替えているために起こります。
結果、 setup-dotnetでインストールした以外のSDKバージョンは参照できなくなります。
.NET SDKのルートパスは、環境変数DOTNET_ROOTに任意のパスを設定することでサクッと切り替えることができます。というのが気付きでした。便利。
setup-dotnetはinstaller.tsでこれを設定しています。
// 184 行目
core.exportVariable(
'DOTNET_ROOT',
path.join(process.env['HOME'] + '', '.dotnet')
);
実際にGitHub Actionsで .NET SDKの参照パスと利用可能バージョン一覧を見てみましょう。
setup-dotnetを利用する前は、標準の/usr/bin/dotnetが利用されています。

setup-dotnetを利用すると、パスが/home/runner/.dotnet/dotnetになり、利用可能なSDKも限定されていることがわかります。

まとめ
一見すると、setup-dotnetを使うとめんどくささが増えてメリットがスポイルされているように見えます。
しかし、プロダクトなど厳密に .NET SDKのバージョンを管理したい場合は、setup-dotnetを使うのは合理的でしょう。
一方で、基本的にホストランナーには最新バージョンの.NET SDKが入っているので、OSSでは脆弱性情報などの対応を除きホストランナーのデフォルトで十分というのは言えそうです。
- setup-dotnetを使うと、.NET SDK previewや任意の.NET SDKをインストールができる
- setup-dotnetを使うと、デフォルトの .NET SDKのパスから、setup-dotnetでインストールした .NET SDKのパスに変更されるため、ホストランナーのデフォルト .NET SDK達はパス省略で使えなくなる
今日の学びでした。