Skip to content

setup-dotnet fails on air-gapped runners for floating versions #762

Description

@juxtin

Description:

On self-hosted runners without outbound internet access, actions/setup-dotnet fails to set up a .NET SDK when a floating version (e.g. 8.0.x, or a bare channel like 8.0) is requested, even when a satisfying SDK is already installed on the runner. Two separate steps in the install path make outbound requests before any check of what is already present locally:

  1. Unconditional runtime pre-install. DotnetCoreInstaller.installDotnet() always runs the install script with --runtime dotnet --channel LTS first ("to get the latest stable version of the dotnet CLI"). This resolves Runtime/LTS/latest.version from builds.dotnet.microsoft.com (falling back to ci.dot.net) on every run, regardless of the requested version. On an air-gapped runner this fails immediately.

  2. Online latest.version resolution for floating/channel versions. A floating input such as 8.0.x is turned into a channel argument (8.0) rather than a concrete version. The install script then fetches Sdk/8.0/latest.version over the network to resolve the exact patch, before it checks whether a matching SDK is already on disk. On an air-gapped runner this also fails.

Representative failing log (floating versions 8.0.x/9.0.x/10.0.x, no outbound access):

install-dotnet.sh --skip-non-versioned-files --runtime dotnet --channel LTS
dotnet-install: Download attempt #1 has failed: Unable to download https://builds.dotnet.microsoft.com/dotnet/Runtime/LTS/latest.version.
... (retries, then falls back to https://ci.dot.net/public/Runtime/LTS/latest.version) ...
Warning: Failed to install dotnet runtime + cli, exit code: 1. dotnet_install: Error: Failed to resolve the exact version number.

install-dotnet.sh --skip-non-versioned-files --channel 8.0
dotnet-install: Download attempt #1 has failed: Unable to download https://builds.dotnet.microsoft.com/dotnet/Sdk/8.0/latest.version.
... (retries, then falls back to https://ci.dot.net/public/Sdk/8.0/latest.version) ...
Error: Failed to install dotnet, exit code: 1. dotnet_install: Error: Failed to resolve the exact version number.

Clarifying the "it already reuses the cache" behavior

When a fully pinned version (e.g. 8.0.422) is requested, the input becomes a concrete --version argument, no latest.version fetch is needed for the SDK, and the install script reports .NET Core SDK with version 'X' is already installed when it finds it on disk. This is why pinned-version runs succeed and appear to "reuse" an existing install.

However, note two things:

  • This reuse is the install script detecting an SDK already present at the install directorysetup-dotnet does not consult the GitHub Actions tool cache (@actions/tool-cache) in its install path. So "preload the tool cache" (as works for actions/setup-java) does not apply here.
  • Even for a pinned version, the unconditional runtime pre-install (point 1) still makes an outbound latest.version request, which fails on a fully air-gapped runner.

For floating/channel versions, the online resolution (point 2) happens before the on-disk check, so an already-satisfying local SDK is never given the chance to short-circuit the network call.

Justification:

This blocks any workflow that runs setup-dotnet with floating versions on air-gapped self-hosted runners. Notably, GitHub's Automatic Dependency Submission (ADS) for NuGet uses floating versions (8.0.x, 9.0.x, 10.0.x) and therefore always takes the online-resolution path, so it cannot be made to work offline by preinstalling SDKs or preloading the tool cache. This came up via a customer support escalation; the report is intentionally scoped to the general capability and contains no customer- or environment-specific details.

For comparison, actions/setup-java is usable in preloaded/offline scenarios because a satisfying local toolchain short-circuits the download.

Proposed behavior:

  • Before performing online version resolution for a floating/channel request, check whether an already-installed SDK under the install directory (DOTNET_ROOT / DotnetInstallDir) satisfies the requested version; if so, use it and skip the network call.
  • Make the runtime pre-install (point 1) tolerant of a runner with no outbound access — e.g. skip it when a suitable CLI/runtime is already present, or don't hard-require the online latest.version resolution for it.
  • (Secondary) Provide a supported way to point resolution/download at an internal mirror/feed (the install scripts already support an Azure feed override), so environments that maintain a mirror can resolve floating versions offline.

Are you willing to submit a PR?

Yes — happy to collaborate on this if the maintainers agree on the approach.

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature requestNew feature or request to improve the current logic

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions