Skip to content

A repo to show you how to use a private NuGet feed to restore packages in Azure DevOps, GitHub Actions, GitLab CI and AppCenter.

License

Notifications You must be signed in to change notification settings

LanceMcCarthy/DevOpsExamples

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DevOps - Pipeline and Workflow Examples

This repository contains a rich set of CI-CD demos where I show you how to:

  • Connect to private nuget feeds; Azure, GitHub packages, and custom (eg Telerik).
  • Build .NET apps and publish to a container registry; Docker, Azure, GitHub, etc.

Although I use Telerik's NuGet server because I have a license, these demos are good for any private feed type; just use your source URL and credentials instead!

Table of Contents

CI Systems

System CI/CD file(s)
GitHub Actions .github/workflows
Azure DevOps (YAML) azure-pipelines.yml
Azure DevOps (classic) click build badge
GitLab CI/CD .gitlab-ci.yml

Badges

Project GitHub Actions Azure DevOps GitLab CI
.NET MAUI MAUI main Build - CLASSIC
ASP.NET Core Build ASP.NET Core Application Build - CLASSIC
ASP.NET Blazor Build Blazor Application Build - CLASSIC Build status
WPF (net48) WPF (.NET Framework) Build - CLASSIC Build status
WinForms (net48) WinForms (.NET Framework) Build - CLASSIC
Console Console (.NET) Build - YAML Build status
WinUI 3 Build WinUI3 Project
Kendo Angular Build Angular Build - CLASSIC Build status
ASP.NET AJAX (net48) Build AJAX Application Build CLASSIC

Bonus Notes

  • Docker and DockerHub integration:
    • The workflows/main_build-aspnetcore.yml uses a Dockerfile to build and publish a Linux image to DockerHub => lancemccarthy/myaspnetcoreapp.
      • Ex. docker run -d -p 8080:8080 lancemccarthy/myaspnetcoreapp:latest
      • Ex. docker run -d -p 8080:8080 lancemccarthy/myblazorapp:latest
    • For a real-world example, visit Akeyless Web Target's docker-publish.yml, which builds and publishes the lancemccarthy/secretsmocker container image to Docker Hub.
      • Ex. docker run -d -p 8080:80 lancemccarthy/secretsmocker:latest
  • Azure DevOps: All statuses are for classic pipelines, except the Console project, which uses Azure DevOps YAML pipelines.

Videos

Azure DevOps with Telerik NuGet Server

The following 4 minute video takes you though all the steps on adding a private NuGet feed as a Service Connection and consuming that service in three different pipeline setups.

YouTube tutorial

  • 0:09 Add a Service connection to the Telerik server
  • 1:14 Classic pipeline for .NET Core
  • 1:47 Classic .NET Framework pipeline
  • 2:25 YAML pipeline setup for .NET Core

Tips and Troubleshooting

GitHub Actions: Using Secrets to Set Environment Variables

A common problem to run into is to think that the environment variable is the same thing as the GitHub Secret (or Azure DevOps pipeline variable). In this demo, I intentionally named the secrets a different name than the environment variable name so that it is easier for you to tell the difference.

However, I know that not everyone has the time to watch the video and just copy/paste the YAML instead. This will cause you to hit a roadblock because you missed the part about setting up the GitHub secret, Azure DevOps pipeline variable or . Here is a 2 screenshot crash-course on how to get back on track.

In your YAML, you probably have done this:

image

That mean you must also have the secrets in your Settings > Secrets list

image

Powershell: Adding or Updating Package Source Dynamically

Option 1 - Update existing package source

You could also dynamically update the credentials of a Package Source defined in your nuget.config file This is a good option when you do not want to use a packageSourceCredentials section that uses environment variables.

# Updates a source named 'Telerik' in the nuget.config
dotnet nuget update source "Telerik" -s "https://nuget.telerik.com/v3/index.json" --configfile "src/nuget.config" -u '${{secrets.MyTelerikEmail}}' -p '${{secrets.MyTelerikPassword}}' --store-password-in-clear-text

That command will look through the nuget.config for a package source with the key Telerik and then add/update the credentials for that source.

Option 2 - Add a new package source

The other approach is a bit simpler because you dont need a custom nuget.config file. Just use the dotnet nuget add source command

dotnet nuget add source 'https://nuget.telerik.com/v3/index.json' -n "AddedTelerikServer" -u ${{secrets.MyTelerikEmail}} -p ${{secrets.MyTelerikPassword}} --store-password-in-clear-text

The --store-password-in-clear-text switch is important. It does not mean the password is visible, rather it means that you're using the password text and not a custom encrypted variant. For more information, please visit https://docs.microsoft.com/en-us/nuget/reference/nuget-config-file#packagesourcecredentials

Using Telerik NuGet Keys

You can use the same approach in the previous section. Everything is exactly the same, except you use api-key for the username and the NuGet key for the password.

Please visit the Announcing NuGet Keys blog post for more details how ot create the key and how to use it.

dotnet nuget update source "Telerik" --source "https://nuget.telerik.com/v3/index.json" --configfile "src/nuget.config" --username 'api-key' --password '${{ secrets.MyNuGetKey }}' --store-password-in-clear-text

IMPORTANT: Protect your key by storing it in a GitHub Secret, then use the secret's varible name in the command

Dockerfile: Using Secrets

When using a Dockerfile to build a .NET project that uses the Telerik NuGet server, you'll need a safe and secure way to handle your NuGet crednetials and your Telerik License Key. This can be done my mounting a Docker secret.

In your GitHub Actions workflow, you can define and set docker secrets in the docker build step. Take a look at the following example, we using GitHub secrest to set two docker secrets telerik-nuget-key=${{secrets.MY_NUGET_KEY}} and telerik-license-key=${{secrets.MY_TELERIK_LICENSE_KEY}}.

    - uses: docker/build-push-action@v3
      with:
        secrets: |
          telerik-nuget-key=${{secrets.MY_NUGET_KEY}}
          telerik-license-key=${{secrets.MY_TELERIK_LICENSE_KEY}}

Now, inside the Dockerfile's build stage, you can mount and use those secrets. See Stage #2 in this example

### STAGE 1 ###
# Create our base image from the aspnet image, and prep any neccessary settings
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
WORKDIR /app

### STAGE 2 ###
# Compile the project and create production-ready artifacts
# ⚠️important⚠️ only use these secrests in the build stage so you dont leak them in your final image
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src/MyApp
COPY . .

# Step 1 - Restore NuGet packages
# In this command, we do three things
#   1. Mount the telerik-nuget-key secret
#   2. Add the Telerik NuGet server to the package sources (usign the telerik-nuget-key for password)
RUN --mount=type=secret,id=telerik-nuget-key \
    dotnet nuget add source 'https://nuget.telerik.com/v3/index.json' -n "TelerikNuGetServer" -u "api-key" -p $(cat /run/secrets/telerik-nuget-key) --store-password-in-clear-text

# 2. Restore NuGet packages
RUN dotnet restore "MyApp.csproj"

# Step 3 - Build the project
# In this command, we do two things:
#   1. We set the environment var 'TELERIK_LICENSE' using the 'telerik-license-key' docker secret
#   2. Run the dotnet publish command, which compiles and  the project in release mode
RUN --mount=type=secret,id=telerik-license-key,env=TELERIK_LICENSE \
    dotnet publish "MyApp.csproj" -o /app/publish /p:UseAppHost=false --self-contained false


### STAGE 3 ###
# Build the final image from the base, but copy the pubilsh artifacts from the intermediate stage
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyBlazorApp.dll"]

Important: Do not set the variables or license file in the base or final stage, you'll leak your secrets in the final image. Please visit the complete Dockerfile and the workflow.