Skip to content

Comments

fix: fall back to portable RID for bundled CLI lookup on Linux#482

Merged
SteveSandersonMS merged 1 commit intomainfrom
stevesa/fix-linux-rid-fallback-424
Feb 16, 2026
Merged

fix: fall back to portable RID for bundled CLI lookup on Linux#482
SteveSandersonMS merged 1 commit intomainfrom
stevesa/fix-linux-rid-fallback-424

Conversation

@SteveSandersonMS
Copy link
Contributor

Problem

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 runtime lookup fails and throws an InvalidOperationException.

This is a regression from v0.1.23 where the bundled CLI feature was introduced.

Fix

Runtime (Client.cs): GetBundledCliPath now 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

@SteveSandersonMS SteveSandersonMS requested a review from a team as a code owner February 16, 2026 10:50
Copilot AI review requested due to automatic review settings February 16, 2026 10:50
@github-actions
Copy link

✅ Cross-SDK Consistency Review

This PR maintains cross-SDK consistency. The changes address a .NET-specific runtime identifier (RID) issue that does not affect the other SDK implementations.

Summary

The PR fixes a bug where the .NET SDK fails to locate the bundled CLI on Linux distributions that return distro-specific RIDs (e.g., ubuntu.24.04-x64) instead of portable RIDs (e.g., linux-x64). The fix adds a fallback mechanism to try portable RIDs when the distro-specific path doesn't exist.

Why Other SDKs Are Not Affected

  • Python SDK: Bundles the CLI directly in copilot/bin/ without platform-specific subdirectories. Uses simple sys.platform detection.

  • Go SDK: Installs the embedded CLI to a user cache directory without runtime-specific subdirectories. Platform detection uses runtime.GOOS.

  • Node.js SDK: Relies on npm's package resolution for the @github/copilot package, which handles platform-specific distribution automatically.

Only the .NET SDK uses .NET's RID-based runtime asset resolution system (runtimes/{rid}/native/), so this is an appropriate language-specific fix with no cross-SDK consistency implications.


This automated review checks for feature parity and API consistency across all four SDK implementations (Node.js, Python, Go, and .NET).

AI generated by SDK Consistency Review Agent

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 CopilotClient bundled CLI lookup to try the runtime-reported RID first, then fall back to a computed portable RID.
  • Update MSBuild targets to derive _CopilotRid from OS/architecture detection rather than $(RuntimeIdentifier), aiming for consistent runtimes/<portableRid>/native output 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 ClientTests already 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 to linux-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>
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
<_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>

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 999 to 1019
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;
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@SteveSandersonMS SteveSandersonMS force-pushed the stevesa/fix-linux-rid-fallback-424 branch 7 times, most recently from 3c7afd5 to 8b5b46d Compare February 16, 2026 11:36
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[.NET] Bundled copilot cli not found on some linux distros

1 participant