.NET SDK 8.0.401 環境で dotnet workload install maui --version 8.0.100 を実行すると、microsoft.net.workloads.8.0.100 が見つからないというエラーで止まることがあります。CI で環境を固定したいときに混乱しやすい「workload の version」の正体と、再現性の高い固定方法を解説します。
起きていることを一言で言うと
--version で指定しているのは「.NET MAUI のバージョン」ではなく、.NET SDK が参照するワークロードセット(workload set)のバージョンです。そのため 8.0.100 のような“SDK のフィーチャーバンドっぽい番号”を入れても、該当するワークロードセットパッケージが NuGet に存在しなければインストールできません。
そして重要なのは、ビルドエージェントで「再現性」を作りたい場合、固定すべき対象は大きく2種類に分かれる点です。
| 固定したいもの | 例 | おすすめの固定方法 | なぜそれが効くのか |
|---|---|---|---|
| ビルドに使う .NET SDK(CLI/SDK の挙動) | 8.0.401 | global.json で SDK バージョンを固定 | 同じリポジトリで常に同じ SDK を選べる |
| ワークロードの組み合わせ(Android/iOS/MAUI などのツールチェーン) | workload set 8.0.401 | workloadVersion(workload set)で固定 | ツールチェーン更新による CI 事故を抑えられる |
| アプリが参照する MAUI の API/実装(ライブラリの挙動) | Microsoft.Maui.* 8.0.3 | <MauiVersion> や PackageReference で固定 | プロジェクト側で依存関係を宣言でき、ロールバックより安全 |
混同しやすい「バージョン」を整理する
今回のようなトラブルは、バージョン番号が似ていても指しているものが違うことから始まります。最低限、次の区別ができると解決が早くなります。
| 名称 | どこで見る/指定する | 例 | 役割 | 今回の質問との関係 |
|---|---|---|---|---|
| .NET SDK バージョン | dotnet --info / global.json | 8.0.401 | CLI と SDK の本体 | ビルドエージェントでまず固定したい |
| SDK のフィーチャーバンド | SDK バージョンの先頭3桁相当 | 8.0.400(※8.0.401 の属する帯) | ワークロードの“世界”が変わる単位 | 8.0.100 を指定しても、今の SDK の帯と合わないと破綻しやすい |
| workload set バージョン | dotnet workload --version / global.json の workloadVersion | 8.0.401 | ワークロード一式をまとめて“この組み合わせ”に固定する仕組み | --version が指すのは基本これ |
| MAUI NuGet のバージョン | <MauiVersion> や PackageReference | 8.0.3 | アプリが参照する MAUI ライブラリ | 「MAUI を厳密に揃える」目的ならここを固定するのが現実解 |
なぜ microsoft.net.workloads.8.0.100 が見つからないのか
結論から言うと、dotnet workload install の --version は、8.0.400 以降で導入された「workload-set update mode(ワークロードセットを基準にインストール/更新するモード)」で使われる指定です。古い形式の “8.0.100 のワークロード” を直接指すためのオプションではありません。
workload set は NuGet に公開されていますが、公開されるパッケージ ID にはルールがあります。Microsoft Learn のドキュメントでは、workload set は Microsoft.NET.Workloads.<feature band> というパッケージ ID(例:Microsoft.NET.Workloads.8.0.400)で配布され、安定版 SDK には対応する workload set バージョンがある、と説明されています。つまり、8.0.401 SDK なら 8.0.401 の workload set を選ぶのが基本です。
実際に NuGet Gallery でも Microsoft.NET.Workloads.8.0.400 パッケージが公開されており、「.NET SDK Workload Versions manifest」を含む(CLI で使うもので、直接参照するためのものではない)と記載されています。一方で Microsoft.NET.Workloads.8.0.100 のような ID は、この仕組みの前提では出てきません。
そのため、--version 8.0.100 を付けると CLI が「8.0.100 の workload set を取りに行く」挙動になり、結果として存在しないパッケージを探して失敗します。ここが今回のエラーの正体です。
ビルドエージェントで環境を固定する現実的な手順
SDK を固定する:まずは global.json
CI で再現性を出す第一歩は、リポジトリに global.json を置いて .NET SDK のバージョンを固定することです。Microsoft Learn の global.json 解説でも、CI シナリオでは使用する SDK を指定するのが一般的、と説明されています。
例(“このリポジトリでは 8.0.401 を使う” を明示):
{
"sdk": {
"version": "8.0.401",
"rollForward": "disable"
}
}
ポイントは rollForward を使って「勝手に別バージョンへロールフォワードしない」方向に寄せることです。ホステッドエージェントの更新や開発者マシン差分で、想定外の SDK が選ばれる事故を減らせます。
ツールチェーンを固定する:workload set(workloadVersion)を使う
.NET 8.0.400 以降では、workload set を指定してワークロード群をまとめて固定できるようになっています。dotnet workload install / restore は、workload-set モードのとき、最新の workload set または global.json / --version で指定した workload set バージョンからインストールします。
リポジトリで固定したい場合は、global.json に workloadVersion を追加します(この値があると、workload コマンドは workload-set モードとして動作します)。
{
"sdk": {
"version": "8.0.401",
"rollForward": "disable",
"workloadVersion": "8.0.401"
}
}
そして CI では、プロジェクト(またはソリューション)に対して dotnet workload restore を実行するのが安全です。restore は、プロジェクトが必要とするワークロードを解析して不足分をインストールするコマンドです。
# 例:ソリューションに必要なワークロードを揃える
dotnet workload restore ./YourApp.sln
# 例:明示的に MAUI を入れる(workloadVersion を global.json で固定している前提)
dotnet workload install maui
なお、global.json に workloadVersion を書いた状態だと、同じディレクトリ配下では --version で別の workloadVersion を指定できない(使いたいなら global.json の影響がない場所で実行する必要がある)という注意点があります。チームで運用する場合は「global.json で固定する」か「CI のコマンドで指定する」か、どちらか一方に寄せると混乱が減ります。
workload-set モードが有効か確認する
「本当に固定できているか?」を確認するために、CI のログに次を残しておくのがおすすめです。
| 確認したいこと | コマンド | 見るポイント |
|---|---|---|
| workload の更新モード | dotnet workload config --update-mode | workload-set になっているか |
| 現在の workload set バージョン | dotnet workload --version | 8.0.401 のような値が出ているか |
| インストール済みワークロード | dotnet workload list | maui / maui-android などが入っているか |
MAUI を厳密に揃えるなら「ワークロード」より「NuGet」を固定する
“MAUI の挙動を揃える” という意味でのバージョン固定は、ワークロードを巻き戻すよりも、プロジェクト側の NuGet パッケージ(Microsoft.Maui.*)を固定するほうが現実的です。これは思想の話ではなく、.NET 8 以降の MAUI の配布設計が「ワークロード + 複数 NuGet パッケージ」になっており、プロジェクトを特定のバージョンへピン止めしやすいことが明確にメリットとして説明されています。
$(MauiVersion) と <MauiVersion> を使う
.NET MAUI のテンプレートでは、Microsoft.Maui.Controls などの参照が Version="$(MauiVersion)" になっていることがよくあります。この $(MauiVersion) は、インストールされている MAUI のバージョンから参照されますが、プロジェクトファイルに <MauiVersion> を書くことで上書きできる、と公式ドキュメントに明記されています。
例(MAUI を 8.0.3 に固定):
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks>
<UseMaui>True</UseMaui>
<!-- ここで MAUI を固定できる -->
<MauiVersion>8.0.3</MauiVersion>
この方式の利点は、開発者のマシンやビルドエージェントで「インストール済みの MAUI が何であれ、プロジェクトが必要なバージョンはこれ」と宣言できる点です。ワークロードをダウングレードするより“プロジェクト主導”で差分を吸収できます。
Central Package Management(CPM)を使うチームは特に要注意
CPM を使っていると、<UseMaui>true</UseMaui> が自動的に必要な MAUI パッケージ参照を追加しなくなる挙動変更があります。結果として「ワークロードは入っているのにパッケージ参照が足りずビルドが壊れる」パターンが起きやすくなるので、Directory.Packages.props 等で MAUI のバージョンを統一する運用が向きます。
それでも “古いワークロードに戻したい/固定したい” 場合:rollback JSON を使う
「特定のワークロード構成でしかビルドが通らない」「いったん既知の構成に戻したい」といった場合、rollback JSON(ロールバック定義)を使ってワークロードを特定の状態に揃える手段が知られています。MAUI チームの GitHub issue でも、CI で rollback .json を公開し、それを dotnet workload install maui --from-rollback-json や --from-rollback-file で使用できる、という説明と具体例が記載されています。
既知の良い環境から rollback JSON を作る
手元や別エージェントで「この組み合わせなら安定する」という状態を作れたら、そこで rollback 定義を出力します。実際の出力例は MAUI リポジトリの issue にも載っており、dotnet workload update --print-rollback が JSON を出力する形式になっています。
# 既知の良い環境で実行(JSON が標準出力に出る)
dotnet workload update --print-rollback
出力された JSON をファイルとして保存します(例:workload-rollback.json)。チーム運用なら、外部リンク切れの影響を受けないよう、社内のアーティファクト置き場(例:社内 NuGet/Blob/Artifacts)に置くのが安全です。
ビルドエージェントで rollback JSON からワークロードを揃える
保存した rollback 定義を使って、ワークロードを指定状態へ寄せます。以下は考え方の例です(URL でもファイルでも指定できるケースがある)。
# rollback ファイルを使って MAUI workload を揃える例
dotnet workload install maui --from-rollback-file ./workload-rollback.json
# 既存の公開 rollback を参照する例(環境により利用可否が変わる)
dotnet workload install maui --from-rollback-json https://aka.ms/dotnet/maui/main.json
ただし rollback 方式は、NuGet 上のパッケージやリンク先の公開状態に依存しやすく、長期の固定には向きません(実際に「rollback JSON のリンクが参照できなくなって失敗する」という報告もあります)。恒久的な再現性が目的なら、まずは workload set と NuGet 固定を優先し、それでも足りない場合の最後の手段として扱うのが無難です。
結局どうするのがベストか:判断フローチャート的まとめ
| 困っていること | まずやること | 次にやること | 最終手段 |
|---|---|---|---|
| ビルドエージェントで突然 MAUI ビルドが落ちる | global.json で SDK を固定 | workloadVersion を固定して dotnet workload restore | rollback JSON で状態固定 |
| チームで MAUI の挙動差(API/実装)が出る | <MauiVersion> を csproj に入れて固定 | CPM なら MAUI パッケージ参照を明示し統一 | (必要なら)社内フィードで nightly/特定ビルドを配布 |
--version 8.0.100 が通らない | --version の意味を確認(workload set) | 8.0.401 なら 8.0.401 の workload set を使う | どうしても 8.0.100 相当が必要なら SDK 自体を 8.0.1xx に寄せる検討 |
補足:ログに残すとデバッグが速くなるコマンド集
CI のトラブルシュートでは「どの SDK・どの workload set・どの MAUI NuGet でビルドしているか」が分からないと議論が空転します。最低限、次の出力をログに残しておくと、原因切り分けが一気に楽になります。
# SDK 情報
dotnet --info
# ワークロードのモードとバージョン
dotnet workload config --update-mode
dotnet workload --version
dotnet workload list
# プロジェクトが要求する MAUI パッケージ(NuGet restore の結果)を見たい場合
dotnet restore -v minimal
ここまで整理しておけば、「SDK は 8.0.401 のまま、MAUI の挙動(NuGet)だけ 8.0.3 に固定する」「ツールチェーン(workload set)を 8.0.401 に固定する」といった狙いどおりの固定がしやすくなります。

コメント