GitHub NuGet packages and how to provide native binaries in their own directory

Creating and publishing packages in GitHub

Creating tokens

Go https://github.com/settings/tokens and create 2 tokens. One with a write:packages scope and one with a read:packages scope for consumption. The first token I called Package Publisher and the second one Package Reader. Save them somewhere safe.

Creating NuGet config file

Create nuget.config file. Something like this:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <packageSources>
        <add key="github" value="https://nuget.pkg.github.com/<YourOrganizationName>/index.json" />
    </packageSources>
</configuration>

Instead of "github" you can use any other description you like. It just a name of the entry. Then you need to put this file in the same directory as solution for your package and for every solution that will consume packages from GitHub. As far as understand that you can put nuget.config higher in directory hierarchy. For example, in the root of your repository or even in the root of your disk. There is also a global nuget.config.

Creating package

Create a Class Library project in Visual Studio, go to Project options and then go to the Package node on the left side. Then you check Generate NuGet package on build. After that, every compilation of this project will generate a .nupkg file automatically. After that provide necessary details. Most important is Repository URL. Without this, it will not publish.

Publishing package

Compile your project, go to directory with a .nupkg file. In my example it was bin\Debug. To publish package, use a following command:

dotnet nuget push SomePackage.1.0.0.nupkg  --source github --api-key <You publishing token>

After that, you can go to your repository in GitHub, and click Packages link on the right side and you see your package.

Consuming package

Copy the same nuget.config file to solution that will consume our package, right click on a project, and then select Manage NuGet packages. Then on the right top side of that window, in Package source, select github. Please note, it is the same "github" that you have in nuget.config file. After that, Visual Studio will ask you for username and password. Use your GitHub username and as password you will have to use Package Reader token. After that, you can click on a Browse section, and it will show all packages from your GitHub repository.

Package with custom binaries

But I also would like to create package with custom binary files that are resides in a own directory. After installation I would like to have something like this:

Application.exe
SomePackage.dll <- from package
native\Some.dll <- from package

It turns out quite non-trivial task.

Changing project file

I put my binary files in the directory called runtime. Next step to add folllowin section to your package’s project:

  <ItemGroup>
    <None Include="runtime/**">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <PackagePath>build\runtime</PackagePath>
      <Pack>true</Pack>
    </None>
    <None Include="SomePackage.targets">
      <PackagePath>build\</PackagePath>
      <Pack>true</Pack>
    </None>
  </ItemGroup>

Creating .targets file

Next step to create .targets that will install your binary files. Here is file I used:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <RuntimeFiles Include="$(MSBuildThisFileDirectory)\runtime\**" />
  </ItemGroup>
  <Target Name="CopyLogFiles" BeforeTargets="Build">
    <Copy SourceFiles="@(RuntimeFiles)" DestinationFolder="$(TargetDir)\%(RecursiveDir)" SkipUnchangedFiles="true"/>
  </Target>
</Project>

Please note, that file name should be the same as your project file name, just extension should be different. After that, when you install this package, everything from runtime directory will installed to output directory of your project. If you have any folders in runtime directory, they will be copied from package with their content. In my case I will have folder called native there.

I hope it helps someone.