Repository navigation
fix(deps): EF Core 系パッケージの版ズレを防ぐ束ねと検出網を追加する - #163
Merged
Merged
Conversation
Dependabot の EF Core メジャー更新 PR (#159-#162) は、プロバイダごとに 1 本ずつ作られていた。どれか 1 本をマージすると Microsoft.EntityFrameworkCore.Relational だけが推移的に 9.x へ上がり、 Npgsql.EntityFrameworkCore.PostgreSQL が 8.x のまま EF Core 9 の内部 API に 対して動く状態になる。 影響: ビルドもテストも通る (テストは InMemory / SQLite しか触らず PostgreSQL 経路を 実行しない) ため CI はグリーンのままで、PostgreSQL 配備でだけ実行時に壊れる。 このアプリは DB プロバイダ非依存が売りで PostgreSQL は正規の配備先 (CLAUDE.md §1 の配備マトリクス) なので、無言で壊れる経路として看過できない。 対応: - .github/dependabot.yml に nuget-ef-core グループを追加し、EF Core 本体・ 各プロバイダ・Identity ストア・ヘルスチェックを major も含めて 1 本の PR へ 束ねる。どのグループに入るかを定義順に依存させないよう、 nuget-minor-and-patch 側には同じ一覧を exclude-patterns で明示する。 - EfCorePackageAlignmentTests を追加し、両 csproj の EF Core 系パッケージが 同じメジャー版であることを検査する。「EF Core 系とは何か」の定義は dependabot.yml の patterns を唯一の真実の源として読み、書き写しによる 検出網の穴を作らない。設定を迂回した手動編集や束ねが外れた場合の最後の砦。 - CLAUDE.md の「注意点(固有の不変条件)」にこの不変条件を追記する。 検証: - dotnet test: 379 件 (新規 2 件を含む) すべてグリーン - npm run typecheck: エラーなし - 検出網の実効性を負のケースで確認済み (Sqlite だけ 9.0.19 にすると EfCorePackages_ShareTheSameMajorVersion が全パッケージの版を並べて失敗する / patterns と exclude-patterns を食い違わせると差分を示して失敗する) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018zmXE9KPUYz9ksgXSBYiJz
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
/code-review ultra の指摘のうち、実証された穴と偽陽性要因に対応した。 検出対象を csproj から packages.lock.json へ移す: - 壊れる仕組みは「Sqlite を上げると Relational が推移的に上がる」ことだが、 Relational はどの csproj にも直接書かれていないため、csproj だけを見る 実装では破壊の主役が視野に入らなかった。ロックファイルは直接参照と推移 依存の両方の解決済み版を持ち、CI も locked-mode restore で一致を強制して いるため、これを正本にする。 - 併せて (a) 属性順が Version 先頭だと参照を取りこぼす正規表現の穴、 (b) csproj のパス一覧を固定していたためプロジェクト追加で検査対象から 外れる穴、の 2 つも解消する (ロックファイルは再帰探索する)。 検出網の自己無効化を防ぐ: - パターンから 1 行消すだけで「そのパッケージは EF Core 系ではない」ことに でき、版揃えの検査も Dependabot の束ねも同時に無効化できていた。名前に EntityFrameworkCore を含むパッケージがすべてパターンに拾われていることを 逆向きに固定するテストを追加する。 セキュリティ更新の経路を塞ぐ: - グループの applies-to は既定が version-updates で、CVE 対応の更新には 及ばない。修正版がメジャーをまたぐと同じ版ズレが再発するため、 nuget-ef-core-security グループを追加し、両グループの applies-to が 実際に別々の更新種別を受け持っていることもテストで固定する。 読みやすさ・偽陽性の除去: - パターン一覧の比較を集合比較にする (並べ替えただけで CI が赤くなるのは 規約違反ではなく検査が硬すぎる偽陽性)。差分は「どちらにのみ有るか」を示す。 - グループ行の探索をコメント行を除いた完全一致にする (EndsWith だと説明文の 「nuget-ef-core:」を先に拾い、誤った位置から走査していた)。 - YAML のキー名を名前付き定数にし、リポジトリルートの解決を 1 回に集約する。 - グループ本文の切り出し (ReadGroupBlock) と、そこからの一覧/単一値の読み取り を分離し、1 メソッド 1 責務にする。 見送り: FindRepositoryRoot の共通化 (既存 4 テストは「Web プロジェクトを探す」 別ヘルパーで、統合すると無関係な 4 ファイルを同一 PR で触ることになるため。 §6「変更は最小スコープに保つ」を優先し別 PR とする) 検証: - dotnet test: 380 件 (新規 3 件を含む) すべてグリーン / npm run typecheck: エラーなし - 指摘が実証した穴が塞がったことを負のケースで確認: 推移依存の Relational だけ 9.0.19 にする / パターンから Npgsql を 3 箇所とも 消す / セキュリティ側の applies-to を消す → いずれも失敗する。 一覧の並べ替えのみ → 通る (偽陽性が出ない)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018zmXE9KPUYz9ksgXSBYiJz
/code-review ultra が変異テストで「通ってしまう」ことを実証した 2 件に対応した。 (1) プロジェクト単位の抜け落ちを検出できなかった Web のロックファイルを退避しても 3 件とも通っていた。全体で 1 件でも パッケージが見つかれば合格としていたため、プロジェクトが検査対象から 外れても気付けない。しかも RestorePackagesWithLockFile を宣言していない プロジェクトに対して dotnet restore --locked-mode はロックファイルを 生成せず正常終了するため、CI のロック強制も同時にすり抜ける。 EveryProject_HasCommittedLockFile を追加し、全 csproj の隣に packages.lock.json が存在することを固定する。 (2) EFCore.* 命名系を取りこぼしていた 逆向き網羅性の目印が EntityFrameworkCore だけだったため、 EFCore.NamingConventions を major 9 で足しても通っていた。EFCore.* は EF Core のメジャー版に厳密追随する系統で、片方は他方の部分文字列では ないため両方を目印にする必要がある。 併せて以下も修正: - bin / obj の除外判定を絶対パスではなくリポジトリ相対パスに対して行う。 チェックアウト先が /home/user/bin/... のような場所だと全ロックファイルが 除外され、実態と食い違うメッセージで落ちていた。区切り単位で比較するので "obj-store" のような無関係な名前は除外しない。 - dependabot.yml の読み込みとリポジトリ全走査を Lazy で 1 度だけにする (§8「同じ計算・取得を繰り返さない」。RepositoryRoot と同じ扱いに揃える)。 - ロックファイル専用だった探索を FindFiles(searchPattern) に一般化し、 csproj の探索と共有する。 検証: - dotnet test: 381 件 (新規 4 件を含む) すべてグリーン / npm run typecheck: エラーなし - 前回素通りした 2 つの変異が落ちることを確認: Web のロックファイル退避 → EveryProject_HasCommittedLockFile が失敗 EFCore.NamingConventions 9.0.0 追加 → 逆向き網羅性テストが失敗 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018zmXE9KPUYz9ksgXSBYiJz
(1) 束ねを後から狭めるキーを検出できなかった
patterns と applies-to が正しくても、同じグループに update-types や
exclude-patterns を書き足せば束ねる範囲を狭められる (update-types を
minor/patch にすると major が再びプロバイダごとの単独 PR に戻る)。
どちらの変異でも 4 件とも通っていた。EF Core 系グループに書いてよいキーを
applies-to / patterns の 2 つに限定し、「何が書いてあるか」だけでなく
「他に何も書かれていないこと」まで固定する。
(2) csproj のリポジトリ全走査が第三者のファイルまで拾っていた
bin / obj しか除外しておらず、node_modules 等に紛れ込んだ csproj も
対象だった。開発者が直しようのないファイルを指して CI が赤くなる。
検査対象を IncidentInsight.sln に登録されたプロジェクトへ変更する。
CI が restore / build / test する範囲そのものを正本にすることで、
検査範囲と「ロックが強制される範囲」が原理的に一致する。
ロックファイルの探索もソリューション経由に統一し、ディレクトリ全走査
(と bin / obj の除外判定) は不要になったので削除する。
(3) ロックファイルの書式変更が素の例外になっていた
GetProperty("dependencies") が KeyNotFoundException を投げ、どのファイルが
読めなかったかも分からなかった。検出網が丸ごと目を潰される場面こそ
説明が要るため、TryGetProperty + Assert にしてファイル名を添える
(このファイルの他の異常系と扱いを揃える)。
検証:
- dotnet test: 381 件すべてグリーン / npm run typecheck: エラーなし
- 前回素通りした 2 つの変異が落ちることを確認:
update-types 追加 → 「想定外のキーがあります: [update-types]」で失敗
exclude-patterns 追加 → 「想定外のキーがあります: [exclude-patterns]」で失敗
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018zmXE9KPUYz9ksgXSBYiJz
(1) グループがどのエコシステム配下にあるかを見ていなかった グループ名だけを dependabot.yml 全体から探していたため、2 つの EF Core グループが npm など別エコシステム配下へ移動しても見つかってしまう。その 状態では NuGet パッケージに一切効かず (nuget 側に残るのは EF Core 系を exclude-patterns で除外しているグループだけなので、プロバイダごとの単独 major PR に逆戻りする) のに 4 件とも通っていた。探索範囲を package-ecosystem: "nuget" エントリの groups: 配下に限定し、「何が 書いてあるか」だけでなく「どこに書いてあるか」まで固定する。 入れ子の切り出しは TakeNestedBlock に共通化した (エコシステム / groups / グループ本文の 3 箇所が同じ規則で範囲を決めるため)。 (2) CLAUDE.md の説明がテストの実装より狭かった 逆向き網羅性の目印を EntityFrameworkCore だけと書いていたが、実装は EFCore も目印にしている。説明が狭いと、EFCore.NamingConventions 等を 追加してテストが落ちたときに「規約違反」ではなく「テストの誤検出」と 誤解され、patterns ではなくテスト側を緩める方向へ進みやすい。併記する。 見送り: FindRepositoryRoot の共通化は issue #164 として切り出した (同 PR の主題と無関係な 4 ファイルを触ることになるため。§6 変更は最小 スコープに保つ) 検証: - dotnet test: 381 件すべてグリーン / npm run typecheck: エラーなし - 前回素通りした変異が落ちることを確認: EF Core グループを npm 配下へ移動 → 3 件が失敗し、 「package-ecosystem: "nuget" 配下に見つかりません」と原因を示す Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018zmXE9KPUYz9ksgXSBYiJz
/code-review ultra が実証した fail-open の穴 2 件に対応した。 (1) 先に定義された広いグループが EF Core 系を横取りできた Dependabot は「最初に一致したグループ」にだけ依存関係を入れる。 patterns: ["Microsoft.*"] のグループを nuget-ef-core より前に置くと EF Core 本体とプロバイダはそちらへ吸い込まれ、束ねは無効化されて プロバイダごとの単独 major PR に戻るが、5 件とも通っていた。 dependabot.yml のコメントは定義順への依存を危険として挙げていたが、 守られていたのは nuget-minor-and-patch 側だけだった。 EF Core グループより前に、EF Core 系パッケージに一致するグループが 定義されていないことを検査する。 (2) 同名グループの重複定義を検出できなかった YAML の重複キーは後に書いた方が採用される一方、検査は先に書いた方を 読んでいた。コピーして書き換えた 2 つ目が実際の設定になり、検査は 正しい 1 つ目を見て通す、という食い違いが起きうる。 グループ名が 1 回しか書かれていないことを検査する。 併せて以下も修正: - パターン照合に RegexOptions.CultureInvariant を併用する。トルコ語 ロケール等で I/i の対応が変わると一致しなくなり、エラーではなく 「対象から静かに外れる」fail-open になるため(このファイルの他の比較は Ordinal 系で揃えてある)。 - ロックファイルの JSON キーと dependabot.yml のパスを名前付き定数にする (YAML キー側だけが定数化されていて不揃いだった。§6)。 見送り: ADO.NET ドライバ(Npgsql / Microsoft.Data.Sqlite.Core / Microsoft.Data.SqlClient)を目印に含める案。SqlClient は EF Core と独立の 版系列(現行 5.x)で、同一メジャー規則を当てはめると常に落ちる。本テストが 固定する不変条件は「EF Core 本体とプロバイダ実装のメジャー版一致」であり、 ドライバの版整合は別の規則として設計する必要がある。 検証: - dotnet test: 382 件 (新規 5 件を含む) すべてグリーン / npm run typecheck: エラーなし - 前回素通りした 2 つの変異が落ちることを確認: Microsoft.* の広いグループを前に置く → 「より前に…定義されています」で失敗 nuget-ef-core を 2 回定義 → 「2 回書かれています」で失敗 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018zmXE9KPUYz9ksgXSBYiJz
/code-review ultra が実証した fail-open の穴 3 件に対応した。 (1) 横取り検査が nuget-ef-core しか見ておらず、適用対象も無視していた セキュリティ更新側 (nuget-ef-core-security) の前に広いグループを置くと CVE 対応の更新が横取りされるが、5 件とも通っていた。逆に、適用対象の 違うグループ (security-updates) を version-updates グループの前に置くと、 横取りしないのに失敗する偽陽性もあった。 検査を 2 つの EF Core グループそれぞれについて行い、比較対象を 「同じ applies-to」かつ「メジャー更新を受け持つ (update-types 未指定か major を含む)」グループに限定する。これで minor/patch 専用グループを 前に置いても落ちない (この不変条件が守るのはメジャー版の一致のため)。 (2) グループのインデントを深くすると検査が空振りしていた nuget-ef-core を 1 段深くすると別グループの子キーになり実質グループでは なくなるが、名前で引く読み取りは通る。定義順の取得だけが -1 を返し、 Take(-1) が空列を返すため「先行グループ 0 件」で合格していた。 グループ定義の直下に居ることを明示的に確認する。 (3) nuget エントリが 2 つ以上あると 1 つ目しか検査していなかった 2 つ目のエントリに束ねの無いグループを置いても通っていた。読み飛ばさず 「検査を広げてから追加せよ」と落とす (fail-closed)。 併せて以下も修正: - update-types のようなインライン記法 (key: ["a", "b"]) を読めるようにする。 箇条書きだけを想定していたため、その書き方のキーを「値ゼロ件」と誤読して いた (今回 update-types を読む必要が生じて表面化した)。 - EF Core 系パッケージ ID の算出で、パターンの読み出しがパッケージ 1 件ごとに 走っていたのをループ外へ移す (§8)。 検証: - dotnet test: 382 件すべてグリーン / npm run typecheck: エラーなし - 前回素通りした 3 つの変異が落ち、偽陽性の 1 件は通ることを確認: security 用の広いグループを前に置く → 「(security-updates) より前に…」で失敗 nuget-ef-core を 1 段深くする → 「グループ定義の直下に見つかりません」で失敗 nuget エントリを 2 つに増やす → 「2 個あります」で失敗 minor/patch 専用グループを前に置く → 通る (メジャー版の一致は壊れないため) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018zmXE9KPUYz9ksgXSBYiJz
(1) セキュリティ側の検査が版更新側のパターンを見ていた(実バグ) AssertNotShadowed が検査対象グループに関わらず EfCoreGroupName の patterns を固定で読んでいた。2 つのパターンが一致している間は結果が 同じだが、その一致は別のテストの責務であり、依存すると防御が二重に 効かなくなる。実際、セキュリティ側だけが持つパターン(将来 Pomelo 等)を 先行グループが横取りしても検出できなかった。groupName 自身の patterns を 読むよう修正する。 (2) groups: が空のとき素の添字範囲外例外になっていた このファイルの他の異常系はすべて日本語で状況を説明して落とすのに、 「束ねが全て失われた」という最も説明が要る場面だけが不透明な例外に 退行していた。件数を確認して説明付きで落とす。 (3) dependency-type: development のグループを横取り扱いしていた 開発時のみの依存に絞ったグループは、本番依存である EF Core 系を拾わない。 先行していても横取りしないため、正しい設定で CI が赤くなる偽陽性だった。 GroupScopeOf / TakesMajorUpdates と同様に、この絞り込みキーも解釈する。 (4) 箇条書きの値を二重引用符に限定していた 整形ツールが冗長な引用符を外すと(YAML として同義で Dependabot の解釈も 変わらない)「想定外の記述があります」で落ちていた。二重引用符・単一引用符・ 引用符なしのいずれも受け付ける。 見送り: - FindRepositoryRoot の共通化: issue #164 で追跡中(無関係な 4 ファイルを 同一 PR で触らないため)。 - 検査全体を EfCoreIdMarkers 基準に寄せて簡素化する案: patterns の一致検査 (AssertSameSet)を落とすと 3 箇所の一覧が自由に食い違えるようになり、 予防層(Dependabot の束ね)が黙って壊れる経路が復活する。検出層だけでなく 予防層の設定自体を固定することが本 PR の目的のため採らない。 検証: - dotnet test: 382 件すべてグリーン / npm run typecheck: エラーなし - 変異で確認: security 側パターンの横取り → 失敗 / groups: を空 → 説明付きで失敗 - 偽陽性が出ないことも確認: dependency-type: development の先行グループ → 通る / 箇条書きの引用符を外す整形 → 通る Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018zmXE9KPUYz9ksgXSBYiJz
(1) dependency-type: development による除外を撤回する(前提が誤っていた)
前コミットで「開発依存に絞ったグループは本番依存の EF Core 系を拾わない」
として横取り検査から除外したが、前提が成り立たない。
Microsoft.EntityFrameworkCore.Design は csproj で PrivateAssets=all を
指定しており、Dependabot からは開発依存に見える。除外を入れると、開発依存に
絞った先行グループが Design を吸い込む経路を見逃す。Design は Relational を
推移依存に持つため、その単独 major PR をマージすれば版ズレが再発する
(=この PR が防ごうとした無言の破壊が、検出網を緑のまま通り抜ける)。
除外を外し、なぜ保守的に倒すのかをコメントに残す。誤検出はレビューで
気付けるが、見逃しは PostgreSQL 配備でしか現れないため。
(2) 書式ゆれの扱いを箇条書き以外にも揃える
箇条書きの値だけ引用符ゆれを許容し、他は厳格なままだった。
- package-ecosystem の値: `nuget` と "nuget" のどちらも受け付ける
(前者は GitHub 公式例にも現れる記法。厳格なままだと 4 テストが
「エントリが見つかりません」で誤って落ちていた)
- インライン記法の値: [minor, patch] と ["minor", "patch"] の両方を
受け付ける(前者だと「値を読み取れませんでした」で誤って落ちていた)
検証:
- dotnet test: 382 件すべてグリーン / npm run typecheck: エラーなし
- 変異で確認:
dependency-type: development の先行グループ → 落ちる(Design を吸い込むため)
package-ecosystem の引用符を外す → 通る(偽陽性が出ない)
引用符なしインライン update-types の先行グループ → 通る(偽陽性が出ない)
Npgsql だけ 9.0.4 → 落ちる(本来の検出力の回帰確認)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018zmXE9KPUYz9ksgXSBYiJz
【なぜ作り直したか】
本ブランチはここまで 8 回、YAML の書き方ごとに検査を継ぎ足してきた。
根本原因は、インデントを数える自前の走査で設定ファイルを読んでいたこと。
YAML には同じ意味を表す書き方が多数あり、想定外の書き方をされると
(a) 検査が対象を見落として素通りする、(b) 正しい設定なのに CI が赤くなる、
のどちらかが起きる。実際、直前のレビューでも次の 4 つが実証された。
素通り: フロー記法のグループ / 引用符付きキーのグループ /
非 EF グループの重複定義 (YAML は後勝ち、走査は先勝ち)
誤検出: エントリ内のキー順の入れ替え / グループ名の行末コメント
書き方を 1 つずつ潰す限りこの класс は終わらないため、読み方そのものを
変える。YamlDotNet (MIT・.NET の標準的な YAML ライブラリ) をテスト
プロジェクトにのみ追加し、「どう書かれているか」ではなく「何が設定されて
いるか」を見る。自前スキャナ (ReadGroupBlock / TakeNestedBlock /
ReadNuGetGroupsBlock / IndentOf / IsIgnorable 等 約 250 行) は不要になり削除。
同名グループの重複定義は解析時点で弾かれるため、専用の回数検査も削除した。
差し引き 106 行減。検査の内容 (版揃え・逆向き網羅性・ロックファイルの有無・
束ねの設定・横取り) は変えていない。
検証: dotnet test 382 件グリーン / npm run typecheck エラーなし /
dotnet restore --locked-mode 成功 (ロックファイル更新済み)
変異 12 ケースで挙動を確認:
落ちる: 版ズレ(Npgsql 9.0.4) / ブロック記法・フロー記法・引用符付きキーの
先行広域グループ / 非 EF グループの重複定義 / update-types による
束ねの縮小 / EF グループを npm 配下へ移動
通る : エントリ内キー順の入れ替え / グループ名の行末コメント /
引用符なし package-ecosystem / 引用符なしインライン一覧 /
patterns の並べ替え
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018zmXE9KPUYz9ksgXSBYiJz
【指摘された問題】 名前に EntityFrameworkCore を含むという理由だけで 4 つのパッケージを同じ 束ね・同じ版揃えの対象にしていたが、版の動き方が 2 種類混ざっていた。 nuget.org の nuspec で確認した対象フレームワーク: Microsoft.EntityFrameworkCore.Sqlite 9.0.0 → net8.0 Npgsql.EntityFrameworkCore.PostgreSQL 9.0.0 → net8.0 Microsoft.AspNetCore.Identity.EntityFrameworkCore 9.0.0 → net9.0 のみ Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore 9.0.0 → net9.0 のみ このアプリは net8.0 なので、4 つを 1 本の PR で 9.x へ上げると net9.0 専用 パッケージで復元が失敗する。かといって EF Core だけ 9.x にすると、版揃えの 検査がメジャー版の不一致として落ちる——逃げ道が無く、支援されている 「net8.0 のまま EF Core 9 へ上げる」経路が塞がれていた。 【対応】 束ねと版揃えの対象を「EF Core の内部 API に結び付くもの」=本体と プロバイダ実装(Sqlite / SqlServer / Npgsql)に限る。Identity ストアと ヘルスチェックは EF Core の公開 API しか使わず、メジャー版は .NET の リリースに追随するため対象外とし、逆向き網羅性テストでは DotNetReleaseTrainPackages として理由付きで明示除外する (黙って外すと「なぜ対象外なのか」が失われるため)。 併せて、YAML 文書が 1 つも無い(空・コメントのみ)場合に素の添字範囲外例外に なっていたのを、状況を説明して落とすようにした(他の異常系と扱いを揃える)。 検証: - dotnet test: 382 件すべてグリーン / npm run typecheck: エラーなし - 変異で確認: 本体・プロバイダだけ 9.0.0 / Identity・HealthChecks は 8.0.29 → 通る (=EF Core 9 への更新経路が塞がれていないこと) Npgsql だけ 9.0.4 → 落ちる (=プロバイダ契約の検出力は維持) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018zmXE9KPUYz9ksgXSBYiJz
(1) 除外一覧に完全一致を求めていた ここでの不変条件は「EF Core 系が minor / patch の束ねへ紛れ込まないこと」で、 除外一覧に他のパッケージが載っていること自体は問題ない。完全一致を求めると、 無関係なパッケージを 1 行除外しただけで「EF Core 系を追加・削除するときは 両方を同じ内容に保ってください」という的外れなメッセージで CI が赤くなり、 正しい設定を消す方向の「修正」を誘発する。部分集合の検査に変える。 2 つの EF Core グループ同士は「同じ顔ぶれ」が不変条件なので完全一致のまま。 (2) YAML パーサへの移行で取り残されたコメントを削除 NuGetGroups フィールドの上に、直下のメソッド(ReadNuGetGroupNamesInOrder)の 説明が残っていた。同じ説明がそのメソッドにも付いており、読み手が別の メンバーの説明を読むことになる。 検証: - dotnet test: 382 件すべてグリーン / npm run typecheck: エラーなし - 変異で確認: 無関係なパッケージを除外一覧へ追加 → 通る(偽陽性が出ない) 除外一覧から Npgsql を落とす → 落ちる(漏れているものを名指しする) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018zmXE9KPUYz9ksgXSBYiJz
This was referenced Aug 18, 2026
This branch had an error being deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
Dependabot が作成中の EF Core メジャー更新 PR(#159 / #160 / #161 / #162)をレビューしたところ、どれか 1 本を単体でマージすると PostgreSQL 配備でだけ実行時に壊れることが分かりました。
.github/dependabot.ymlの既定は「minor / patch はまとめる、major は個別 PR」です。そのため EF Core のメジャー更新はプロバイダごとにバラバラの PR として現れます。たとえば #161(Microsoft.EntityFrameworkCore.Sqlite→ 9.0.19)のロックファイル差分は次のとおりで、Sqlite 1 本を上げただけでMicrosoft.EntityFrameworkCore.Relationalが 9.0.19 へ推移的に上がります。一方
Npgsql.EntityFrameworkCore.PostgreSQLは 8.0.11 のまま残るため、EF Core 9 の内部 API に対して EF Core 8 向けのプロバイダが動くことになります。なぜ気付けないか: ビルドは通り(NuGet は Relational を最も高い版へ解決するだけ)、テストも通ります(テストは InMemory / SQLite しか触らず PostgreSQL 経路を一度も実行しない)。実際 #161 の CI は
build-and-test/Docker image build & smoke testとも success です。このアプリは DB プロバイダ非依存が売りで PostgreSQL は正規の配備先(CLAUDE.md §1 の配備マトリクス)なので、無言で壊れる経路として看過できないと判断しました。対応
.github/dependabot.yml—nuget-ef-coreグループを追加し、EF Core 本体・各プロバイダ・Identity ストア・ヘルスチェックを major も含めて 1 本の PR に束ねる。揃わない組み合わせがそもそも PR として現れないようにする(予防)。nuget-minor-and-patch側には同じ一覧をexclude-patternsで明示しています。tests/IncidentInsight.Tests/Packaging/EfCorePackageAlignmentTests.cs(新規) — 両 csproj の EF Core 系パッケージが同じメジャー版であることを検査する(検出)。設定を迂回した手動編集や、将来 dependabot 設定から束ねが外れた場合の最後の砦です。patternsを唯一の真実の源として読み取ります。テスト側へ書き写すとパッケージを 1 つ足したときに片方だけ更新されて検出網に穴が空くため(§6 一元管理)。既存のAnalyticsScriptContractTests等と同じくソースを直接走査し、YAML パーサを新規依存として足していません。nuget-ef-core.patternsとnuget-minor-and-patch.exclude-patternsの一致も固定しています(片方だけ足すと、そのパッケージは update-type によって入るグループが変わり、結局メジャー更新だけ単独 PR に戻るため)。CLAUDE.md— 「注意点(固有の不変条件)」にこの不変条件を追記。EF Core 8 → 9 の実アップグレードそのものは本 PR の対象外です(
net8.0継続の可否・マイグレーション再生成の要否を含む別判断が必要なため)。本 PR のマージ後は、その更新が Npgsql を含む 1 本の PR としてまとめて提示されるようになります。既存 Dependabot PR の扱い
#159 / #160 / #161 / #162 は単体でマージすべきではありません。本 PR がマージされると、これらの PR は再ベース時に
EfCorePackages_ShareTheSameMajorVersionで CI が赤くなり、機械的にブロックされます(無言で通り抜けなくなる)。クローズして束ね PR の再作成を待つのが妥当と考えますが、判断は owner にお任せします。検証
dotnet test— 379 件すべてグリーン(既存 377 件 + 新規 2 件)npm run typecheck— エラーなしMicrosoft.EntityFrameworkCore.Sqliteだけ 9.0.19 にするとEfCorePackages_ShareTheSameMajorVersionが失敗し、全 8 パッケージの版を並べたメッセージを出すpatternsとexclude-patternsを食い違わせるとDependabotConfig_KeepsEfCorePackagesOutOfTheMinorAndPatchGroupが差分を示して失敗する🤖 Generated with Claude Code
https://claude.ai/code/session_018zmXE9KPUYz9ksgXSBYiJz
Generated by Claude Code