.NET Core and BitBucket Pipelines!

A CI/CD pipeline story

Ali Bahraminezhad
ITNEXT

--

Build — Test — Caching

I’m working in a team that BitBucket is our git repository. The CI/CD in BitBucket is almost the same as the other competitors in the market such as Azure DevOps, GitLab and GitHub. In this article, I explain how to establish your CI/CD pipeline with BitBucket pipelines for .NET Core.

Step — 1: Getting your pipeline ready!

  1. Create your BitBucket repo and push the latest version of your code in it
  2. Create your pipeline: (From the left menu select Pipelines and then select .NET Core from the drop-down on the right)

The default bitbucket-pipelines.yml is something like the following YAML code:

# Only use spaces to indent your .yml configuration.
# -----
# You can specify a custom docker image from Docker Hub as your build environment.
image: microsoft/dotnet:sdk
pipelines:
default:
- step:
caches:
- dotnetcore
script: # Modify the commands below to build your repository.
- export PROJECT_NAME=yourProjectName
- export TEST_NAME=yourTestName
- dotnet restore
- dotnet build $PROJECT_NAME
- dotnet test $TEST_NAME

BitBucket pipeline is neat because it is using docker containers for each build. To support .NET Core 3.1 change the docker image to mcr.microsoft.com/dotnet/core/sdk:3.1.

💡 Get the list of the all docker images from official Microsoft docker hub page.

# Only use spaces to indent your .yml configuration.
# -----
# You can specify a custom docker image from Docker Hub as your build environment.
image: mcr.microsoft.com/dotnet/core/sdk:3.1
pipelines:
default:
- step:
caches:
- dotnetcore
script:
- export SOLUTION_NAME=MyProject
- export CONFIGURATION=Release
- dotnet restore SOLUTION_NAME.sln
- dotnet build --no-restore $SOLUTION_NAME.sln --configuration $CONFIGURATION
- dotnet test --no-build $SOLUTION_NAME.sln
  • Change the pipeline docker image to .NET Core SDK 3.1.
  • Create a variable called SOLUTION_NAME and assign MyProject value to it.
  • Execute restore, build and test command with SOLUTION_NAME variable.

The pipeline is small, but it does the job for the CI part. One of the exciting parts of this pipeline is the cache section; the first build might take a while for getting the SDK image or restoring the Nuget packages but the upcoming builds, thanks to caching part, will be fast.

Although this pipeline runs the test, you cannot see the details of them because you need to log the result in JUNIT format. E.g. take a look at the following image:

It builds and runs the tests, and everything goes fine, but I cannot see how many tests it passes!

Step — 2: Dotnet tests and JUnit format

Dotnet test command has an option to determine the output file format for the test results. Still, it doesn’t support JUnit as a builtin format, but you can install third-party loggers to enable dotnet test command logs in JUnit format.

One of the best available loggers is JUnitTestLogger project. Quickly install it on your tests project(s).

# As an example for my own project
cd ./Web.Tests
dotnet add package JUnitTestLogger --version 1.1.0

Step — 3: Change the pipeline to generate the logs!

The last part of every BitBucket pipeline is the tear-down step. In that part, BitBucket looks for the log files in the following directories named [test-results, failsafe-reports, test-reports, surefire-reports].

We need to define a new variable for output test logs, something like this:

REPORTS_REL_PATH=./test-reports/build_${BITBUCKET_BUILD_NUMBER}

💡 ${BITBUCKET_BUILD_NUMBER} is one of the built-in variables in every pipeline.

Now, all we need is to pass this argument to dotnet test command to generate test results in JUnit XML format and store them in test-reports directory.

— test-adapter-path:. — logger:”junit;LogFilePath=$REPORTS_REL_PATH/junit.xml”

The changed YAML file:

# Only use spaces to indent your .yml configuration.
# -----
# You can specify a custom docker image from Docker Hub as your build environment.
image: mcr.microsoft.com/dotnet/core/sdk:3.1
pipelines:
default:
- step:
caches:
- dotnetcore
script:
- export SOLUTION_NAME=MyProject
- export CONFIGURATION=Release
- export REPORTS_REL_PATH=./test-reports/build_${BITBUCKET_BUILD_NUMBER}
- export REPORTS_PATH=./${PROJECT_NAME}/test-reports/build_${BITBUCKET_BUILD_NUMBER}
- dotnet restore SOLUTION_NAME.sln
- dotnet build --no-restore $SOLUTION_NAME.sln --configuration $CONFIGURATION
- dotnet test --no-build $SOLUTION_NAME.sln --test-adapter-path:. --logger:"junit;LogFilePath=$REPORTS_REL_PATH/junit.xml"

Thanks to this change, you can see the number of passed tests and if any of them fails you have access to the details of the failure:

Sample of failed and successful tests

The last but maybe not final step: Publishing the artefacts

Publishing a dotnet project is easy with “dotnet publish” command.

dotnet publish --configuration $CONFIGURATION  --no-build ./Web/Web.csproj -o ./publish

This command publishes the web application into the publish directory. Now we need to tell the BitBucket pipeline to save those artefacts.

artifacts:
- publish/**

The final YAML file:

# Only use spaces to indent your .yml configuration.
# -----
# You can specify a custom docker image from Docker Hub as your build environment.
image: mcr.microsoft.com/dotnet/core/sdk:3.1
pipelines:
default:
- step:
caches:
- dotnetcore
script:
- export SOLUTION_NAME=MyProject
- export CONFIGURATION=Release
- export WEB_PROJECT_NAME=Web
- export REPORTS_REL_PATH=./test-reports/build_${BITBUCKET_BUILD_NUMBER}
- dotnet restore SOLUTION_NAME.sln
- dotnet build --no-restore $SOLUTION_NAME.sln --configuration $CONFIGURATION
- dotnet test --no-build $SOLUTION_NAME.sln --test-adapter-path:. --logger:"junit;LogFilePath=$REPORTS_REL_PATH/junit.xml"
- dotnet publish --configuration $CONFIGURATION --no-build ./Web/${WEB_PROJECT_NAME}.csproj -o ./publish
artifacts:
- ./publish/**

Now after every pipeline job, you have access the artefacts tab in the pipeline result like below:

What’s next?

You have almost everything for further steps such as integration tests and using artefacts for deployment.

Good luck in your integration and deployment.

--

--

Writer for

I’m a geek, a software engineer who likes to build amazing stuff 😉Here on Medium I write about different things but mostly focused on programming and .NET!