Generate a .NET Core NuGet Package with GitHub Packages and AWS Code Build

About

GitHub Packages is a convenient way to store and serve your NuGet packages. There are a couple of reasons why you might want to consider using GitHub Packages:

  • You need to share NuGet packages with an extended team and need to do so securely.
  • You have an antiquated on premise NuGet repository, which limits the ability to share with developers outside of your firewall.
  • You do not use Azure DevOps (with built-in NuGet hosting) and need a place to share common code.

If this sounds like you, GitHub Packages may be what you’re looking for. With this guide, you will be able to:

  • Generate a .NET Core class library project as a NuGet package using AWS CodePipeline/CodeBuild
  • Automatically push the NuGet package into GitHub Packages
  • Setup your local environment to consume the GitHub NuGet package
  • Configure CodeBuild on the consuming project to access the GitHub NuGet package at build-time

What you will need

  • Access to setup a new AWS CodePipeline
  • Access to AWS Secrets Manager in order to create a new secret, or access to an existing GitHub access token secret.
  • VSCode or similar environment

Configure a NuGet Project for Deployment to GitHub Packages

To configure a project for NuGet hosting in GitHub Packages, follow these steps. It’s assumed that you have a .NET Core classlib project already committed to Github.

Setup a Buildspec

First, setup a buildspec.yaml file in the root project folder. The buildspec completes the following tasks:

  • Requests access to secrets from the secret manager. Secret syntax: <secret name>:<json key>
  • Writes a nuget.config file to the source directory, with the GitHub token, username, and url from the secret. Note that dynamic creation of the file was setup for the following reasons:
  • Removes the possibility of a dev committing personal access token secrets in the file inadvertently.
  • Allows for adding automated GitHub token rotation via secret manager rotation settings (would require authoring a custom lambda).
  • Runs any unit tests found in the solution. If you project has no tests (shame), you can remove this line.
  • Runs the command to create a NuGet package.
  • Runs the command to push the package to GitHub Packages.
version: 0.2
# Buildspec Reference Doc: https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#build-spec-ref-syntax

#################################
# Runtime Environment Variables #
#################################
env:
  secrets-manager:
    # By default, CodeBuild creates a new IAM role with permissions that provide read access to any SSM param or Secret prefixed with: CodeBuild/
    GITHUB_ACCESS_TOKEN: CodeBuild/github/token:TOKEN
    GITHUB_USERNAME: CodeBuild/github/token:USERNAME
    GITHUB_PACKAGE_URL: CodeBuild/github/token:URL

################
# Build Phases #
################
phases:
  pre_build:
    commands:
      # Write a nuget.config file with secrets, must be done before tests run.
      - echo "<?xml version=\"1.0\" encoding=\"utf-8\"?><configuration><packageSources><clear /><add key=\"github\" value=\"$GITHUB_PACKAGE_URL\" /></packageSources><packageSourceCredentials><github><add key=\"Username\" value=\"$GITHUB_USERNAME\" /><add key=\"ClearTextPassword\" value=\"$GITHUB_ACCESS_TOKEN\" /></github></packageSourceCredentials></configuration>" | tee ./src/PackageTest1/nuget.config
      - dotnet test

  build:
    commands:
      # Build the nuget package
      - dotnet pack --configuration Release

  post_build:
    commands:
      # Publish the nuget package. Note that this command will fail if it tries to overwrite an existing version. 
      - cd ./src/PackageTest1
      - dotnet nuget push ./bin/Release/*.nupkg --source "github"

Notes:

  • If the secret names are different, they can be updated in the buildspec file env section, or set as part of CodeBuild Environment configuration.
  • You will need to update the paths to match your project.
  • The echo command under pre_build has a path hiding out at the end of the line.
  • Two or more projects can be setup to store NuGet packages in the same GitHub repo. Just use the same RepositoryUrl url.

Once all of this section is complete, commit the changes to the repository.

Creating new Versions of the NuGet Package

Simply commit the updates to GitHub , and the CodeBuild project will build and push the new version to GitHub Packages. There is only one requirement: before committing updates to package source to GitHub, you will need to manually increment the <Version> number. Otherwise the build will fail.

Create AWS CodePipeline for Auto-Deployment to GitHub Packages

The following section focuses on setting up the CI/CD pipeline in AWS CodePipeline/CodeBuild.

Setup GitHub Secrets

If you do not already have one, the first thing to do is setup a new GitHub personal access token and configure new secrets in the AWS Secret Manager.

Recommendation: Do not use your personal GitHub user account for a “production” package. Create and use a generic GitHub Package user account as a ‘service’ account.

  1. Create a new Personal Access token in the GitHub account of choice. Name it AWS CodeBuild or something that ties it to it’s purpose. Hang on to the token that’s generated.
  2. In AWS Secrets Manager, create a new secret:
    1. Choose “Other type of secrets”
    2. Add the following three key/value pairs
      1. TOKEN: Personal GitHub access token.
      2. USERNAME: Username associated with the token
      3. URL: The full URL to your GitHub packages, e.g. https://nuget.pkg.github.com/{YOUR ORGANIZATION}/index.json
    3. Choose the default encryption key, click Next
    4. Secret name: CodeBuild/github/token
      1. “CodeBuild” is important, the secret will not be accessible automatically to the CodeBuild project if not included in the name.
    5. Click Next
    6. Pick “disable automatic rotation” and click, Next, then click Store

Create a new CodePipeline

Setup a new code pipeline that will use the buildspec created above to compile, test and publish the NuGet package to GitHub Packages.

  1. Create a new code pipeline
    1. Choose “New Service Role”
    2. Ensure that “Allow AWS CodePipeline to create service role…” is checked.
    3. Default Advanced Settings are fine.
  2.  Setup the GitHub source
    1. Connect to GitHub using the “service” account (see Setup GitHub Secrets above).
    2. Choose GitHub webhooks or AWS CodePipeline as appropriate.
    3. Pick the project repo and branch, click Next
  3. Setup the build provider
    1. Pick AWS CodeBuild
    2. Select the region
    3. Click Create a new project
    4. Setting up the new CodeBuild project
      1. Pick Managed Instance
      2. Choose Ubuntu → Standard → Standard:4.0
      3. Always use latest
      4. Environment type: Linux
      5. Pick New Service Role, rename as needed
      6. Under “Additional Settings”: Add the three environment variables with the type set to “Secret Manager”.  This step is required if you have chosen to have CodeBuild generate the role and manage permissions. Otherwise, you will need to grant access to the assigned role manually.
        1. GITHUB_ACCESS_TOKEN: CodeBuild/github/token:TOKEN
        2. GITHUB_USERNAME: CodeBuild/github/token:USERNAME
        3. GITHUB_PACKAGE_URL: CodeBuild/github/token:URL
      7. Make sure “use a buildspec file” is selected
      8. Enable CloudWatch logs (without this, you will not see build console output)
      9. Click “Continue to CodePipeline”
    5. Click “Next”
  4. Skip the deploy stage (this is handled by the NuGet push command in the buildspec).
  5. Click Create Code Pipeline.

This will automatically run the new CodeBuild project.

How To Consume a NuGet Package From GitHub Packages

Now that the NuGet package is stored in GitHub Packages, projects can now consume that package. As it is a private NuGet host that requires authentication, some additional items will need to be added to your consuming project.

Setup for Local Development

  1. In the root of your .csproj (not the solution root), create a new nuget.config file.
  2. In GitHub, go to Developer Settings → Personal Access Tokens and generate a new one specific to NuGet packages stored in GitHub Packages.
  3. Add the following to the nuget.config file, replacing the GitHub information as appropriate:
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <packageSources>
            <clear />
            <add key="github" value="https://nuget.pkg.github.com/{YOUR ORGANIZATION}/index.json" />
        </packageSources>
        <packageSourceCredentials>
            <github>
                <add key="Username" value="your-github-username" />
                <add key="ClearTextPassword" value="12******************************34" />
            </github>
        </packageSourceCredentials>
    </configuration>

Important! Do not commit the nuget.config file to source control. Recommend adding it to the root .gitignore.

Once the above is configured, you can use NuGet add package PackageTest1 to add the package to your project.

Setup for CodeBuild

In order to get access to the NuGet files in CodeBuild, the project’s buildspec will need to include the same bits found in the Setup a Buildspec section above.

Reference Material