fix: fall back to portable RID for bundled CLI lookup on Linux#482
fix: fall back to portable RID for bundled CLI lookup on Linux#482SteveSandersonMS merged 1 commit intomainfrom
Conversation
✅ Cross-SDK Consistency ReviewThis PR maintains cross-SDK consistency. The changes address a .NET-specific runtime identifier (RID) issue that does not affect the other SDK implementations. SummaryThe PR fixes a bug where the .NET SDK fails to locate the bundled CLI on Linux distributions that return distro-specific RIDs (e.g., Why Other SDKs Are Not Affected
Only the .NET SDK uses .NET's RID-based runtime asset resolution system ( This automated review checks for feature parity and API consistency across all four SDK implementations (Node.js, Python, Go, and .NET).
|
There was a problem hiding this comment.
Pull request overview
This PR fixes bundled Copilot CLI discovery on Linux (and other OSes with non-portable RuntimeIdentifier values) by ensuring the .NET SDK prefers portable RIDs (e.g. linux-x64) when locating/copying the bundled CLI.
Changes:
- Update
CopilotClientbundled CLI lookup to try the runtime-reported RID first, then fall back to a computed portable RID. - Update MSBuild targets to derive
_CopilotRidfrom OS/architecture detection rather than$(RuntimeIdentifier), aiming for consistentruntimes/<portableRid>/nativeoutput paths.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
dotnet/src/build/GitHub.Copilot.SDK.targets |
Changes RID resolution for download/copy output paths to always prefer portable RIDs. |
dotnet/src/Client.cs |
Adds portable-RID fallback when resolving the bundled CLI path at runtime. |
Comments suppressed due to low confidence (1)
dotnet/src/Client.cs:1037
- The new RID-fallback logic is behaviorally important (it changes how the client locates/launches the bundled CLI) but isn’t covered by tests. Since
ClientTestsalready focus on CLI startup behavior, it would be valuable to add a unit/integration test that exercises the fallback (e.g., by factoring RID resolution / base directory lookup behind an internal seam so tests can simulate a distro-specific RID and verify it falls back tolinux-x64/win-x64).
// Fall back to portable RID (e.g., linux-x64) when distro-specific RID
// (e.g., ubuntu.24.04-x64) doesn't match the bundled path.
var portableRid = GetPortableRid();
if (portableRid != null && portableRid != rid)
{
var portablePath = Path.Combine(AppContext.BaseDirectory, "runtimes", portableRid, "native", binaryName);
if (File.Exists(portablePath))
{
return portablePath;
}
}
return null;
}
private static string? GetPortableRid()
{
string os;
if (OperatingSystem.IsWindows()) os = "win";
else if (OperatingSystem.IsLinux()) os = "linux";
else if (OperatingSystem.IsMacOS()) os = "osx";
else return null;
var arch = System.Runtime.InteropServices.RuntimeInformation.OSArchitecture switch
{
System.Runtime.InteropServices.Architecture.X64 => "x64",
System.Runtime.InteropServices.Architecture.Arm64 => "arm64",
_ => null,
};
return arch != null ? $"{os}-{arch}" : null;
| <PropertyGroup> | ||
| <_CopilotRid Condition="'$(RuntimeIdentifier)' != ''">$(RuntimeIdentifier)</_CopilotRid> | ||
| <_CopilotRid Condition="'$(_CopilotRid)' == '' And $([MSBuild]::IsOSPlatform('Windows')) And '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'X64'">win-x64</_CopilotRid> | ||
| <_CopilotRid Condition="$([MSBuild]::IsOSPlatform('Windows')) And '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'X64'">win-x64</_CopilotRid> |
There was a problem hiding this comment.
Removing the $(RuntimeIdentifier) override means consuming projects that explicitly build/publish for a different RID (e.g. dotnet publish -r win-x64 on Linux, or CI cross-target builds) will still download/copy the CLI for the build host OS/arch, and place it under runtimes/<hostRid>/native. This produces incorrect outputs for cross-RID builds. Consider honoring $(RuntimeIdentifier) when it is one of the supported portable RIDs (win-x64/win-arm64/linux-x64/linux-arm64/osx-x64/osx-arm64), and only falling back to OS/arch detection when $(RuntimeIdentifier) is empty or distro-specific (e.g. contains a dot like ubuntu.24.04-x64).
| <_CopilotRid Condition="$([MSBuild]::IsOSPlatform('Windows')) And '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'X64'">win-x64</_CopilotRid> | |
| <!-- Prefer consuming project's portable RuntimeIdentifier when available --> | |
| <_CopilotRid Condition="'$(RuntimeIdentifier)' == 'win-x64'">win-x64</_CopilotRid> | |
| <_CopilotRid Condition="'$(RuntimeIdentifier)' == 'win-arm64'">win-arm64</_CopilotRid> | |
| <_CopilotRid Condition="'$(RuntimeIdentifier)' == 'linux-x64'">linux-x64</_CopilotRid> | |
| <_CopilotRid Condition="'$(RuntimeIdentifier)' == 'linux-arm64'">linux-arm64</_CopilotRid> | |
| <_CopilotRid Condition="'$(RuntimeIdentifier)' == 'osx-x64'">osx-x64</_CopilotRid> | |
| <_CopilotRid Condition="'$(RuntimeIdentifier)' == 'osx-arm64'">osx-arm64</_CopilotRid> | |
| <!-- Fallback to build host OS/arch when RuntimeIdentifier is empty or distro-specific --> | |
| <_CopilotRid Condition="'$(_CopilotRid)' == '' And $([MSBuild]::IsOSPlatform('Windows')) And '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'X64'">win-x64</_CopilotRid> |
There was a problem hiding this comment.
Addressed. We now parse OS from the RID prefix (win*->win, osx*->osx, else->linux) and arch from the suffix (-x64/-arm64), so cross-compile works correctly. Distro-specific RIDs like ubuntu.24.04-x64 normalize to linux-x64. When no RID is set, we use NETCoreSdkPortableRuntimeIdentifier.
dotnet/src/Client.cs
Outdated
| var binaryName = OperatingSystem.IsWindows() ? "copilot.exe" : "copilot"; | ||
| var rid = Path.GetFileName(System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier); | ||
| searchedPath = Path.Combine(AppContext.BaseDirectory, "runtimes", rid, "native", binaryName); | ||
| return File.Exists(searchedPath) ? searchedPath : null; | ||
| if (File.Exists(searchedPath)) | ||
| { | ||
| return searchedPath; | ||
| } | ||
|
|
||
| // Fall back to portable RID (e.g., linux-x64) when distro-specific RID | ||
| // (e.g., ubuntu.24.04-x64) doesn't match the bundled path. | ||
| var portableRid = GetPortableRid(); | ||
| if (portableRid != null && portableRid != rid) | ||
| { | ||
| var portablePath = Path.Combine(AppContext.BaseDirectory, "runtimes", portableRid, "native", binaryName); | ||
| if (File.Exists(portablePath)) | ||
| { | ||
| return portablePath; | ||
| } | ||
| } | ||
|
|
||
| return null; |
There was a problem hiding this comment.
searchedPath is only set to the initial RID-specific path. If the portable RID fallback is also attempted and fails, the thrown error will still report only the first path, which can be misleading when debugging. Consider updating searchedPath to reflect the final attempted path, or include both attempted locations in the message (e.g. RID-specific and portable).
This issue also appears on line 1007 of the same file.
There was a problem hiding this comment.
No longer applicable. The code was simplified to attempt only a single path using the portable RID from GetPortableRid(), so searchedPath always reflects the actual path checked.
3c7afd5 to
8b5b46d
Compare
On Linux distros that install .NET from distribution packages (Ubuntu, Fedora, RHEL, etc.), RuntimeInformation.RuntimeIdentifier returns distro-specific RIDs like ubuntu.24.04-x64 instead of the portable linux-x64. The bundled CLI is placed under runtimes/linux-x64/native/, so the lookup fails and throws. Fix both the runtime lookup and build-time RID resolution: - Client.cs: GetBundledCliPath now falls back to the portable RID (e.g., linux-x64) when the distro-specific RID path doesn't exist. - GitHub.Copilot.SDK.targets: Always use portable RIDs derived from OS/architecture detection instead of the project's RuntimeIdentifier, which may be distro-specific. Fixes #424 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
8b5b46d to
60e6fb6
Compare
Problem
On Linux distros that install .NET from distribution packages (Ubuntu, Fedora, RHEL, etc.),
RuntimeInformation.RuntimeIdentifierreturns distro-specific RIDs likeubuntu.24.04-x64instead of the portablelinux-x64. The bundled CLI is placed underruntimes/linux-x64/native/, so the runtime lookup fails and throws anInvalidOperationException.This is a regression from v0.1.23 where the bundled CLI feature was introduced.
Fix
Runtime (
Client.cs):GetBundledCliPathnow tries the distro-specific RID first, then falls back to the portable RID (e.g.,linux-x64) computed from OS/architecture detection.Build time (
GitHub.Copilot.SDK.targets): Always use portable RIDs derived from OS/architecture detection instead of the project's$(RuntimeIdentifier), which may be distro-specific when .NET is installed via distro packages.Fixes #424