Overview
I recently had a customer ask me how they could migrate their NuGet packages from one GitHub instance to another (e.g.: from GitHub Enterprise Server to GitHub Enterprise Cloud). I wasn’t aware of any tooling that did this, so I decided to write my own.
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 🚀!
Running the script
Prerequisites
gh cli
installed and logged in to be able to access the source GitHub instance:1
gh auth login
- Auth to read packages from the source GitHub instance with
gh
:1
gh auth refresh -h github.com -s read:packages # update -h with source github host
gpr
installed:1
dotnet tool install gpr -g
- 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
<target-pat>
must havewrite:packages
scope- This assumes that the target org’s repo name is the same as the source.
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.
Usage
You can call the script via:
1
2
3
4
5
6
./migrate-nuget-packages-between-orgs.sh \
<source-org>
<source-host> \
<target-org> \
<target-pat> \
<path-to-gpr>
Example
An example of this in practice:
1
2
3
4
5
6
./migrate-nuget-packages-between-orgs.sh \
joshjohanning-org-packages \
github.com \
joshjohanning-org-packages-migrated \
ghp_xyz \
/home/codespace/.dotnet/tools/gpr
Use this one-liner to copy all
.nupkg
files to the current working directory before running the script:
1 find / -name "*.nupkg" -exec cp "{}" ./ \;
Notes
- The script uses
gpr
to re-push the packages to the target org. It assumes that the target org’s repo has the same name as the source.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 ofgpr
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"
- Also, in the script, I had to delete
_rels/.rels
and[Content_Types].xml
because there was somehow two copies of each file in the package and it causes gpr to fail when extracting/zipping the package to re-push - To clean up the working directory when done, run this one-liner:
1
rm *.nupkg *.zip
Improvement Ideas
- Add a source folder input instead of relying on current directory
- Map between repositories where the target repo is named differently than the source repo
- Dynamically determine out where
gpr
is installed instead of passing in a parameter (right now we are passing thegpr
path in as a parameter explicitly because sometimesgpr
is aliased togit pull --rebase
)
Summary
Drop a comment here or an issue or PR on the repo if you have any feedback or suggestions! Happy packaging! 📦