# NuGet to GitHub Packages Build and publish NuGet packages to GitHub Packages registry. [Back to Examples](/examples) ## Overview GitHub Packages provides a NuGet registry alongside your repository. This recipe shows how to build NuGet packages and publish them to GitHub Packages, making it easy to share internal libraries within your organization. ## Prerequisites ### Authentication Publishing to GitHub Packages requires a personal access token (PAT) or GitHub Actions token with the `write:packages` scope. | Environment Variable | Description | |---------------------|-------------| | `GITHUB_TOKEN` | Token for GitHub Packages authentication | | `NUGET_API_KEY` | Alternative: can use same token | ### GitHub Packages Feed URL The feed URL format is: ``` https://nuget.pkg.github.com/{OWNER}/index.json ``` Replace `{OWNER}` with your GitHub username or organization name. ## Basic Package Publishing Build and publish a package to GitHub Packages: ```csharp var project = Dotnet.Project("./src/MyLib/MyLib.csproj"); // Build the project Dotnet.Build(project); // Create NuGet package Nuget.Pack(project, o => o .WithConfiguration(Configuration.Release)); // Push to GitHub Packages var owner = "my-username"; // or organization name Nuget.Push(project, o => o .ToSource($"https://nuget.pkg.github.com/{owner}/index.json") .WithApiKey(Env("GITHUB_TOKEN"))); ``` ## Package with Symbols Include symbols for debugging: ```csharp Nuget.Pack(project, o => o .WithConfiguration(Configuration.Release) .WithSymbols() .WithSource()); Nuget.Push(project, o => o .ToSource($"https://nuget.pkg.github.com/{owner}/index.json") .WithApiKey(Env("GITHUB_TOKEN"))); ``` Note: GitHub Packages doesn't have a separate symbol server, but symbols are included in the package. ## Version Management ### Version from Project File Set the version in your `.csproj`: ```xml net9.0 1.2.3 MyCompany.MyLib My Company A useful library ``` Then use it in your build: ```csharp var project = Dotnet.Project("./src/MyLib/MyLib.csproj"); var version = project.Version; Log.Info($"Building {project.Name} v{version}"); Dotnet.Build(project); Nuget.Pack(project); Nuget.Push(project, o => o .ToSource($"https://nuget.pkg.github.com/{owner}/index.json") .WithApiKey(Env("GITHUB_TOKEN"))); ``` ### Pre-release Versions Create pre-release packages with version suffixes: ```csharp // For CI builds, append build number var buildNumber = Env("BUILD_NUMBER", required: false) ?? "local"; Nuget.Pack(project, o => o .WithConfiguration(Configuration.Release) .WithVersionSuffix($"preview.{buildNumber}")); // Creates: MyLib.1.2.3-preview.42.nupkg ``` ### Override Version Entirely ```csharp var version = "2.0.0-beta.1"; Nuget.Pack(project, o => o .WithConfiguration(Configuration.Release) .WithVersion(version)); ``` ## Conditional Publishing with Profiles Separate build and publish: ```csharp var publish = DefineProfile("publish"); var project = Dotnet.Project("./src/MyLib/MyLib.csproj"); var tests = Dotnet.Project("./tests/MyLib.Tests/MyLib.Tests.csproj"); var owner = "my-organization"; // Always build and test Dotnet.Build(project); Dotnet.Test(tests); // Always create package (for verification) Nuget.Pack(project, o => o .WithConfiguration(Configuration.Release) .Output(Root / "packages")); // Only push with -p publish if (publish) { Nuget.Push(Root / "packages" / "*.nupkg", o => o .ToSource($"https://nuget.pkg.github.com/{owner}/index.json") .WithApiKey(Env("GITHUB_TOKEN")) .SkipDuplicates()); Log.Info($"Published to GitHub Packages: {project.Name} v{project.Version}"); } ``` Usage: ```bash # Build, test, and create package ando # Build, test, create package, and push ando -p publish ``` ## Multi-Project Publishing Publish multiple packages from a solution: ```csharp var publish = DefineProfile("publish"); var owner = "my-organization"; var projects = new[] { Dotnet.Project("./src/MyLib.Core/MyLib.Core.csproj"), Dotnet.Project("./src/MyLib.Extensions/MyLib.Extensions.csproj"), Dotnet.Project("./src/MyLib.Testing/MyLib.Testing.csproj"), }; // Build and test all foreach (var project in projects) { Dotnet.Build(project, o => o.Configuration = Configuration.Release); } Dotnet.Test(Dotnet.Project("./tests/MyLib.Tests/MyLib.Tests.csproj")); // Pack all foreach (var project in projects) { Nuget.Pack(project, o => o .WithConfiguration(Configuration.Release) .Output(Root / "packages")); } // Push all if (publish) { Nuget.Push(Root / "packages" / "*.nupkg", o => o .ToSource($"https://nuget.pkg.github.com/{owner}/index.json") .WithApiKey(Env("GITHUB_TOKEN")) .SkipDuplicates()); } ``` ## GitHub Actions Integration ### Workflow File ```yaml # .github/workflows/nuget.yml name: NuGet Package on: push: branches: [main] tags: ['v*'] pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v4 - name: Install ANDO run: dotnet tool install -g ando - name: Build and test run: ando - name: Publish package if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: ando -p publish ``` ### Package Consumption To consume packages from GitHub Packages, add a `nuget.config` to your project: ```xml ``` Or authenticate via environment: ```bash dotnet nuget add source https://nuget.pkg.github.com/OWNER/index.json \ --name github \ --username USERNAME \ --password $GITHUB_TOKEN ``` ## Dual Publishing: GitHub Packages + NuGet.org Publish to both registries: ```csharp var pushGitHub = DefineProfile("push-github"); var pushNuGet = DefineProfile("push-nuget"); var project = Dotnet.Project("./src/MyLib/MyLib.csproj"); var owner = "my-organization"; Dotnet.Build(project); Dotnet.Test(Dotnet.Project("./tests/MyLib.Tests/MyLib.Tests.csproj")); Nuget.Pack(project, o => o .WithConfiguration(Configuration.Release) .WithSymbols() .Output(Root / "packages")); // Push to GitHub Packages if (pushGitHub) { Nuget.Push(Root / "packages" / "*.nupkg", o => o .ToSource($"https://nuget.pkg.github.com/{owner}/index.json") .WithApiKey(Env("GITHUB_TOKEN")) .SkipDuplicates()); Log.Info("Published to GitHub Packages"); } // Push to NuGet.org if (pushNuGet) { Nuget.EnsureAuthenticated(); Nuget.Push(Root / "packages" / "*.nupkg", o => o .ToNuGetOrg() .SkipDuplicates()); Log.Info("Published to NuGet.org"); } ``` Usage: ```bash # Push to GitHub Packages only ando -p publish-github # Push to NuGet.org only ando -p publish-nuget # Push to both ando -p publish-github,push-nuget ``` ## Full Example: Internal Library Complete build script for an internal library: ```csharp // build.csando var publish = DefineProfile("publish"); var project = Dotnet.Project("./src/MyCompany.Utilities/MyCompany.Utilities.csproj"); var tests = Dotnet.Project("./tests/MyCompany.Utilities.Tests/MyCompany.Utilities.Tests.csproj"); var owner = "my-company"; var version = project.Version; // Install SDK if needed Dotnet.SdkInstall(); // Build Log.Info($"Building {project.Name} v{version}"); Dotnet.Build(project, o => o.Configuration = Configuration.Release); // Test Dotnet.Test(tests); // Pack Nuget.Pack(project, o => o .WithConfiguration(Configuration.Release) .WithSymbols() .WithSource() .Output(Root / "packages")); // Publish if (publish) { var packagePath = Root / "packages" / $"{project.Name}.{version}.nupkg"; Nuget.Push(packagePath, o => o .ToSource($"https://nuget.pkg.github.com/{owner}/index.json") .WithApiKey(Env("GITHUB_TOKEN")) .SkipDuplicates()); Log.Info($"Published {project.Name} v{version} to GitHub Packages"); Log.Info($"Install with: dotnet add package {project.Name} --version {version}"); } // Copy packages to host for inspection Ando.CopyArtifactsToHost("packages", "./packages"); ``` ## Handling Duplicate Versions By default, `SkipDuplicates()` is enabled, which skips publishing if the version already exists. This is useful for idempotent builds. To fail on duplicates (enforce unique versions): ```csharp Nuget.Push(project, o => o .ToSource($"https://nuget.pkg.github.com/{owner}/index.json") .WithApiKey(Env("GITHUB_TOKEN")) .SkipDuplicates(false)); // Will fail if version exists ``` ## Options Reference ### Nuget.Pack Options | Option | Description | |--------|-------------| | `Output(path)` | Output directory for .nupkg | | `WithConfiguration(Configuration)` | Build configuration | | `WithVersion(string)` | Override package version | | `WithVersionSuffix(string)` | Pre-release suffix (e.g., "beta.1") | | `WithSymbols(bool)` | Include .snupkg for debugging | | `WithSource(bool)` | Include source in symbols | | `SkipRestore()` | Skip restore before packing | | `SkipBuild()` | Skip build before packing | ### Nuget.Push Options | Option | Description | |--------|-------------| | `ToSource(string)` | NuGet feed URL | | `ToNuGetOrg()` | Push to nuget.org | | `WithApiKey(string)` | API key for authentication | | `SkipDuplicates(bool)` | Skip if version exists (default: true) | | `WithoutSymbols(bool)` | Don't push .snupkg | ## See Also - [NuGet Provider](/providers/nuget) - NuGet operations reference - [Dotnet Provider](/providers/dotnet) - .NET build operations - [GitHub Releases Recipe](/recipes/github-releases) - Creating GitHub releases