Troubleshooting GitHub Package NuGet feeds with GitHub Actions

Make sure you configure the actions/setup-dotnet action correctly to set up the right .NET SDK and authenticate to a GitHub Packages NuGet feed.

.NET

GitHub Actions

If you’re building .NET applications with GitHub Actions, then you want to ensure the correct .NET SDK is in place. If you’re using GitHub Packages to access a private NuGet feed then you’ll also need to be able to authenticate to that feed. The actions/setup-dotnet action is great for doing both of these tasks, but there are some gotchas I’ve encountered when using it.

Using the action is pretty straight forward:

- uses: actions/setup-dotnet@v6

If you have a global.json file present in the root of your repo it will read it by default. If you don’t then it doesn’t know which version to install and you’ll see this logged in the workflow run:

The global.json wasn't found in the root directory. No .NET version will be installed.

Better to use dotnet-version to indicate one (or more) SDK version ranges to install:

- uses: actions/setup-dotnet@v6
  with:
    dotnet-version: 10.0.x

That is fine, but the best approach in my opinion is to have a global.json file with an sdk property set like this:

{
  "sdk": {
    "version": "10.0.401",
    "rollForward": "latestFeature"
  },
  "test": {
    "runner": "Microsoft.Testing.Platform"
  }
}

The action will read the SDK version from there. This is often a good thing as it means you just need to set the version in one place rather than all through your workflow files.

Using GitHub Packages feeds

Assuming you’ve already configured your GitHub packages feed in your NuGet.Config file (and note the use of the newer packageSourceMapping block):

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <packageSources>
        <add key="nuget" value="https://api.nuget.org/v3/index.json" />
        <add key="github" value="https://nuget.pkg.github.com/flcdrg/index.json" />
    </packageSources>

    <packageSourceMapping>
        <packageSource key="nuget">
            <package pattern="*" />
        </packageSource>
        <packageSource key="github">
            <package pattern="Verify.MongoDB" />
        </packageSource>
    </packageSourceMapping>
</configuration>

Then you can use the action to wire up authentication for the Packages feed with the source-url input parameter:

- uses: actions/setup-dotnet@v6
  with:
    source-url: https://nuget.pkg.github.com/flcdrg/index.json
  env:
    NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}

Note that you must grant the GitHub token access to read packages!

permissions:
  contents: read
  packages: read

And you’d think that would be it. But there are some edge cases to be aware of.

Building from a subdirectory

The action defaults to looking in the root of the repository for global.json and NuGet.Config files. If your .NET source code resides in a subdirectory (often the case if you are using a monorepo approach), then make sure you tell the action where they are:

- uses: actions/setup-dotnet@v6
  with:
    global-json-file: globaljson-and-nugetconfig/global.json
    source-url: https://nuget.pkg.github.com/flcdrg/index.json
    config-file: globaljson-and-nugetconfig/nuget.config

If you forget to set global-json-file and/or config-file then it will only look in the root of the repository, and either load the wrong files or load nothing if those files don’t exist at the root (as we saw earlier).

When the action/setup-dotnet action runs with source-url, it actually generates an additional temporary nuget.config file. You can see this in the workflow run output:

dotnet-auth: Finding any source references in /home/runner/work/using-setup-dotnet-action/using-setup-dotnet-action/nuget.config, writing a new temporary configuration file with credentials to /home/runner/work/using-setup-dotnet-action/nuget.config

That temporary file contains the authentication details for the feed.

But the restore fails:

Determining projects to restore...
/usr/share/dotnet/sdk/10.0.401/NuGet.targets(198,5): warning : Your request could not be authenticated by the GitHub Packages service. Please ensure your access token is valid and has the appropriate scopes configured. [/home/runner/work/using-setup-dotnet-action/using-setup-dotnet-action/globaljson-and-nugetconfig/tests/tests.csproj]
  Retrying 'FindPackagesByIdAsync' for source 'https://nuget.pkg.github.com/flcdrg/download/verify.mongodb/index.json'.

If you take a look at the contents of the temporary nuget.config file you might see the problem:

<configuration>
  <config>
    <add key="defaultPushSource" value="https://nuget.pkg.github.com/flcdrg/index.json"/>
  </config>
  <packageSources>
    <add key="Source" value="https://nuget.pkg.github.com/flcdrg/index.json"/>
  </packageSources>
  <packageSourceCredentials>
    <Source>
      <add key="Username" value="flcdrg"/>
      <add key="ClearTextPassword" value="***"/>
    </Source>
  </packageSourceCredentials>
</configuration>

The action knows the feed URL, but it has defaulted to using Source as the source name.

The issue here is that when dotnet restore runs, it finds the nuget.config file in the application directory and combines that with the temporary file.

The packageSourceMapping defines the github source as the only place to look for Verify.MongoDB package. But the temporary nuget.config file didn’t use that name. So you end up that error.

The solution is to set the config-file input parameter so that the action can use the correct source name from your nuget.config file. With the names aligned, everything should work correctly.

Summary