Logo

dev-resources.site

for different kinds of informations.

Deploying NuGet packages with Docker in GitHub actions

Published at
2/1/2023
Categories
nuget
dotnet
github
docker
Author
lukepatterson31
Categories
4 categories in total
nuget
open
dotnet
open
github
open
docker
open
Author
15 person written this
lukepatterson31
open
Deploying NuGet packages with Docker in GitHub actions

As part of a POC to migrate one of our services from Azure DevOps Pipelines, I had to package and deploy some NuGet library dependencies to a private feed with GitHub Actions.

In an effort to reduce deployment time our DevOps engineers wanted to remove any unnecessary steps by having all tools and software requirements installed on our runners.

This presented us with a challenge as we still needed the flexibility of using different .NET SDK's and runtimes but didn't necessarily want them all installed on the runners.

The solution we found was to use custom Dockerfiles to build, package and deploy our libraries and a Docker image of GitVersion for semantic versioning of the packages.

The library's Dockerfile:

FROM mcr.microsoft.com/dotnet/sdk:latest

ARG REPO_DIR
ARG BUILD_CONFIG
ARG PACKAGE_VERSION

COPY ./$REPO_DIR /$REPO_DIR

WORKDIR /$REPO_DIR
RUN dotnet restore
RUN dotnet build --no-restore --configuration $BUILD_CONFIG
RUN dotnet test
RUN mkdir /packages
RUN dotnet pack --configuration $BUILD_CONFIG /p:Version=$PACKAGE_VERSION --no-build --output /packages;
RUN --mount=type=secret,id=key source /run/secrets/key \
    && dotnet nuget push "/packages/*.nupkg" -s "https://nuget-feed-url.com/nuget/v3" -k "feed-key"
Enter fullscreen mode Exit fullscreen mode

The Dockerfile is fairly straightforward: We restore, build, test and pack the library, we mount the secret .env file, which contains our private feed's API key, and finally push the packages to the feed.

The library's workflow:

name: Call reusable Docker package deployment workflow
on:
  workflow_dispatch:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  call-pack-and-deploy-workflow:
    uses: lukepatterson31/github-actions/.github/workflows/docker-pack-and-deploy.yml@main
    with:
      prerelease: ${{ github.REF != 'refs/heads/main' && github.event_name == 'workflow_dispatch' }}
      build-configuration: 'Release'
      repo-dir: './src'
      docker-tag: 'MyLibrary'

    secrets: inherit
Enter fullscreen mode Exit fullscreen mode

The library's workflow passes the various inputs to the re-useable package and deploy workflow and calls it.

The re-useable package and deploy workflow:

We check out our repository

name: Deploy Packages in Docker Reusable Workflow
on:
  workflow_call:
    inputs:
      prerelease:
        required: true
        type: string
      build-configuration:
        required: true
        type: string
      repo-dir:
        required: false
        type: string
      docker-tag:
        required: true
        type: string

jobs: 
  build:
    runs-on: [ self-hosted, RunnerName ]
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0  
Enter fullscreen mode Exit fullscreen mode

We generate a release or pre-release version number by running the GitVersion tool from a Docker container and extract the version variable we want with awk.


      - name: Set release version
        if: ${{ success() && inputs.prerelease == 'false' }}
        run: |
          echo "package_version=$(docker run --rm -v "$(pwd):/repo" gittools/gitversion:6.0.0-fedora.33-7.0 /repo | awk '/"SemVer/ {gsub(/"|",/,""); print$2}' )" >> $GITHUB_ENV            

      - name: Set pre-release version
        if: ${{ success() && inputs.prerelease == 'true' }}
        run: |
          echo "package_version=$(docker run --rm -v "$(pwd):/repo" gittools/gitversion:6.0.0-fedora.33-7.0 /repo | awk '/"SemVer/ {gsub(/"|",/,""); print$2}' )-pre" >> $GITHUB_ENV

Enter fullscreen mode Exit fullscreen mode

We create the .env file containing the NuGet feed API key

      - name: Create env file
        run: |
          echo "FEED_KEY={{ secrets.feed_key }}" > .env
Enter fullscreen mode Exit fullscreen mode

We build the Docker image with the BuildKit enabled, allowing us to mount the .env file as a secret. After the build command is finished we remove the .env file.


      - name: Build Dockerfile
        if: ${{ success() }}       
        run: |
          DOCKER_BUILDKIT=1 docker build -t ${{ inputs.docker-tag }} -f ./docker/Dockerfile \
          --secret id=key,src=.env \
          --build-arg REPO_DIR=${{ inputs.repo-dir }} \
          --build-arg BUILD_CONFIG=${{ inputs.build-configuration }} \
          --build-arg PACKAGE_VERSION=${{ env.package_version }} \
          --no-cache .
          rm -f .env
Enter fullscreen mode Exit fullscreen mode

We use docker create to execute the package and deploy steps without starting a container as we don't need it to run, then we remove the stopped container.

      - name: Package nuget files
        if: ${{ success() }}
        run: |
          docker create --name pack ${{ inputs.docker-tag }}
          if (( $(docker container ls -a | grep -c 'pack') > 0 )); then docker container rm pack; fi
Enter fullscreen mode Exit fullscreen mode

Finally we tag and push the new library release to the repository.

      - name: Tag and push
        if: ${{ success() && inputs.prerelease == 'false' }}
        run: |
          git tag v${{ env.package_version }} ${{ github.sha }}
          git push origin v${{ env.package_version }}
Enter fullscreen mode Exit fullscreen mode

Here's the whole workflow:

name: Deploy Packages in Docker Reusable Workflow
on:
  workflow_call:
    inputs:
      prerelease:
        required: true
        type: string
      build-configuration:
        required: true
        type: string
      repo-dir:
        required: false
        type: string
      docker-tag:
        required: true
        type: string

jobs: 
  build:
    runs-on: [ self-hosted, RunnerName ]
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0    

      - name: Set release version
        if: ${{ success() && inputs.prerelease == 'false' }}
        run: |
          echo "package_version=$(docker run --rm -v "$(pwd):/repo" gittools/gitversion:6.0.0-fedora.33-7.0 /repo | awk '/"SemVer/ {gsub(/"|",/,""); print$2}' )" >> $GITHUB_ENV            

      - name: Set pre-release version
        if: ${{ success() && inputs.prerelease == 'true' }}
        run: |
          echo "package_version=$(docker run --rm -v "$(pwd):/repo" gittools/gitversion:6.0.0-fedora.33-7.0 /repo | awk '/"SemVer/ {gsub(/"|",/,""); print$2}' )-pre" >> $GITHUB_ENV

      - name: Create env file
        run: |
          echo "FEED_KEY={{ secrets.feed_key }}" > .env

      - name: Build Dockerfile
        if: ${{ success() }}       
        run: |
          DOCKER_BUILDKIT=1 docker build -t ${{ inputs.docker-tag }} -f ./docker/Dockerfile \
          --secret id=key,src=.env \
          --build-arg REPO_DIR=${{ inputs.repo-dir }} \
          --build-arg BUILD_CONFIG=${{ inputs.build-configuration }} \
          --build-arg PACKAGE_VERSION=${{ env.package_version }} \
          --no-cache .
          rm -f .env

      - name: Package nuget files
        if: ${{ success() }}
        run: |
          docker create --name pack ${{ inputs.docker-tag }}
          if (( $(docker container ls -a | grep -c 'pack') > 0 )); then docker container rm pack; fi

      - name: Tag and push
        if: ${{ success() && inputs.prerelease == 'false' }}
        run: |
          git tag v${{ env.package_version }} ${{ github.sha }}
          git push origin v${{ env.package_version }}

Enter fullscreen mode Exit fullscreen mode
nuget Article's
30 articles in total
Favicon
Simplifying Dependency Management with NuGet Central Package Management
Favicon
Failed to access Nuget in China
Favicon
Seu Primeiro Pacote NuGet: Um Passo a Passo Prático
Favicon
Central Package Management in .NET - Simplify NuGet Dependencies
Favicon
Custom NET8 Entity Framework Core Generic Repository
Favicon
How to publish a package on Nuget.org
Favicon
Automate Your C# Library Deployment: Publishing to NuGet and GitHub Packages with GitHub Actions
Favicon
My First NuGet Package: EmojiToText
Favicon
Publish C# Project to Nuget.org
Favicon
My Very First NuGet package
Favicon
Introducing VirtualStorageLibrary: A .NET Solution for In-Memory Tree Structures
Favicon
C# | Create Nuget Package using .NET Standard
Favicon
GitHub | Deploy .net core NuGet Packages in GitHub Packages Registry
Favicon
CSV Schema Validation
Favicon
Publishing Nuget in GitHub Packages
Favicon
.NET MAUI: Update NuGet Packages using Visual Studio Code
Favicon
How I Built a NuGet Package
Favicon
Lee's opinions on Umbraco + naming things
Favicon
Migrating the XM Cloud Introduction Repo to a new Nuget feed.
Favicon
How This NuGet Package Almost Cost Me My Job
Favicon
How to create and deploy a “Nuget Package” using Visual Studio
Favicon
How to change default Nuget packages folder on Windows
Favicon
How to Make a NuGet Package for C++ Development in Visual Studio
Favicon
Data Validation Nuget Package
Favicon
Create a NuGet-Package
Favicon
AspNetCore.VersionInfo 1.1.0 is out
Favicon
Deploying NuGet packages with Docker in GitHub actions
Favicon
Introducing TF WhatsUp, a Tool for Better Terraform Notes
Favicon
Creating a Nuget Package From a .Net 6 class library
Favicon
Elasticsearch.Net vs NEST

Featured ones: