Camden Narzt
Camden Narzt

Reputation: 2003

How to dotnet restore a multi-target framework in CI for testing using old version of dotnet?

I have a C# package which currently supports dotnet 8 and I'm trying to modify it to add dotnet 6 and 7 support as well. I've gotten it to build for all versions using dotnet 8 locally on my laptop, using <TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks> in the .csproj file and removing all C# features added after net6.0. However when I push it to GitHub and my workflow runs in Github Actions, I test it against dotnet 6 and 7 as well as 8, and it breaks on the dotnet restore step.

My workflow:

name: .NET

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

env:
  DOTNET_CLI_TELEMETRY_OPTOUT: true
  DOTNET_GENERATE_ASPNET_CERTIFICATE: false

jobs:
  build:

    runs-on: macos-latest
    name: .NET ${{ matrix.dotnet }}
    strategy:
      matrix:
        dotnet:
          - 8.0 # EOL: 2026-11-10
          - 7.0 # EOL: 2024-05-14
          - 6.0 # EOL: 2024-11-12
          # version support doc: https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core
    steps:
    - uses: actions/checkout@v3
      with:
        repository: getargv/getargv
        path: getargv
        token: ${{ secrets.GH_PAT }}
    - name: Build libgetargv
      run: make install_dylib
      working-directory: getargv
    - uses: actions/checkout@v3
      with:
        path: getargv.cs
    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      id: setup
      with:
        dotnet-version: ${{ matrix.dotnet }}.x
    - name: Create temporary global.json
      run: "echo '{\"sdk\":{\"version\": \"${{ steps.setup.outputs.dotnet-version }}\"}}' > ./global.json"
      working-directory: getargv.cs
    - name: Restore dependencies
      run: dotnet restore
      working-directory: getargv.cs
    - name: Build
      run: dotnet build --no-restore --framework net${{ matrix.dotnet }}
      working-directory: getargv.cs
    - name: Test
      run: dotnet test --no-build --verbosity normal
      working-directory: getargv.cs

I can set the framework when building with a flag, but the restore step has no such flag.

The error I get is fairly predictable:

Error: /Users/runner/.dotnet/sdk/7.0.404/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.TargetFrameworkInference.targets(160,5): error NETSDK1045: The current .NET SDK does not support targeting .NET 8.0.  Either target .NET 7.0 or lower, or use a version of the .NET SDK that supports .NET 8.0. Download the .NET SDK from https://aka.ms/dotnet/download [/Users/runner/work/getargv.cs/getargv.cs/getargv.cs/Getargv/Getargv.csproj::TargetFramework=net8.0]
Error: /Users/runner/.dotnet/sdk/7.0.404/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.TargetFrameworkInference.targets(160,5): error NETSDK1045: The current .NET SDK does not support targeting .NET 8.0.  Either target .NET 7.0 or lower, or use a version of the .NET SDK that supports .NET 8.0. Download the .NET SDK from https://aka.ms/dotnet/download [/Users/runner/work/getargv.cs/getargv.cs/getargv.cs/Getargv.Tool/Getargv.Tool.csproj::TargetFramework=net8.0]
Error: /Users/runner/.dotnet/sdk/7.0.404/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.TargetFrameworkInference.targets(160,5): error NETSDK1045: The current .NET SDK does not support targeting .NET 8.0.  Either target .NET 7.0 or lower, or use a version of the .NET SDK that supports .NET 8.0. Download the .NET SDK from https://aka.ms/dotnet/download [/Users/runner/work/getargv.cs/getargv.cs/getargv.cs/Getargv.Tests/Getargv.Tests.csproj::TargetFramework=net8.0]
Error: /Users/runner/.dotnet/sdk/6.0.417/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.TargetFrameworkInference.targets(144,5): error NETSDK1045: The current .NET SDK does not support targeting .NET 7.0.  Either target .NET 6.0 or lower, or use a version of the .NET SDK that supports .NET 7.0. [/Users/runner/work/getargv.cs/getargv.cs/getargv.cs/Getargv/Getargv.csproj]
Error: /Users/runner/.dotnet/sdk/6.0.417/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.TargetFrameworkInference.targets(144,5): error NETSDK1045: The current .NET SDK does not support targeting .NET 7.0.  Either target .NET 6.0 or lower, or use a version of the .NET SDK that supports .NET 7.0. [/Users/runner/work/getargv.cs/getargv.cs/getargv.cs/Getargv.Tool/Getargv.Tool.csproj]
Error: /Users/runner/.dotnet/sdk/6.0.417/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.TargetFrameworkInference.targets(144,5): error NETSDK1045: The current .NET SDK does not support targeting .NET 7.0.  Either target .NET 6.0 or lower, or use a version of the .NET SDK that supports .NET 7.0. [/Users/runner/work/getargv.cs/getargv.cs/getargv.cs/Getargv.Tests/Getargv.Tests.csproj]

How do people test against older dotnet versions while also supporting newer versions?

Edit:

Changing the build command to: dotnet build --framework net${{ matrix.dotnet }} doesn't use the specified framework for restore.

Upvotes: 3

Views: 2160

Answers (4)

tsul
tsul

Reputation: 1576

Yes, things are a little more complicated for a multi-targeting project. This answer is just a summary how to build them using CLI when you have only one of the target frameworks installed.

Suppose you have .NET 6 SDK installed only and your solution projects multi-target net6.0 and net8.0.

Then the following commands work for me:

  • dotnet build -f net6.0 --no-restore
  • dotnet build -p:TargetFrameworks=net6.0
  • dotnet restore -p:TargetFrameworks=net6.0
  • dotnet restore -p:TargetFramework=net6.0
  • dotnet test -f net6.0 --no-restore
  • dotnet test -p:TargetFrameworks=net6.0

And the following ones do not work:

  • dotnet build
  • dotnet build -f net6.0
  • dotnet build -p:TargetFramework=net6.0
  • dotnet restore
  • dotnet test
  • dotnet test -f net6.0
  • dotnet test -p:TargetFramework=net6.0

As you can see, there is an inconsistency between using --framework switch, TargetFramework and TargetFrameworks properties in the different dotnet-subcommands.

My case is that I have multi-targeted solution (the main project targets net6.0 and the tests project targets net6.0 and net8.0 via TargetFrameworks switch).

For me, having dotnet build -f net6.0 not working in that case is the strangiest thing with the dotnet CLI, worthy of an issue on MS dotnet github page. Maybe it's already added, didn't check that.

P.S. Another case, you have .NET 8 SDK installed and the same multi-targeting solution (net6.0 main project and net6.0+net8.0 test project). Then the things are rather different, and the following commands succeeded:

  • dotnet build
  • dotnet build -f net6.0
  • dotnet restore
  • dotnet test -f net8.0

Upvotes: 0

GChuf
GChuf

Reputation: 2230

You can specify the target framework for dotnet restore with dotnet restore -p:TargetFramework=net7.0.

dotnet restore and dotnet build are wrappers around dotnet msbuild, and dotnet restore is actually the same as calling dotnet msbuild -t:restore. Some additional information can be found in this question.

I would try changing your code like this:

    - name: Restore dependencies
      run: dotnet restore -p:TargetFramework=net${{ matrix.dotnet }}

Upvotes: 3

stefan.seeland
stefan.seeland

Reputation: 3270

The common Github Actions way to go:

  1. Install all targeted SDKs
  2. Use the <TargetFrameworks> tag in the test csproj to pick the .net versions that should be tested. The testrunner will run each test in each .net version listed there.

Thats sufficient for every project I have seen so far.

Test Matrix approach

If your really really want to test, like you drafted, using a test matrix to ensure support / passed tests on lacking SDKs your approach seems to be reasonable. Things I fixed in your GHA workflow:

  1. In this POC it seems that the format of your matrix values get messed up -> avoid "." in the version.
  2. You need to pick the .net version on running the tests.
  build:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        dotnet: [8, 7, 6]
    steps:
    - uses: actions/checkout@v4
    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: ${{ matrix.dotnet }}.0.x
    - name: Restore dependencies
      run: dotnet restore
    - name: Build
      run: dotnet build --configuration Release --no-restore --framework net${{ matrix.dotnet }}.0
    - name: Test
      run: dotnet test --no-build --verbosity normal --configuration Release --framework net${{ matrix.dotnet }}.0

Disclaimer

I skipped complexity of global.json and sdk pinning in Directory.Build.props. It does not really make sense in case of explicitly installing/omitting the SDK version anyway. And settings in global.json were related to NETSDK1045 in the past.

A starting point that does not fail (added latestMajor) is e.g.:

    - name: Create temporary global.json
      run: "echo '{\"sdk\":{\"version\": \"${{ matrix.dotnet }}.0.0\" ,\"rollForward\": \"latestMajor\" }}' > ./global.json"

Upvotes: 0

Andrii Krot
Andrii Krot

Reputation: 336

dotnet --version # Check the installed versions
dotnet --list-sdks # List all installed SDKs
dotnet --global install "desired_version" # Install the desired version

After specifying the .NET SDK version, execute the dotnet restore command for your multi-target framework project. This command will restore the NuGet packages required for your project.

# Navigate to your project directory
cd path/to/your/project

dotnet restore

Once the packages are restored, you can run your tests using the dotnet test command. This assumes you have unit tests set up for your project.

dotnet test

Make sure to adapt the version numbers and paths according to your project's requirements.

Example:

# Install .NET SDK 7.0.404
dotnet --global install 7.0.404

# Navigate to your project directory
cd path/to/your/project

# Restore packages
dotnet restore

# Run tests
dotnet test

Upvotes: 0

Related Questions