From 782a30613f362dad68ce3fac7711b6573003ca87 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sat, 22 Aug 2026 10:07:08 +1000 Subject: [PATCH] Do not resolve a glibc viewer against musl Rids yields the framework's own RID first, and the comment there explains why that matters: on Alpine it is linux-musl-x64, which the package does not ship, so the probe misses and the caller falls through to the dotnet tool rather than resolving a glibc build against musl. The synthesised RID below it then undid exactly that. linux-{arch} is yielded for any Linux, musl included, and that names the glibc build - so the probe hit after all and handed back an apphost that cannot start. Inline snapshots survive it, because the launch failure is swallowed and staging takes over, but a file snapshot whose resolved tool is the viewer throws out of LaunchProcess. Stop after the framework's own RID when it names musl. Rids takes the identifier as an argument now so the rule can be tested from any machine, which matters for a bug whose only natural home is a container nobody runs the suite in. The same shape is in DiffEngineViewer's NativeResolver.Rids and is not touched here. --- src/DiffEngine.Tests/BundledViewerRidTests.cs | 36 +++++++++++++++++++ .../Viewer/BundledViewerDirectory.cs | 23 ++++++++++-- 2 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 src/DiffEngine.Tests/BundledViewerRidTests.cs diff --git a/src/DiffEngine.Tests/BundledViewerRidTests.cs b/src/DiffEngine.Tests/BundledViewerRidTests.cs new file mode 100644 index 00000000..6b6efd8b --- /dev/null +++ b/src/DiffEngine.Tests/BundledViewerRidTests.cs @@ -0,0 +1,36 @@ +#if NET10_0 +/// +/// Which RIDs the bundled viewer is probed for. +/// +/// The framework's own RID comes first, and on Alpine that is linux-musl-x64, which the package +/// does not ship - so the probe is supposed to miss and the caller falls through to the dotnet +/// tool. The synthesised linux-{arch} that followed it undid that, because it names the glibc +/// build, and resolving one against musl hands back an apphost that cannot start. +/// +/// +public class BundledViewerRidTests +{ + [Test] + [Arguments("linux-musl-x64")] + [Arguments("linux-musl-arm64")] + public async Task MuslProbesItsOwnRidAndNothingElse(string runtimeIdentifier) + { + var rids = BundledViewerDirectory.Rids(runtimeIdentifier).ToList(); + + await Assert.That(rids).IsEquivalentTo([runtimeIdentifier]); + } + + /// + /// Everywhere else the synthesised RID still follows, which is what makes the probe work when + /// the framework reports something the package does not ship under that exact name. + /// + [Test] + public async Task GlibcStillFallsBackToTheSynthesisedRid() + { + var rids = BundledViewerDirectory.Rids("linux-x64").ToList(); + + await Assert.That(rids.Count).IsGreaterThan(1); + await Assert.That(rids[0]).IsEqualTo("linux-x64"); + } +} +#endif diff --git a/src/DiffEngine/Viewer/BundledViewerDirectory.cs b/src/DiffEngine/Viewer/BundledViewerDirectory.cs index 8f108f3e..abda2325 100644 --- a/src/DiffEngine/Viewer/BundledViewerDirectory.cs +++ b/src/DiffEngine/Viewer/BundledViewerDirectory.cs @@ -80,13 +80,30 @@ static class BundledViewerDirectory } #endif - static IEnumerable Rids() - { + static IEnumerable Rids() => #if NET6_0_OR_GREATER + Rids(RuntimeInformation.RuntimeIdentifier); + + internal static IEnumerable Rids(string runtimeIdentifier) + { // The framework's own value first. On Alpine that is linux-musl-x64, a RID we do not // ship, so the probe misses and the caller falls through to the dotnet tool rather than // resolving a glibc build against musl. - yield return RuntimeInformation.RuntimeIdentifier; + yield return runtimeIdentifier; + + // And nothing else, or the synthesised RID below undoes that: linux-{arch} is the glibc + // build, so on musl the probe would hit after all and hand back an apphost that cannot + // start. Falling through to the dotnet tool is the outcome the comment above describes and + // was not what happened + if (runtimeIdentifier.Contains("-musl-", StringComparison.Ordinal)) + { + yield break; + } +#else + InnerRids(); + + static IEnumerable InnerRids() + { #endif var architecture = RuntimeInformation.OSArchitecture switch