Skip to content

Main Index

Dr. Neil's Notes

Software > Coding

Using GitHub Packages with NuGet

Introduction

This note introduces using GitHub to manage and maintain packages. As most of the work, and hobby, projects I do are in .NET, the contents of this note are focussed on NuGet packages. Packaging is a common, and standard, way to enable code to be shared between projects that are not in the same distributed output. For example in these notes the code for the Azure Key Vault could be compiled into a DLL that is shared by different products that require secret keys. Packaging also supports versioning of the code, and shared component. This provides the facility for different products to be working with different versions of the shared code.

Building the Package

When building a .NET project that you desire to distribute as a package, you can define the details of the package in the .csproj file. Previously this was often done in a .nuspec file, and you can still find .nuspec files in folders of many projects.

For example the .csproj file for a project named CoolProject could contain a PropertyGroup as follows

<PropertyGroup>
    <IsPackable>true</IsPackable>
    <Authors>DrNeil</Authors>
    <Description>A .NET library for using doing cool things</Description>
    <PackageLicenseExpression>NONE</PackageLicenseExpression>
    <PackageProjectUrl>https://github.com/ORGANIZATION_NAME/CoolProject</PackageProjectUrl>
    <RepositoryUrl>https://github.com/ORGANIZATION_NAME/CoolProject</RepositoryUrl>
    <PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
    <None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>

For a package you want to publish outside of your organization, the PackageLicenseExpression should be set to reflects the license you wish to use for distribution.

Obviously, replace the ORGANIZATION_NAME with your GitHub organization name, or if you are not an organization it will be the GitHub name where you have your repositories, for example my account is DrNeil so the the RepositoryUrl would be https://github.com/DrNeil/CoolProject

The PackageReadmeFile element is used to set the file used to describe the package, and here is set to a README.md file in the project folder. The ItemGroup at the end indicates the README.md file should not be compiled and is purely for packing in the NuGet package.

When you build this project you can package it. Normally you would only want to package a release build and so from a command line build this would be achieved as follows:

dotnet pack -c Release CoolProject.csproj

Pushing the package

Typically publishing a package should be done from the CI (Continuous Integration) process, for example an Azure DevOps PipeLine or GitHub Action. However for a hobby project, like many of those in these Notes publishing from a local build is fine, and GitHub actions is beyond the scope of this Note.

In order to write a package to GitHub you will need a Classic Personal Access Token (PAT) with the scope write:packages. To create a Classic PAT see the GitHub docs here

Once you have a PAT you can use it from a PowerShell script as follows:

$ghKey = Read-Host -Prompt "Enter your github PAT"
dotnet nuget push "bin/Release/CoolProject.1.0.0.nupkg"  --api-key $ghKey  --source https://nuget.pkg.github.com/<ORGANIZATION_NAME>/index.json

Consuming the package

In order to restore any private packages from GitHub you will need a Classic Personal Access Token (PAT) with the scope read:packages. To create a Classic PAT see the GitHub docs here

Then in the project folder where you desire to restore the package, add the GitHub packages folder for your organization to the nuget.config file. If you do not have a nuget.config file you may need to create one. The packageSources define the locations from which to retrieve packages when the .NET compiler tries to restore the packages to your local hard drive in order to build the project. It is possible to have many packageSources, sometimes, many from different GitHub organizations.

An example nuget.config file.

<configuration>
    <packageSources>
        <clear />
        <!-- `key` can be any identifier for your source. -->
        <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
        <add key="github" value="https://nuget.pkg.github.com/<ORGANIZATION_NAME>/index.json" />
    </packageSources>

    <!-- Define mappings by adding package patterns beneath the target source. -->
    <!-- Contoso.* packages and NuGet.Common will be restored from contoso.com, everything else from nuget.org. -->
    <packageSourceMapping>
        <!-- key value for <packageSource> should match key values from <packageSources> element -->
        <packageSource key="nuget.org">
            <package pattern="*" />
        </packageSource>
        <packageSource key="github">
            <package pattern="*" />
        </packageSource>
    </packageSourceMapping>
</configuration>

Then you can add the package to your .csproj file using the PackageReference element

 <ItemGroup>
    <PackageReference Include="CoolProject" Version="1.0.0" />
  </ItemGroup>

To restore (retrieve) the packages you can either do a build dotnet build, or explicitly run dotnet restore.

When you restore the packages, if the package has not been made public, you will be asked for the GitHub credentials, You will need to enter your GitHub username and the PAT for the password.

Conclusions

Packaging components to be shared amongst products is a great way of sharing code between different deliverables. As packaging also supports versioning, it enables you to have different versions of the shared code used by different products, enabling you to roll out updates across products over time, and not forcing every product to always be on the latest version of the packaged code.


Last update: July 29, 2023 22:31:32
Created: July 23, 2023 00:13:22
Authors: Neil Roodyn