Public Access
Stop the SDK's own trimmer version deciding whether CI can restore
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.
This commit is contained in:
@@ -0,0 +1,68 @@
|
|||||||
|
<Project>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
◆ THE TRIMMER'S VERSION IS PINNED HERE BECAUSE OTHERWISE THE LOCK FILES ARE NOT LOCKED.
|
||||||
|
|
||||||
|
Microsoft.NET.ILLink.Tasks is not referenced by anything in this repository. The SDK adds it
|
||||||
|
on its own to any project that sets IsTrimmable or IsAotCompatible — DodoSSH.Contracts and
|
||||||
|
DodoSSH.Crypto do, and the Android head gets it from trimming being on by default there — and
|
||||||
|
the version it asks for is whatever the running SDK happens to bundle. That version lives in
|
||||||
|
the SDK's own Microsoft.NETCoreSdk.BundledVersions.props, as a KnownILLinkPack item.
|
||||||
|
|
||||||
|
Which makes it a dependency whose version is a property of the toolchain rather than of this
|
||||||
|
repository, and that is the whole problem: packages.lock.json records it as a Direct reference
|
||||||
|
with a requested range, so the lock file silently means "whichever SDK last ran a restore".
|
||||||
|
global.json says rollForward: latestMinor, so CI's setup-dotnet installs the newest 10.x SDK
|
||||||
|
that exists on the day it runs. The moment .NET ships a servicing release, CI's SDK asks for a
|
||||||
|
version the committed lock files do not have, and the locked-mode restore in ci.yml fails with
|
||||||
|
NU1004 before a single file is compiled.
|
||||||
|
|
||||||
|
That is not hypothetical. It closed the whole pipeline: main's run 125 and every open pull
|
||||||
|
request went red together, on
|
||||||
|
|
||||||
|
error NU1004: The package reference Microsoft.NET.ILLink.Tasks version has changed
|
||||||
|
from [10.0.10, ) to [10.0.11, ).
|
||||||
|
|
||||||
|
with nothing in any of those commits touching a package. .NET had shipped SDK 10.0.400, which
|
||||||
|
bundles ILLink 10.0.11 where 10.0.302 bundled 10.0.10, and setup-dotnet installed it the next
|
||||||
|
time anything ran.
|
||||||
|
|
||||||
|
Worse than the outage is the shape of the repair without this pin. Regenerating the lock files
|
||||||
|
holds only until the next servicing release, and it cannot be done from a machine whose newest
|
||||||
|
SDK is older than the runner's: a restore on 10.0.302 writes 10.0.10 straight back and re-breaks
|
||||||
|
CI, so the recorded version becomes a fact about whoever ran restore last rather than about this
|
||||||
|
repository. That is exactly the state locking exists to prevent, and it is not a hypothetical
|
||||||
|
either — every SDK installed on the machine this pin was written on tops out at 10.0.302.
|
||||||
|
|
||||||
|
Pinning it makes the recorded version a decision this repository made, reviewable in a diff
|
||||||
|
like every other version in Directory.Packages.props, and identical on every machine whatever
|
||||||
|
SDK it has. Moving it is then a deliberate edit here plus a regenerated lock file, which is the
|
||||||
|
same ceremony any other dependency bump gets.
|
||||||
|
|
||||||
|
It is an Update on the SDK's item rather than a PackageVersion in Directory.Packages.props, and
|
||||||
|
it has to be: the reference is implicit, so the SDK supplies the version itself and central
|
||||||
|
package management never gets asked. ProcessFrameworkReferences reads @(KnownILLinkPack) when
|
||||||
|
it runs, which is why this lives in Directory.Build.targets — the item does not exist yet while
|
||||||
|
Directory.Build.props is being evaluated.
|
||||||
|
|
||||||
|
Keep this within a patch or two of the runtime the SDK ships. It is the trimming analyzer and
|
||||||
|
the ILLink task, so a small skew is harmless, but a version far behind the framework being
|
||||||
|
analysed is a real way to miss a trim warning.
|
||||||
|
-->
|
||||||
|
<Target Name="PinTheILLinkPackVersion" BeforeTargets="ProcessFrameworkReferences">
|
||||||
|
<!--
|
||||||
|
Inside a target, and not for tidiness. The SDK ships one KnownILLinkPack per target framework
|
||||||
|
and they all share the identity "Microsoft.NET.ILLink.Tasks", so the TargetFramework metadata
|
||||||
|
is the only thing telling net10.0's entry from net8.0's. A condition on %(...) is item
|
||||||
|
batching, which MSBuild permits in a target and rejects during evaluation with MSB4191 — so
|
||||||
|
an ItemGroup at the top of this file cannot express "only the net10.0 one" at all, and the
|
||||||
|
unconditioned Update it would have to become rewrites every framework's entry.
|
||||||
|
-->
|
||||||
|
<ItemGroup>
|
||||||
|
<KnownILLinkPack Update="Microsoft.NET.ILLink.Tasks"
|
||||||
|
Condition="'%(TargetFramework)' == 'net10.0'"
|
||||||
|
ILLinkPackVersion="10.0.11" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -74,9 +74,9 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.NET.ILLink.Tasks": {
|
"Microsoft.NET.ILLink.Tasks": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[10.0.10, )",
|
"requested": "[10.0.11, )",
|
||||||
"resolved": "10.0.10",
|
"resolved": "10.0.11",
|
||||||
"contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg=="
|
"contentHash": "IBf7lbovvjGWVWXZX5cJ/cO0WXbId0Zq4BuSeT94mGZuOAP66oMeH9PTBZ9Jpp3Jb6jtK0qm/NyUbPRo1gC/wQ=="
|
||||||
},
|
},
|
||||||
"MinVer": {
|
"MinVer": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
|
|||||||
@@ -22,9 +22,9 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.NET.ILLink.Tasks": {
|
"Microsoft.NET.ILLink.Tasks": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[10.0.10, )",
|
"requested": "[10.0.11, )",
|
||||||
"resolved": "10.0.10",
|
"resolved": "10.0.11",
|
||||||
"contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg=="
|
"contentHash": "IBf7lbovvjGWVWXZX5cJ/cO0WXbId0Zq4BuSeT94mGZuOAP66oMeH9PTBZ9Jpp3Jb6jtK0qm/NyUbPRo1gC/wQ=="
|
||||||
},
|
},
|
||||||
"MinVer": {
|
"MinVer": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.NET.ILLink.Tasks": {
|
"Microsoft.NET.ILLink.Tasks": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[10.0.10, )",
|
"requested": "[10.0.11, )",
|
||||||
"resolved": "10.0.10",
|
"resolved": "10.0.11",
|
||||||
"contentHash": "f5VCIE7AJpd5YvzNTeMGVzQIgyE9tX+AreTYwQF+REbu+DZo/2Ae+jNSwhPEYrVz6RRkd7y8ubXjk6Nn6Ka+Cg=="
|
"contentHash": "IBf7lbovvjGWVWXZX5cJ/cO0WXbId0Zq4BuSeT94mGZuOAP66oMeH9PTBZ9Jpp3Jb6jtK0qm/NyUbPRo1gC/wQ=="
|
||||||
},
|
},
|
||||||
"MinVer": {
|
"MinVer": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
|
|||||||
Reference in New Issue
Block a user