Post

GitHub Packages: Migrate NuGet Packages to GitHub Packages

Overview

To complete my NuGet Package migration series, I wanted to demonstrate how one could migrate NuGet packages to GitHub Package. The other system we are migrating from (whether it be Azure Artifacts, Artifactory, etc.) doesn’t so much matter, as long as we are able to download/access the .nupkg files on a system so that we can re-push to GitHub Packages.

See my other NuGet package migration posts:

The script

The repo and docs can be found here:

I decided to store the script in a separate GitHub repo than my github-misc-scripts repo to better facilitate any feedback/suggestions/improvements I might get - feel free to submit a PR if you can improve things 🚀!

Update the mappings file

Your csv file should look something like this:

1
2
3
4
Package,Target GitHub Repo
./mypkg.11.0.1.nupkg,my-org/my-repo
./mypkg.11.0.2.nupkg,my-org/my-repo

Leave a trailing space at the end of the csv file.

Running the script

There are two scripts that need to be ran:

  1. Generate the list of packages to migrate in the current directory and creating a mappings csv file
    • We then need to fill out the GitHub repository mapping for each package in the form of owner/repo
  2. Migrate the packages to GitHub Packages

Prerequisites

But first, the prequisites:

  1. gpr installed:
    1
    
    dotnet tool install gpr -g
    
  2. Can use this one-liner to find the absolute path for gpr for the <path-to-gpr> parameter:
    1
    
    find / -wholename "*tools/gpr" 2> /dev/null
    
  3. <pat> must have write:packages scope

We are passing gpr in as a parameter explicitly because sometimes gpr is aliased to git pull --rebase and that’s not what we want here.

Generate the Mappings File

This finds all .nupkg files in the current directory and generates a mappings csv file.

1
2
3
./generate-nuget-package-mappings.sh \
  . \
  > <mappings-file>

Use this one-liner to copy all .nupkg files to a directory before ./generate-nuget-package-mappings.sh:

1
find / -name "*.nupkg" -exec cp "{}" ./  \;

Afterwards, you need to edit the csv file to add the target GitHub repo reference, in the form of owner/repo.

Leave a trailing space at the end of the csv file.

Migrate the Packages

This pushes the packages to the mapped GitHub repo:

1
2
3
4
./migrate-nuget-packages-to-github.sh \
  <mappings-file> \
  <pat> \
  <path-to-gpr>

Complete Example

An example of this in practice:

1
2
3
4
5
6
7
8
9
10
11
12
# 1. generate mappings file
./generate-nuget-package-mappings.sh \
  . \
  > packages.csv

# 2. edit the mappings file to add the GitHub repo in the form of `owner/repo`

# 3. push packages
./migrate-nuget-packages-between-orgs.sh \
  packages.csv \
  ghp_xyz \
  /home/codespace/.dotnet/tools/gpr

Notes

  • The script uses gpr to re-push the packages to the target org
    • I initially tried writing this with dotnet nuget push, but that doesn’t seem to work since the package’s <RepositoryUrl> element would still be referencing the original repository. See error:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      
      dotnet nuget push \
        -s github \
        -k ghp_pat \
        NUnit3.DotNetNew.Template_1.7.1.nupkg
      
      Pushing NUnit3.DotNetNew.Template_1.7.1.nupkg to 'https://nuget.pkg.github.com/joshjohanning-org-packages-migrated'...
        PUT https://nuget.pkg.github.com/joshjohanning-org-packages-migrated/
      warn : Source owner 'joshjohanning-org-packages-migrated' does not match repo owner 'joshjohanning-org-packages' in repository element.
        BadRequest https://nuget.pkg.github.com/joshjohanning-org-packages-migrated/ 180ms
      error: Response status code does not indicate success: 400 (Bad Request).
      
    • gpr works because it rewrites the <repository url="..." /> element in the .nuspec file in the .nupkg before pushing
    • There is an item on GitHub’s roadmap to support pushing packages directly to an organization; this should allow dotnet nuget push to work instead of gpr
      • gpr still might be preferred since you would have to tie the NuGet package to the repository manually post-migration
      • For dotnet nuget push to work, you will have to add the feed first using this command:
        1
        2
        3
        4
        5
        6
        
        dotnet nuget add source \
          --username my-github-username \
          --password "ghp_pat" \
          --store-password-in-clear-text \
          --name github \
          "https://nuget.pkg.github.com/OWNER/index.json"
        

Improvement Ideas

  • Add a source folder input instead of relying on current directory for ./generate-nuget-package-mappings.sh
  • Dynamically determine out where gpr is installed instead of passing in a parameter (right now we are passing the gpr path in as a parameter explicitly because sometimes gpr is aliased to git pull --rebase)

Summary

Drop a comment here or an issue or PR on the repo if you have any feedback or suggestions! Happy packaging! 📦

This post is licensed under CC BY 4.0 by the author.