Bitbucket Pipelines: Custom NuGet Packages with AWS CodeArtifact

Bitbucket Pipelines accelerate .NET Builds

Introduction

If you are a .NET shop, it is likely that, at some point, your codebase and team will grow to a size where it becomes beneficial to publish and consume custom NuGet packages. Additionally, in many cases, it is likely you need to host these NuGet packages in a private artifact repository.

In this post, we will look at using Bitbucket Pipelines to publish NuGet packages to, and consume NuGet packages from, AWS CodeArtifact.

Bitbucket Pipelines is an excellent CI/CD tool that allows users of Bitbucket to automate the Continuous Integration and Continuous Delivery of their software.

AWS CodeArtifact very capable managed artifact repository service that can be used to publish and consume software packages for application development.

Publishing Custom NuGet Packages

Details involving the creation of the .NET class libraries and applications is beyond the scope of this post. We can create a new class library from the command line with the command dotnet new classlib, and we can create a simple application for consuming the library with dotnet new console.

One item to note is that the pipelines in this post assume that the GeneratePackageOnBuild property is set to true for the class libraries, as seen below:

<Project Sdk=”Microsoft.NET.Sdk”>
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
  </PropertyGroup>
</Project>

Once we’ve created a class library, we’ll need to create both a domain and a repository in AWS CodeArtifact.

Next, we’ll need to introduce a bitbucket-pipelines.yml file for the Git repository that houses our library (or libraries). An example follows.

image: mcr.microsoft.com/dotnet/sdk:6.0

publishPackages: &publishPackages
step:
    name: Publish packages to AWS CodeArtifact
    caches:
      – dotnetcore
      – apt-cache
    script:
      # Install Zip, required to unzip the AWS CLI installer.
      – apt-get update
      – apt-get install –yes –force-yes zip
      # Install the AWS CLI
      – curl “https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip” -o “awscliv2.zip”
      – unzip awscliv2.zip
      – ./aws/install
      – aws –version
      # Add the AWS CodeArtifact NuGet repo
      – aws codeartifact login –tool dotnet –domain $AWS_CODE_ARTIFACT_DOMAIN –repository $AWS_CODE_ARTIFACT_REPO
      # Build packages using git tag as the version
      – dotnet –version
      – pkg_version=`git describe –always HEAD`
      – dotnet build -c release /p:Version=$pkg_version -o publish_output
      # Push the NuGet packages to AWS CodeArtifact
      – find publish_output -type f -name “*.nupkg” -exec dotnet nuget push “{}” –source $AWS_CODE_ARTIFACT_DOMAIN/$AWS_CODE_ARTIFACT_REPO \;

pipelines:
  tags:
    ‘*.*.*’: # Trigger based on a tagged version being pushed
      – <<: *publishPackages
definitions:
  caches:
    # See: https://support.atlassian.com/bitbucket-cloud/docs/cache-dependencies/
    apt-cache: /var/cache/apt

Note that we’re installing the zip package with Apt, and we’re installing the AWS CLI. We are doing this within the pipeline for simplicy in this post. If you’d like to use a custom Docker image that has these build dependencies already provided, refer to our post Bitbucket Pipelines: Custom Docker Image w/ Amazon Elastic Container Service.

We’re also using YAML anchors to introduce re-usable pipeline sections.

In order to publish the libraries to AWS CodeArtifact:

  • We run aws codeartifact login to make CodeArtifact available as a NuGet repository
  • We run dotnet build, passing in the Git tag as the version to use when building the libraries
  • Finally, we run the find command for files matching *.nupkg, and execute dotnet nuget push on the results

To make use of this bitbucket-pipelines.yml file, we’ll need to enable Bitbucket Pipelines for the repostory, and provide values for the following pipeline repository variables:

  • AWS_DEFAULT_REGION: the region where the AWS CodeArtifact domain and repository are located
  • AWS_ACCESS_KEY_ID: the access key ID for a service account with permissions to publish artifacts
  • AWS_SECRET_ACCESS_KEY (secured): the secret access key for the service account
  • AWS_CODE_ARTIFACT_DOMAIN: the name of the AWS CodeArtifact domain
  • AWS_CODE_ARTIFACT_REPO: the name of the AWS CodeArtifact repository

With this configuration in place, pushing a Git tag with a version — such as 1.0.0 — will build the class libraries with that version, and push the NuGet packages to AWS CodeArtifact.

Consuming Custom NuGet Packages

Once we have published the NuGet packages to AWS CodeArtifact, we are ready to consume them in our .NET applications.

Local

There are several ways we can consume these packages locally — for instance, using the aws codeartifact login command:

aws codeartifact login –tool dotnet –domain <my-domain> –repository <my-repsitory> –profile <my-profile>

This command will update our user-level NuGet configuration with a new entry for our AWS CodeArtifact NuGet package source.

Bitbucket Pipelines

To consume these packages from Bitbucket Pipelines, we’ll introduce a bitbucket-pipelines.yml file for the Git repository that houses our .NET application.

image: mcr.microsoft.com/dotnet/sdk:6.0

# Use YAML Anchors to define common steps for re-use in pipelines.
# See: https://community.atlassian.com/t5/Bitbucket-questions/How-to-reuse-steps-for-different-branches/qaq-p/875615
buildAndTest: &buildAndTest
  step:
    name: Publish packages to AWS CodeArtifact
    caches:
      – dotnetcore
      – apt-cache
    script:
      # Install Zip, required to unzip the AWS CLI installer.
      – apt-get update
      – apt-get install –yes –force-yes zip
      # Install the AWS CLI
      – curl “https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip” -o “awscliv2.zip”
      – unzip awscliv2.zip
      – ./aws/install
      – aws –version
      # Add the AWS CodeArtifact NuGet repo
      – aws codeartifact login –tool dotnet –domain $AWS_CODE_ARTIFACT_DOMAIN –repository $AWS_CODE_ARTIFACT_REPO
      # Build the application and run unit tests
      – dotnet –version
      – dotnet restore
      – dotnet build –no-restore –configuration Release
      – dotnet test

pipelines:
  branches:
    master:
      – <<: *buildAndTest
    develop:
      – <<: *buildAndTest
  pull-requests:
    ‘**’:
      – <<: *buildAndTest
definitions:
  caches:
    # See: https://support.atlassian.com/bitbucket-cloud/docs/cache-dependencies/
    apt-cache: /var/cache/apt

The considerations noted previously regarding the bitbucket-pipelines.yml file for the class libraries broadly apply here as well:

  • We’re manually installing depenencies; to do this with a custom Docker image, refer to our post Bitbucket Pipelines: Custom Docker Image w/ Amazon Elastic Container Service
  • We’re using YAML anchors to organize pipeline sections
  • The same steps must be taken to enable and configure Bitbucket Pipelines, including providing values for the following repository variables: AWS_DEFAULT_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_CODE_ARTIFACT_DOMAIN, and AWS_CODE_ARTIFACT_REPO

With this configuration in place, merging code into the master or develop branches, or updating a pull request, will trigger a build & test process that is able to resolve private NuGet dependencies using the AWS CodeArtifact repository.

Conclusion

If you’ve followed along with this post, we now have custom class libraries that are versioned using Git tags, and automatically pushed as NuGet packages to AWS CodeArtifact.

In addition, we have commands we can run locally to consume these packages, and pipelines we can use with Bitbucket Pipelines to consume these packages during Continuous Integration and Continuous Delivery.

In future posts, we’ll look at related topics surrounding the automation of CI/CD with Bitbucket Pipelines and .NET Core code, such as:

Watch this space!

Author: Nathanial Woolls, IDMWORKS, IAM Architect