Skip to content

LanceMcCarthy/DevOpsExamples

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,144 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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 Build status
ASP.NET Core Build ASP.NET Core Application Build Status Build status
ASP.NET Blazor Build Blazor Application Build Status Build status
WPF (net48) WPF (.NET Framework) Build - CLASSIC Build status
WinForms (net48) WinForms (.NET Framework) Build - CLASSIC Build status
Console Console (.NET) Build Status AKEYLESS Build status
WinUI 3 Build WinUI3 Project Build Status Build status
Kendo Angular Build Angular Build Status Build status
ASP.NET AJAX (net48) Build AJAX Application Build Status Build status

All Azure DevOps status badges are for classic pipelines, except the Console project, which uses Azure DevOps YAML pipelines.

Docker Examples

This repo also contains examples on how to build and publish a Telerik powered .NET project as a container image. The image names below are published to the lancemccarthy Docker Hub user, but the approach also works for any container registry.

Image GitHub Action Dockerfile
aspnetcore-reporting-from-centosbase link
aspnetcore-reporting-from-msftbase link
myblazorapp link

Note

When creating a container, forward port 8080 to the host. For example:

  1. Run docker run -d -p 9999:8080 lancemccarthy/myblazorapp:latest
  2. Visit site on http://localhost:9999

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

Important

The recording has some outdated information, take the following updates into consideration when watching:

  • Use the v3 server address https://nuget.telerik.com/v3/index.json
  • Use an API key credential if your telerik.com account is SSO (see Announcing NuGet Keys).

Tips and Troubleshooting

GitHub Actions: Using Secrets to Set Environment Variables

If you have environment variable placeholders in your nuget.config file, you can easily set them using GitHub Secrets. For example, let's say in your packageSourceCredentials, you have the following the environment variable placeholders %TELERIK_USERNAME% and %TELERIK_PASSWORD%

<?xml version="1.0" encoding="utf-8"?>
<configuration>
...
  <packageSourceCredentials>
    <Telerik_v3_Feed>
      <add key="Username" value="%TELERIK_USERNAME%" />
      <add key="ClearTextPassword" value="%TELERIK_PASSWORD%" />
    </Telerik_v3_Feed>
  </packageSourceCredentials>
  ...
</configuration>

You can directly set those vars on the same step which you invoke the dotnet restore/build/publish command. For example, here I use an API key from my GitHub Actions Secrets for credentials

    - name: Restore NuGet Packages
      run: dotnet restore src/MyProject.csproj --configfile src/nuget.config
      env:
        TELERIK_USERNAME: "api-key"
        TELERIK_PASSWORD: ${{secrets.TELERIK_API_KEY}}

Tip

This is also very useful for Dependabot runs. You can set a Dependabot secret (in the repo settings) and it will be able to restore packages during checks that were triggered by Dependabot.

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.

# Setting credentials for the 'Telerik_v3_Feed' defined in the nuget.config file.
dotnet nuget update source "Telerik_v3_Feed" -s "https://nuget.telerik.com/v3/index.json" -u '${{secrets.MyTelerikEmail}}' -p '${{secrets.MyTelerikPassword}}' --configfile "src/nuget.config" --store-password-in-clear-text

That command will look through the nuget.config for a package source with the key Telerik_v3_Feed 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_v3_Feed" -s "https://nuget.telerik.com/v3/index.json" -u 'api-key' -p '${{secrets.MyNuGetKey}}' --configfile "src/nuget.config" --store-password-in-clear-text

Caution

Protect your key by storing it in a GitHub Secret, then use the secret's ID 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. In the following example, notice how we are setting two docker secrets (nuget-sec and license-sec) using the values from GitHub secrets.

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

Now, inside the Dockerfile, you can mount and use those secrets. See Stage 2 in the following example:

### STAGE 1 ###
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app

### STAGE 2 ###
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
# STEP 1. Mount the 'nuget-sec' secret, then:
# a. add the Telerik package source
# b. restore packages
RUN --mount=type=secret,id=nuget-sec,required \
    dotnet nuget add source 'https://nuget.telerik.com/v3/index.json' -n "Telerik_v3_Feed" -u "api-key" -p "$(cat /run/secrets/nuget-sec)" --store-password-in-clear-text \
    && \
    dotnet restore "MyBlazorApp.csproj"
# STEP 2. Mount the "license-sec" secret, then:
# a. create the license file
# b. build the project
# c. delete the file so you don't distribute it in your image (important!)
RUN --mount=type=secret,id=license-key,required \
    mkdir -p ~/.telerik  && echo "$(cat /run/secrets/license-sec)" > ~/.telerik/telerik-license.txt \
    && \
    dotnet publish "Researcher.Web/Researcher.Web.csproj" -o /app/publish /p:UseAppHost=false --no-restore --self-contained false \
    && \
    rm -rf ~/.telerik

### STAGE 3 ###
# Build final from base, but copy ONLY THE PUBLISH ARTIFACTS from stage 2
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyBlazorApp.dll"]

Caution

Pay attention to whether or not you are including any secrets in your final image. You can run your container to explore the files (and env vars in Exec) to make sure.

Telerik License Approaches

Depending on how you're building our code, there are several ways to introduce the Telerik License Key at the right time for the build. Let me show you two; variable and file.

Approach 1 - Using a Variable

This is by far the easiest and safest way. You can use a secret (GitHub Action secret or AzDO Variable secret) and set the TELERIK_LICENSE environment variable before the project is compiled.

In a YAML workflow/pipeline, you can set the environment variable at the beginning of the job or on a step that needs it.

GH Actions

  - run: dotnet publish MyApp.csproj -o /app/publish /p:UseAppHost=false --no-restore
    env:
      TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}

Azure Pipelines YAML

  - powershell: dotnet publish MyApp.csproj -o /app/publish /p:UseAppHost=false --no-restore
    displayName: 'Build and publish the project'
    env:
      TELERIK_LICENSE: $(MY_TELERIK_LICENSE_KEY) # AzDO pipeline secret variable

If you're using classic pipelines, you can use a pipeline variable:

Image

Important

License key length - If you are using a Library Variable Group, there is a character limit for the variable values. The only way to have a long value in the Variable Group is to link it from Azure KeyVault. If you cannot use Azure KeyVault, then use a normal pipeline variable instead (seen above) or use the Secure File approach instead (see below).

Approach 2 - Using a File

You have two options for a file-base option. Set the TELERIK_LICENSE_PATH variable or add a file named telerik-license.txt to the project directory. The licensing runtime will do a recursive check from the project directory to root, and then finally %appdata%/telerik/.

On Azure DevOps, there is a powerful feature called Secure Files. It lets you upload a file and then use it in a pipeline. Go to your Library tab, then select Secure File. After you've uploaded the Secure File to your Azure DevOps project, you can use it in a pipeline.

Here are several ways to use that Secure File.

Caution

Never include the telerik-license.txt file inside your application/docker image!

YAML Pipeline

With a YAML pipeline, you can use the DownloadSecureFile@1 task, then use $(name.secureFilePath) to reference it. For example:

  - task: DownloadSecureFile@1
    name: DownloadTelerikLicenseFile # defining the 'name' is important
    displayName: 'Download Telerik License Key File'
    inputs:
      secureFile: 'telerik-license.txt'

  - task: MSBuild@1
    displayName: 'Build Project'
    inputs:
      solution: 'myapp.csproj'
      platform: Any CPU
      configuration: Release
      msbuildArguments: '/p:RestorePackages=false'
    env:
      # use the name.secureFilePath value to set the special TELERIK_LICENSE_PATH
      TELERIK_LICENSE_PATH: $(DownloadTelerikLicenseFile.secureFilePath) 
Classic Pipeline

With a classic pipeline, you can use the same DownloadSecureFile Task

Image

Important

Make sure you set the reference name which gets prefixed to the .secureFilePath output variable.

Scenario 1 - Task With Env Var Inputs

With the secure file downloaded to the runner, you can now set the TELERIK_LICENSE_PATH variable using $(telerik.secureFilePath).

image
Scenario 2 - Task Without Env Var Inputs

Not all AzDO tasks have the "Environment variables" section (e.g. MsBuild task doesn't have it). To solve this, you can set a pipeline variable before that task. Using the secure file

  1. Add a new Powershell or Bash task (after the Download Secure File task)
  2. Set the TELERIK_LICENSE_PATH using task.setvariable command with issecret=trueand the secure file task's output variable
# If using Powershell
Write-Host "##vso[task.setvariable variable=TELERIK_LICENSE_PATH;issecret=true]$(telerik.secureFilePath)"

# If using Bash
echo "##vso[task.setvariable variable=TELERIK_LICENSE_PATH;issecret=true]$(telerik.secureFilePath)"
image
Scenario 3 - Move Secure File

If you have nay trouble with the TELERIK_LICENSE_PATH variable, you can just simply move the file to the root build directory.

  1. Add a new Powershell or Bash task (after the Download Secure File task)
Move-Item -Path "$(telerik.secureFilePath)" -Destination "$(Build.Repository.LocalPath)/telerik-license.txt" -Force
  1. Build the code
  2. Delete the file (so you don't accidentally include it in your distribution)
Remove-Item -Path "$(Build.Repository.LocalPath)/telerik-license.txt" -Force

As you can see, there are a wide range of options. The one you choose highly depends on your environment and CI requirements.

About

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

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors