Action that pushes a multi-target Xamarin.Forms package

I’m trying to configure a GitHub action that builds my multi-target Xamarin.Forms plugin and pushes the package to GitHub and NuGet. Pushing the package to GitHub and NuGet shouldn’t be much of a problem anymore. I’m having trouble with building my multi-targeted nuget package on the GitHub CI.

First I had the following GitHub action:

name: .NET Core

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v2
    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1.5.0
      with:
        dotnet-version: 3.1.301
        # Authenticates packages to push to GPR
        source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
      env:
        NUGET_AUTH_TOKEN: '%NUGET_AUTH_TOKEN%'
    - name: Install dependencies
      run: dotnet restore
    - name: Build
      run: dotnet build --configuration Release --no-restore
    - name: Test
      run: dotnet test --no-restore --verbosity normal
    - name: Pack
      run: dotnet pack --no-build --configuration Release MintPlayer.MVVM/MintPlayer.MVVM.csproj --output .
    - name: PushNuget
      run: dotnet nuget push *.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.PUBLISH_TO_NUGET_ORG }} --skip-duplicate
    - name: PushGithub
      #run: dotnet nuget push *.nupkg --source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json --api-key ${{ github.token }} --no-symbols --skip-duplicate
      run: dotnet nuget push *.nupkg --no-symbols --skip-duplicate
      env:
        NUGET_AUTH_TOKEN: ${{ github.token }}

and also the following nuget.config in my solution root, to let the action know about the nuget source to download the required NuGet packages:

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

When pushing this configuration to my GitHub repository, I’m getting the following errors at the Build stage:

/Users/runner/work/MintPlayer.MVVM/MintPlayer.MVVM/Demo/MintPlayer.MVVM.Demo/MintPlayer.MVVM.Demo.UWP/MintPlayer.MVVM.Demo.UWP.csproj(175,3): error MSB4019: The imported project "/Users/runner/hostedtoolcache/dncs/3.1.301/x64/sdk/3.1.301/Microsoft/WindowsXaml/v16.0/Microsoft.Windows.UI.Xaml.CSharp.targets" was not found. Confirm that the expression in the Import declaration "/Users/runner/hostedtoolcache/dncs/3.1.301/x64/sdk/3.1.301//Microsoft/WindowsXaml/v16.0/Microsoft.Windows.UI.Xaml.CSharp.targets" is correct, and that the file exists on disk.
/Users/runner/work/MintPlayer.MVVM/MintPlayer.MVVM/Demo/MintPlayer.MVVM.Demo/MintPlayer.MVVM.Demo.iOS/MintPlayer.MVVM.Demo.iOS.csproj(140,3): error MSB4019: The imported project "/Users/runner/hostedtoolcache/dncs/3.1.301/x64/sdk/3.1.301/Xamarin/iOS/Xamarin.iOS.CSharp.targets" was not found. Confirm that the expression in the Import declaration "/Users/runner/hostedtoolcache/dncs/3.1.301/x64/sdk/3.1.301//Xamarin/iOS/Xamarin.iOS.CSharp.targets" is correct, and that the file exists on disk.
/Users/runner/work/MintPlayer.MVVM/MintPlayer.MVVM/Demo/MintPlayer.MVVM.Demo/MintPlayer.MVVM.Demo.Android/MintPlayer.MVVM.Demo.Android.csproj(101,3): error MSB4019: The imported project "/Users/runner/hostedtoolcache/dncs/3.1.301/x64/sdk/3.1.301/Xamarin/Android/Xamarin.Android.CSharp.targets" was not found. Confirm that the expression in the Import declaration "/Users/runner/hostedtoolcache/dncs/3.1.301/x64/sdk/3.1.301//Xamarin/Android/Xamarin.Android.CSharp.targets" is correct, and that the file exists on disk.

I then modified my workflow to use msbuild instead (at least until the build stage:

name: .NET Core

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    runs-on: macos-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v2
    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1.5.0
      with:
        dotnet-version: 3.1.301
        # Authenticates packages to push to GPR
        source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
      env:
        NUGET_AUTH_TOKEN: '%NUGET_AUTH_TOKEN%'
    - name: Install dependencies
      #run: dotnet restore
      #env:
      #  NUGET_AUTH_TOKEN: ${{ github.token }}
      run: msbuild /t:restore
    - name: Build
      #run: dotnet build --configuration Release --no-restore
      run: msbuild Library/MintPlayer.MVVM/MintPlayer.MVVM.csproj /p:Configuration=Release /p:RestorePackages=false
    - name: Test
      run: dotnet test --no-restore --verbosity normal
    - name: Pack
      run: dotnet pack --no-build --configuration Release MintPlayer.MVVM/MintPlayer.MVVM.csproj --output .
    - name: PushNuget
      run: dotnet nuget push *.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.PUBLISH_TO_NUGET_ORG }} --skip-duplicate
    - name: PushGithub
      #run: dotnet nuget push *.nupkg --source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json --api-key ${{ github.token }} --no-symbols --skip-duplicate
      run: dotnet nuget push *.nupkg --no-symbols --skip-duplicate
      env:
        NUGET_AUTH_TOKEN: ${{ github.token }}

But it’s still not working.

How can I build a Xamarin.Forms package in GitHub CI and push it to my nuget sources?

Edit:

The console in Github actions prints the following error between the tons of text:

/Users/runner/.nuget/packages/msbuild.sdk.extras/2.1.2/Build/LanguageTargets/CheckMissing.targets(44,5): error : The specified language targets for uap10.0.16299 is missing. Ensure correct tooling is installed for 'uap'. Missing: '/Library/Frameworks/Mono.framework/Versions/6.12.0/lib/mono/xbuild/Microsoft/WindowsXaml/v16.0/Microsoft.Windows.UI.Xaml.CSharp.targets' [/Users/runner/work/MintPlayer.MVVM/MintPlayer.MVVM/Library/MintPlayer.MVVM/MintPlayer.MVVM.csproj]

So this probably means that the UWP tools aren’t available on macos. I can see that the android and iOS builds succeed, but the UWP build fails with the former message.

Alright, I’m getting closer. My GitHub action currently looks like this:

name: .NET Core

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    runs-on: windows-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v2
    
    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1.5.0
      with:
        dotnet-version: 3.1.301
        # Authenticates packages to push to GPR
        source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
      env:
        NUGET_AUTH_TOKEN: '%NUGET_AUTH_TOKEN%'
    
    # Add  MsBuild to the PATH: https://github.com/microsoft/setup-msbuild
    - name: Setup MSBuild.exe
      uses: microsoft/setup-msbuild@v1.0.1
    
    - name: Install dependencies
      run: msbuild /t:restore
      env:
        NUGET_AUTH_TOKEN: ${{ github.token }}
    
    - name: Build
      run: msbuild Library/MintPlayer.MVVM/MintPlayer.MVVM.csproj /p:Configuration=Release /p:RestorePackages=false /p:OutputPath=../..
    
    - name: PushNuget
      run: dotnet nuget push *.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.PUBLISH_TO_NUGET_ORG }} --skip-duplicate
    - name: PushGithub
      run: dotnet nuget push *.nupkg --no-symbols --skip-duplicate
      env:
        NUGET_AUTH_TOKEN: ${{ github.token }}

It succeeds in pushing to Nuget.org, but fails for github.com:

A successful push looks like this:

So why does it still fail to push with the following error:

An error was encountered when fetching 'PUT https://nuget.pkg.github.com/OWNER/'. The request will now be retried.
An error occurred while sending the request.
The response ended prematurely.

Edit:
Okay, I incremented the package version and now it seems to push to both NPR and GPR successfully.

Also, as @joelrevans pointed out the dotnet nuget push --skip-duplicate command works fine for nuget.org but errors on github.com, causing the workflow to fail. Is this a known bug or can we get around this somehow?

It seems that when I install this package in my Xamarin.Forms projects, I’m only able to call the UWP classes, and in all of the projects. So the UWP declarations are also available in the Android and iOS projects. What could be causing this?

When I manually pack the project (Right-click -> Pack) the package works fine, but the workflow somehow messes this up.

How can I fix this and get the Android declarations available in my android project, iOS declarations in my iOS project and UWP declarations in my UWP project?

@PieterjanDeClippel ,

It seems that when I install this package in my Xamarin.Forms projects, I’m only able to call the UWP classes, and in all of the projects. So the UWP declarations are also available in the Android and iOS projects. What could be causing this?

This should be related to Xamarin.Forms, maybe you can reference “Xamarin.Forms documentation” and “Xamarin.Forms application fundamentals” to get more detailed information.

When I manually pack the project (Right-click -> Pack) the package works fine, but the workflow somehow messes this up.

What commands did you use to package the project in your workflow?
Did you also try the same commands to package the same project on your local environment? Did it work?
You can reference “Configuring dotnet CLI for use with GitHub Packages”, it should be helpful to you.

This is my latest workflow (with the MSBuild commands).

I’ve done the following steps:

  • Incremented my package version to ensure the package is installed from my local folder
  • Run the same MSBuild commands from my workflow locally
  • Installed my nuget package in a new Xamarin.Forms project (It’s the new version, so the local package)

The results are the same. I cannot access the platform classes in my platform projects. The UWP declarations are available in all projects.

Summary:

  • MSBuild (manually) → Only UWP classes available
  • MSBuild (workflow) → Only UWP classes available
  • Right-click project → Pack → All classes per target platform available = ok

If I just right-click the library and click the Pack-command, the resulting package is working fine.

Note: I already tried using the dotnet-cli:

dotnet build --configuration Release --no-restore

But this command fails with the following errors. That’s why tried using MSBuild:

/Users/runner/work/MintPlayer.MVVM/MintPlayer.MVVM/Demo/MintPlayer.MVVM.Demo/MintPlayer.MVVM.Demo.iOS/MintPlayer.MVVM.Demo.iOS.csproj(140,3): error MSB4019: The imported project "/Users/runner/hostedtoolcache/dncs/3.1.301/x64/sdk/3.1.301/Xamarin/iOS/Xamarin.iOS.CSharp.targets" was not found. Confirm that the expression in the Import declaration "/Users/runner/hostedtoolcache/dncs/3.1.301/x64/sdk/3.1.301//Xamarin/iOS/Xamarin.iOS.CSharp.targets" is correct, and that the file exists on disk.
/Users/runner/.nuget/packages/msbuild.sdk.extras/2.1.2/Build/Workarounds.targets(27,5): error : If you are building projects that require targets from full MSBuild or MSBuildFrameworkToolsPath, you need to use desktop msbuild ('msbuild.exe') instead of 'dotnet build' or 'dotnet msbuild' [/Users/runner/work/MintPlayer.MVVM/MintPlayer.MVVM/Library/MintPlayer.MVVM/MintPlayer.MVVM.csproj]

Workflow file for this attempt:

name: .NET Core

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    runs-on: macos-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v2
    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1.5.0
      with:
        dotnet-version: 3.1.301
        # Authenticates packages to push to GPR
        source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
      env:
        NUGET_AUTH_TOKEN: '%NUGET_AUTH_TOKEN%'
    - name: Install dependencies
      run: dotnet restore
      env:
        NUGET_AUTH_TOKEN: ${{ github.token }}
    - name: Build
      run: dotnet build --configuration Release --no-restore
    - name: Test
      run: dotnet test --no-restore --verbosity normal
    - name: Pack
      run: dotnet pack --no-build --configuration Release MintPlayer.MVVM/MintPlayer.MVVM.csproj --output .
    - name: PushNuget
      run: dotnet nuget push *.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.PUBLISH_TO_NUGET_ORG }} --skip-duplicate
    - name: PushGithub
      #run: dotnet nuget push *.nupkg --source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json --api-key ${{ github.token }} --no-symbols --skip-duplicate
      run: dotnet nuget push *.nupkg --no-symbols --skip-duplicate
      env:
        NUGET_AUTH_TOKEN: ${{ github.token }}

I also tried using nuget.exe but this gives me the following error, because I’m targetting multiple frameworks:

A numeric comparison was attempted on "$(_TargetFrameworkVersionValue)" that evaluates to "" instead of a number, in condition "'$(_TargetFrameworkVersionValue)' >= '4.0'".  C:\Program Files\dotnet\sdk\5.0.100-preview.7.20366.6\Sdks\Microsoft.NET.Sdk.WindowsDesktop\targets\Microsoft.NET.Sdk.WindowsDesktop.props

@PieterjanDeClippel ,

Maybe you need to check the code of your NuGet package for Xamarin to see if it has correctly contained all the platform-specific projects (iOS, Android, and Windows).
To view more detailed information, you can reference “Create packages for Xamarin with Visual Studio 2017 or 2019”.

Thanks for the response.

I’ve used the xamarin-forms plugin template some time ago. But apparently, the Plugin for Xamarin.Forms template doesn’t exist anymore. Was pretty convenient though using .android.cs, .ios.cs extensions with built-in itemgroup-conditions.

Do you know what commands are being run when one right-clicks the project and hits the Pack command? I want to try and run these commands manually to test if this works.

dotnet build --configuration Release --no-restore

doesn’t seem to work. Using the MSBuild cli only seems to pack the uwp declarations. Actually clicking the Pack command in VS works fine and packs all declarations and makes them available in the dedicated projects. But what does this do behind the scene? (Actually a Visual Studio question has nothing to do with GitHub…)

@PieterjanDeClippel ,

The problems you are facing should be related to Xamarin.
I recommend that you can directly report the problems in the .NET board of Developer Community. You can get more detailed and professional support about this topic.

In addition, the GitHub Actions board of the GitHub Community is used for the discussions and questions about GitHub Actions . If you need help with anything related to workflow configuration, such as syntax, GitHub-hosted runners, or building actions, you can look for an existing topic or start a new one in this board.

Link to issue: https://github.com/dotnet/msbuild/issues/5721

I found out that the issue is introduced when specifying the OutputPath in the msbuild pack command. So instead of using

msbuild /t:Pack /p:Configuration=Debug /p:OutputPath=../.. Library/MintPlayer.MVVM/MintPlayer.MVVM.csproj

I’m using the following 2 commands instead:

msbuild /t:Pack /p:Configuration=Debug Library/MintPlayer.MVVM/MintPlayer.MVVM.csproj
copy Library/MintPlayer.MVVM/bin/Debug/*.nupkg .