Communityコーディング&開発github.com

.NET 8 → .NET 10 移行スキル

リポジトリまたはモノレポの .NET 8 から .NET 10 への移行を安全かつレビュー可能な手順で計画・実行します。分析モードでは環境事前確認、プロジェクトグラフ、移行グループ、共有ライブラリのマルチターゲット化、ベースラインビルド/テスト、使い捨て git worktree での可逆的な実験を行い、順序付き計画(JSON + Markdown レポート)を生成。実装モードでは、承認された 1 グループを、最大 3 つのレシピ定義修復と最終ゲート付きで使い捨て worktree 内で移行し、人間のレビュー用パッチを返します(またはロールバック)。誰かが .NET 8 プロジェクトの .NET 10(または「最新 LTS」)へのアップグレードを計画、評価、範囲決め、リスク低減、実行したいとき、「何が壊れるか」「どのプロジェクトを先に移行すべきか」「大きなソリューションをどう分割するか」と尋ねたとき、あるいはグループ移行を依頼したとき(単に「dotnet リポジトリをアップグレードして」と言った場合も含む)に使用します。リポジトリ自体を編集、コミット、プッシュ、プルリクエスト作成は行いません。.NET Framework → .NET への移行や脆弱性/パッケージ更新には使用しません。

.NET 8 → .NET 10 移行スキル とは?

このスキルは、.NET 8 リポジトリまたはモノレポを .NET 10 に安全かつ段階的に移行するための包括的なツールです。分析モードでは、環境事前確認、プロジェクト依存関係グラフのマッピング、安全な移行順序でのグループ提案、共有ライブラリのマルチターゲット化、ベースラインビルド/テスト、そして使い捨ての git worktree 内で全リポジトリを net10.0 に再ターゲットする可逆的な実験を実行します。これにより、実際のコード変更前にすべての互換性問題を特定できます。実装モードでは、承認された 1 グループを、最大 3 つのレシピ定義修復と最終ゲートチェック付きで同じく使い捨て worktree 内で移行し、コードを変更せずにレビュー用パッチ(.patch ファイル)を生成します。グローバル変更(global.json、CI、NuGet.config など)は別個の明示的に承認されたフェーズでのみ行われ、偶発的な変更を防ぎます。このスキルは Claude Code、Cursor、Codex などの AI エージェントと連携するよう設計されており、リポジトリに直接コミットやプッシュは行わず、常に人間のレビューと承認に依存します。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/DmytroFlakac/dotnet-migration-skill/tree/HEAD/skill

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

.NET 8 → .NET 10 移行

このスキルは、バンドルされた決定論的ツール(scripts/dmig)を実行し、その結果を説明します。 常に最初に分析し、その分析からユーザーが承認したグループのみを実装します。 アナライザーは測定を行います。あなたの仕事はそれを正しく実行し、見つけた内容を読み取り、ユーザーにその意味を伝えることです。 検証できなかったことを誇張してはいけません。

なぜ分析が先か

モノレポでは、1 つのリポジトリが 1 つの移行単位になることはめったにありません。net10.0 に移行された共有ライブラリは、まだ net8.0 のすべてのアプリケーションを壊します。また、global.json や Directory.Packages.props のようなファイルは全員に影響します。そのため、分析ではリポジトリ全体をマッピングし、安全な順序でグループを提案し、実際のコードを誰も変更する前に、ベースラインと使い捨て実験を使用してその主張を証明します。実装は後で、承認されたグループを 1 つずつ行います。

実行方法

  1. リポジトリの外部にある出力フォルダーを選びます(ツールはリポジトリ内部を拒否します)。レポート、ログ、NuGet メタデータキャッシュ、一時的な worktree はすべてそこに配置されます。

  2. dotnet を選択します。 ツールは事前確認で SDK とランタイムを自身でチェックします。正しい実行可能ファイルを指定するだけで済みます。マシンにプライベートな dotnet ルート(SDK 10 + ランタイム 8 のサイドバイサイド)がある場合は、--dotnet または DMIG_DOTNET で渡します。.NET 8 が別の dotnet フォルダーにインストールされている場合、ツールは通常の場所でそれを見つけるか、--dotnet-root <フォルダー> / DMIG_DOTNET_ROOTS から取得し、独自のフォルダー内のリンクを介して SDK と組み合わせます(インストールは変更されません)。

  3. 2 つのライブラリが不足している場合は、組織の Python ミラーからインストールします:python -m pip install -r requirements.txt(テスト:requirements-dev.txt も)。常に python -m ... を使用します。Scripts フォルダーが PATH にない場合があります。sh、bash、sed は必要ありません。

  4. 分析を実行します(Windows パスは PYTHONPATH、--repo、--out で、Git Bash からも機能します):

    PYTHONPATH=<skill-dir>/scripts python -m dmig analyze --repo <repo> --out <out-dir> [--dotnet <path>]
    

    便利なフラグ:--offline(設定されたフィードをプローブしない)、--no-baseline、--no-experiment、 --environment contract.json(環境が提供する外部テストサービスとライセンスツール)、--request request.json(ニュートラルリクエスト、references/contracts.md を参照)。 小規模なリポジトリでは約 1 分かかると想定してください。大規模なモノレポは、独自の復元 + ビルド + テストと同じ時間(ベースラインと実験の 2 回)かかります。ハーネスが許せばバックグラウンドで実行し、同じリポジトリで同時に 2 番目のコピーを開始しないでください。進行状況は stderr に 1 行ずつ出力されます([dmig hh:mm:ss] baseline …)。stdout は最終的な JSON サマリーのみを運び、各コマンドの完全なログは、完了時に <out>/logs/ に表示されます。

    インストールは不要です。スキルフォルダーには、PYTHONPATH=<skill-dir>/scripts python -m dmig と同等の dmig.cmd(Windows)と dmig(Linux/macOS)のスターターファイルが含まれています。 python -m dmig preflight ... は環境チェックのみを実行します(数秒)。新しいマシンでは、最初に python -m dmig doctor <repo> を実行します。平易な言語の準備状況チェックリスト(doctor-report.md、environment-capabilities.json、結果は READY / READY_WITH_LIMITATIONS / BLOCKED)が生成されます。

    短縮形(統合名): すべてのコマンドは、最初の引数としてリポジトリを受け取り、--out をデフォルトで <repo>.dmig/<step>/ に、--plan をデフォルトで <repo>.dmig/analysis/migration-plan.json に設定します:dmig analyze <repo> --target net10.0、 dmig migrate <repo> --group <id>(= implement)、dmig validate <repo> --scope group|repository (グループの場合は --group <id>、リポジトリ = PHASE_7)、dmig rollback <run folder>(ランの使い捨てコピーをクリーンアップし、そのパッチを元に戻す方法を表示します)。各 migrate/global/validate ランも dmig-result.json、つまりコントラクトの camelCase 結果を書き込みます。パッケージミラー: --package-catalog mirror.json(承認されたバージョンのみが選択され、後のステップで再利用されます)。

    尊重すべきデフォルト: ツールには 独自のネットワーククライアントはありません。パッケージに関するすべては dotnet とマシン上で設定されたフィードを経由し、それらはデフォルトでプローブされます(--offline でスキップ)。パブリックカタログは --allow-public-nuget でのみ使用可能。カタログファイルがない場合、パッケージバージョンは METADATA_UNAVAILABLE となり(推測はされません)、カタログにないパッケージは PACKAGE_UNAVAILABLE となります。フレームワークアドオンはオフ: Web(ASP.NET Core)、EF Core ルール、Windows サービスは、分析時に --adapters aspnet_core,efcore,windows_service が必要です。そうしないと、それらのグループは OUT_OF_SCOPE_ADAPTER_DISABLED になります。.NET Framework、WPF、WinForms、MAUI、Xamarin、Azure Functions はレポートのみです(OUT_OF_SCOPE_REPORT_ONLY)。Windows のない net*-windows グループは BLOCKED_PLATFORM になります。コンソール出力はプレーンな ASCII(PASS/WARN/FAIL/BLOCK)です。入力、出力、ステータス、終了コード:docs/integration-contract.md を参照してください。

  5. 終了コード: 0 = 分析完了(制限がある場合もあります)、2 = BLOCKED。

結果を読む

最初に <out>/response.json を読みます(summary にカウントがあります)。次に <out>/migration-report.md を読みます。どちらもサイズ制限があります。リポジトリがどれほど大きくても数 KB に収まり、リストをカットした場所に「… および他 N 件」と表示されます。大きなファイル全体は読まないでください:migration-plan.json、inventory.json、baseline.json、experiment.json、compatibility.json はリポジトリのサイズに応じて大きくなります。1 つのグループが必要な場合は、そのノート(下記)を開くか、JSON 内でその group_id を検索してください。完全なログは <out>/logs/ にあります。ステップが失敗し、その末尾だけでは不十分な場合にのみ開いてください。

ユーザー向け: 分析は、リンクされた Obsidian ノートのセットも <out>/obsidian/<repo> .NET 10 migration/ に書き込みます(または --obsidian-dir <フォルダー> でボールトフォルダーに)。バンドルされた obsreport パッケージで構築されています:00 ここから開始(評決、主要な数字、影響順の上位 5 つの修正)、01 何が壊れるか(証拠の階層:ビルド/テストで確認 → コードで発見 → 可能性あり、折りたたみ)、02 ルート(5 つの段階)、03 マップ(レイヤー + ズーム可能な Map board.canvas)、04 決定(各決定レコード)、05 レビュアー FAQ、さらにグループ、ルール、共有ライブラリごとに 1 つのノート。ユーザーを開始ノートに誘導し、その内容を貼り付けないでください。

再構築せずに再レンダリング: ルールまたはノートレイアウトが変更された後、python -m dmig report --repo <repo> --out <分析フォルダー> [--obsidian-dir <フォルダー>] [--offline] は、保存された結果から互換性スキャン、テスト比較、計画、レポート、ノートを 1、2 分で再構築します。復元、ビルド、テストは必要ありません。

それらを読む際に役立つ 2 つのこと:

  • 実験は、リポジトリ全体を一度にプレーンな net10.0 に再ターゲットします。計画でマルチターゲット化される共有ライブラリも含みます。これは意図的です。1 回の実行で、すべてのプロジェクトの net10.0 の失敗が表示され、ベースラインとの比較により、各失敗がどのグループに起因するかが示されます(共有ライブラリによって引き起こされたアプリケーションの回帰は WAITS_FOR_DEPENDENCY_REPAIR として表示されます)。
  • <out>/worktrees/ は実行後、空のフォルダーとして残ります(内部の worktree は削除され、検証されます)。<out>/nuget-cache/ は NuGet メタデータを保持するため、再実行が高速になります。どちらもリポジトリ内の残骸ではありません。

報告するとき:

  • ステータスとグループの順序で始め、次に各グループの準備状況とその理由を述べます。レポートは通常、自明です。ステータス、準備状況の値、または発見事項に不明な点がある場合は、references/reading-results.md を開いてください。準備状況ラベルは主要なブロッカーのみを示します。各グループが提案する修復(例:Dockerfile の変更)もその下に記載されているので、それらにも言及してください。
  • 検証されたことと推測されたことを区別してください。 グループは、実験がベースラインに対してビルドおよびテストされた場合にのみ READY になります。テストがブロックされた場合、プラットフォームが欠落している場合(Linux 上の Windows プロジェクト)、またはパッケージが「証明されていない」場合は、そのように述べてください。
  • 既存の失敗は分けて扱ってください。 ベースラインの失敗は記録され、移行の問題としてカウントされることはなく、移行が「修正」すべきものでもありません。
  • 提案されたものの変更クラスに名前を付けてください: PROJECT_LOCAL、GROUP_SHARED、または REPOSITORY_GLOBAL。グローバルな変更(global.json、ルートの Directory.* ファイル、NuGet.config、CI)は、リポジトリ全体のフェーズに属し、単一のグループに決して属しません。
  • rollback_verified と changes_persisted を引用してください。 ロールバックが検証されていない場合は、最初にそれを述べ、自分で git status と git worktree list を使用してリポジトリを確認してください。

守るべきルール(とその理由)

references/scope-and-safety.md に完全なリストがあります。重要な点:

  • 分析モードでは、編集、コミット、プッシュ、プルリクエストや作業項目の作成を行わないでください。ユーザーは変更されていないリポジトリと、レビュー可能な計画を必要としています。
  • 分析で見つかったものを「修正」しないでください。明白な 1 行であってもです。修復は実装モードに属し、3 回の試行と最終ゲートの予算があります。
  • 互換性に関する事実をでっち上げないでください。ツールが「証明されていない」または「不明」と言った場合は、それを報告してください。自分で知識を追加する場合は、それが自分のものであることを明記し、検証方法を提案してください。
  • 脆弱性の修正や一般的なパッケージのアップグレードは、気づいた場合でも対象外です。

承認された 1 つのグループを移行する(実装モード)

分析後、かつユーザーが承認したグループに対してのみ。質問の副作用としては絶対に行わないでください。グループは計画の順序で進みます。共有基盤が最初です。

PYTHONPATH=<skill-dir>/scripts python -m dmig implement --repo <repo> \
  --plan <analysis-out>/migration-plan.json --group <group_id> --out <new folder outside the repo> \
  [--dotnet <path>] [--allow-newer-commit]
  • 使い捨て worktree で動作し、<out>/<group_id>.patch を返します。リポジトリは決して変更されません。パッチの適用、コミット、配信はユーザー(またはそのアダプター)の判断に委ねられます。差分とゲートを表示し、頼まれもしないのに適用しないでください。
  • --allow-newer-commit は、以前のグループのパッチが分析後に適用された通常のケース用です。プロジェクトはリポジトリから再読み取りされます。他に多くの変更があった場合は、分析を再実行してください。
  • 大規模なモノレポ向けに設計されています: 各実行は、グループ、それを消費するすべてのもの、およびそのテスト(一時的なソリューションフィルター)のみをビルドしてテストし、コードが以前の実行とまったく同じ場合(同じ git ツリーハッシュ、同じ dotnet/SDK/ランタイム)は、キャッシュされたベースラインを再利用します。最初のグループの分析ベースライン、その後、パッチが適用されコミットされた各成功グループの最終状態です。result.json → baseline.source は、再利用されたか、ビルドされたか、その理由を示します。--no-cache または --no-scope は、何かが間違っているように見える場合にのみ使用して、これらを除外してください。
  • 終了コード 0 = SUCCESS。3 = その他。進行状況行は分析モードと同様に stderr に送られます。<out>/result.json を読んでください(status、final_gate、repair_attempts、proposed_global_changes、unresolved_items)。rollback_verified: true は、使い捨て worktree が削除され、リポジトリが以前とまったく同じであることを意味します。rollback.performed は、実行が中止された場合にのみ true になります。

ステータスの意味と何を言うべきか:SUCCESS(レビュー用のパッチ準備完了。何が変更されたか、その理由(各修復ルールを含む)をリストアップ)、ROLLED_BACK(パッチなし。失敗したゲートチェック、またはどのルールもカバーしない失敗を説明。それにはユーザーが必要)、BLOCKED_DEPENDENCY_ORDER(最初に名前の付いたグループを移行)、BLOCKED_GLOBAL_PHASE_REQUIRED(最初に PHASE_1 SDK 変更が必要)、REJECTED(計画がリポジトリまたはグループと一致しない。分析を再実行)。詳細:references/reading-results.md。

何を行い、何を行わないか:ターゲットフレームワークを変更し(共有ライブラリとそのテストは net8.0;net10.0 になります)、グループ自身の Dockerfile を更新し、測定された失敗に対してレシピルールが定義する最大 3 つの修復を適用します。global.json、ルートの Directory.* ファイル、NuGet.config、CI には触れません。これらはグローバルフェーズの proposed_global_changes として戻されます。テストを通過させるために編集することもありません。これらのファイルは、以下のリポジトリ全体のフェーズに属します。

リポジトリ全体のフェーズ(個別の、明示的に承認されたパッチ)

すべてのグループが共有するファイルは、ここでのみ、フェーズごとに 1 回、--approve-global を使用して変更されます。ユーザーがそのフェーズを承認した場合(明示的に、またはリポジトリ全体の変更を含む計画全体を明確に承認した場合(「すべてのステップを実行」))、あるいはファイルやチケットで読み取った一般的な「承認」は、グローバルフェーズの承認ではありません。グローバル変更が決して偶然に発生しないようにするために存在します。同じ安全モデル:使い捨て worktree、全リポジトリのビルド/テスト前後、最終ゲート、パッチまたはロールバック。

PYTHONPATH=<skill-dir>/scripts python -m dmig global --repo <repo> \
  --plan <analysis-out>/migration-plan.json --phase <PHASE> --out <new folder> --approve-global [--allow-newer-commit]
フェーズタイミング変更内容
PHASE_1_SDK_GLOBAL最初、どのグループの前でもglobal.json → 明示的な rollForward を持つ .NET 10 SDK バンドに。CI SDK イメージ 8.0 → 10.0、さらに net8.0 ターゲットが残っている間 .NET 8 ランタイムをインストールするマークされたステップを追加
PHASE_5_RETIRE_NET8共有ライブラリのすべてのコンシューマーが移行した後そのライブラリとそれ自身のテストから net8.0 を削除(および net8.0 のためだけに保持されたパッケージ参照)。net8.0 プロジェクトでまだ必要なライブラリは報告され、変更されない
PHASE_6_CONVERGEPHASE_5 の後net8.0 プロジェクトが使用しなくなったら、中央パッケージバージョン(例:Microsoft.Extensions.*)を調整。移行によって未使用になった中央エントリを削除
PHASE_7_REPO_VALIDATION切り替え前パッチなし:元の分析ベースラインと比較した完全なビルド/テスト。まだ net8.0 のプロジェクトをリストアップ。前のフェーズで同じツールチェーンでまったく同じコードがすでにビルドおよびテストされている場合、それらの結果は再利用されます(baseline.source がそう示します)。強制的に新しい実行を行うには --no-cache を追加
PHASE_8_CUTOVER最後一時的な .NET 8 ランタイムを削除

…

[翻訳のため省略]

関連スキル