From 93e35a0095f5cca675f67bfd93cae76f9ec80ebd Mon Sep 17 00:00:00 2001 From: Jaap-Jan de Wit | DodoTech Date: Wed, 12 Aug 2026 11:44:56 +0200 Subject: [PATCH] Stop the SDK's own trimmer version deciding whether CI can restore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI went red across the whole repository — main's run 125 and every open pull request at once — on a restore that never reached a compiler: error NU1004: The package reference Microsoft.NET.ILLink.Tasks version has changed from [10.0.10, ) to [10.0.11, ). The packages lock file is inconsistent with the project dependencies so restore can't be run in locked mode. Nothing in any of those commits touched a package. .NET had shipped SDK 10.0.400. ◆ THE VERSION IN THE LOCK FILES WAS NEVER THIS REPOSITORY'S TO DECIDE. Microsoft.NET.ILLink.Tasks is referenced by nothing here. The SDK adds it to any project setting IsTrimmable or IsAotCompatible — DodoSSH.Contracts and DodoSSH.Crypto do, and the Android head gets it from trimming being on by default — and it supplies the version itself, from the KnownILLinkPack item in its own Microsoft.NETCoreSdk.BundledVersions.props. 10.0.302 says 10.0.10; 10.0.400 says 10.0.11. packages.lock.json records that as a Direct reference with a requested range, so what the committed file actually means is "whichever SDK last ran a restore". global.json says rollForward: latestMinor, so setup-dotnet installs the newest 10.x SDK that exists on the morning it runs. The gate did its job — an unreviewed dependency change is exactly what it is there to stop — but the change it caught was not one anybody could have reviewed, and it will recur on every servicing release. Regenerating the lock files alone would have been the worse repair, and not only because it holds until the next release. It cannot be done from this machine at all: every SDK installed here tops out at 10.0.302, which writes 10.0.10 straight back and re-breaks CI. The recorded version would flip according to who restored last — the precise state locking exists to prevent. So the version is pinned in Directory.Build.targets and the three lock files are regenerated against the pin. It is an Update on the SDK's item rather than a PackageVersion in Directory.Packages.props because the reference is implicit: the SDK supplies a version, so central package management is never consulted. It sits in a target because the conditioning is on %(TargetFramework) — all the KnownILLinkPack items share one identity and only that metadata separates net10.0's from net8.0's — and item batching in a condition is legal inside a target and MSB4191 during evaluation. Pinned forward to 10.0.11 rather than back to 10.0.10, which would have been a one-line change with no lock file churn. Holding the trimmer a release behind the framework it analyses to dodge an error is how a missed trim warning happens, and taking the newer one makes the bump a reviewed diff, which is what the gate was asking for. Verified against the SDK that broke it rather than only the one here: - sdk:10.0-alpine, 10.0.400, `dotnet restore DodoSSH.slnx --locked-mode` — exit 0. That is ci.yml's line, on CI's SDK. - the android workload on sdk:10.0-noble, 10.0.400, locked-mode restore of DodoSSH.Client.Android — exit 0. That is scripts/ci-android.sh's line. - locally on 10.0.302, the same locked-mode restore of the solution — exit 0. One set of lock files satisfying both SDKs is the whole point of the pin, and the third check is the one that demonstrates it. Release build clean: 0 errors, and 0 IL-prefixed diagnostics from the newer analyser on the two trimmable projects. 1,869 tests over 19 suites, none failing. A caution for the next person, learned the hard way here: `--force-evaluate` on Windows rewrites every lock file it touches with CRLF, and 23 of the 26 had no content change at all. Only the three that really moved are in this commit. --- Directory.Build.targets | 68 +++++++++++++++++++ src/DodoSSH.Client.Android/packages.lock.json | 6 +- src/DodoSSH.Contracts/packages.lock.json | 6 +- src/DodoSSH.Crypto/packages.lock.json | 6 +- 4 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 Directory.Build.targets diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 0000000..af5dc2a --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,68 @@ + + + + + + + + + + + diff --git a/src/DodoSSH.Client.Android/packages.lock.json b/src/DodoSSH.Client.Android/packages.lock.json index aeb0ac1..c9baa32 100644 --- a/src/DodoSSH.Client.Android/packages.lock.json +++ b/src/DodoSSH.Client.Android/packages.lock.json @@ -74,9 +74,9 @@ }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.10, )", - "resolved": "10.0.10", - "contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg==" + "requested": "[10.0.11, )", + "resolved": "10.0.11", + "contentHash": "IBf7lbovvjGWVWXZX5cJ/cO0WXbId0Zq4BuSeT94mGZuOAP66oMeH9PTBZ9Jpp3Jb6jtK0qm/NyUbPRo1gC/wQ==" }, "MinVer": { "type": "Direct", diff --git a/src/DodoSSH.Contracts/packages.lock.json b/src/DodoSSH.Contracts/packages.lock.json index 25e824a..6c374b1 100644 --- a/src/DodoSSH.Contracts/packages.lock.json +++ b/src/DodoSSH.Contracts/packages.lock.json @@ -22,9 +22,9 @@ }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.10, )", - "resolved": "10.0.10", - "contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg==" + "requested": "[10.0.11, )", + "resolved": "10.0.11", + "contentHash": "IBf7lbovvjGWVWXZX5cJ/cO0WXbId0Zq4BuSeT94mGZuOAP66oMeH9PTBZ9Jpp3Jb6jtK0qm/NyUbPRo1gC/wQ==" }, "MinVer": { "type": "Direct", diff --git a/src/DodoSSH.Crypto/packages.lock.json b/src/DodoSSH.Crypto/packages.lock.json index c92c2df..0d4bae0 100644 --- a/src/DodoSSH.Crypto/packages.lock.json +++ b/src/DodoSSH.Crypto/packages.lock.json @@ -16,9 +16,9 @@ }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.10, )", - "resolved": "10.0.10", - "contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg==" + "requested": "[10.0.11, )", + "resolved": "10.0.11", + "contentHash": "IBf7lbovvjGWVWXZX5cJ/cO0WXbId0Zq4BuSeT94mGZuOAP66oMeH9PTBZ9Jpp3Jb6jtK0qm/NyUbPRo1gC/wQ==" }, "MinVer": { "type": "Direct", -- 2.54.0