An explanation of CI/CD

An explanation of CI/CD

You may have heard about CI/CD pipelines and wanted to understand what the mysterious acronyms are, the benefits and whether they might be useful for your project.

CI stands for continuous integration. In essence developers are encouraged to integrate changes they are working on frequently into the code base and each time this occurs an automated process builds the application and runs a series of automated tests. These tests should ideally cover as wide a range of the application functionality as possible. This set of consecutive build and test steps is known as the pipeline.

There are two main benefits to this. One is that if there are errors, or if changes you’ve made in one area have inadvertently affected another, the developer knows quickly. The earlier you know about software bugs the quicker and cheaper they are to fix. At this stage the details are fresh in the developer’s mind and it’s often a simple and quick fix. Compare this to bugs discovered later where you might have to have a user report, tester verification, developer ‘spin up’ time, user testing, deployment etc.

Secondly, making frequent integrations into the code base and knowing tests pass gives you confidence as a developer, allowing you to work more in a more agile and nimble fashion, speeding up development times.

If a CI pipeline is integrated at an early stage of the project the additional time cost is often very low. It often takes about the same amount of time to write an automated test as it does to manually test something, and of course if you write the automated test you get the benefit of that test each time someone makes a change in the future.

The second part of the acronym, CD, stands for continuous deployment. The idea here being that if you have a CI pipeline you can extend it to add automated deployment (assuming the build and test phases pass successfully). This obviously makes deployment itself quicker and cheaper, but also easy deployments allow small frequent updates instead of large infrequent ones. This means bugs are fixed quicker and new functionality can be released in smaller chunks with user feedback gained before proceeding with the next step. This improves software quality and overall user experience.

If your project is quite small and truly one off it may not be worth setting up CI/CD for it but for all other projects it should at least be considered.

CI/CD Strategies:

If you have a project on Azure DevOps, the easiest way to setup a CI/CD pipeline is to use Azure Pipelines and deploy to an Azure App Service. This uses a yaml file to define the pipeline steps which are then run for you by Azure. Some example steps for this are shown below.

If you have a project on Github the equivalent is Github Actions, which can also be used to deploy to Azure, AWS, or other cloud providers. Bitbucket has a similar feature called Bitbucket Pipelines.

If you want more control over the build and deploy process, you can run your own dedicated build server. Jenkins is a popular open source automation server which is often used for this. This can handle the build and test, as well as deploy. Alternatively it can hand over the deployment to Octopus Deploy which supports a wide range of deployment targets.

Example Steps for Azure Devops:

If you have an existing Azure DevOps project the following steps can be used to set up a CI/CD pipeline to deploy a .Net Core application onto Azure app service. The steps are similar for Github.

  1. Sign into your Azure Devops project.
  2. Click on Pipelines and then New Pipeline.
  3. Select your code location (usually Azure Repos if it’s a DevOps project) and then your actual repo.
  4. Select ASP.NET project.

This generates a skeleton azure-pipelines.yaml file which defines your CI/CD pipeline. You will usually want to have two stages: build and deploy.

The build stage steps will depend on your project will usually look something like the following:

- task: UseDotNet@2
displayName: 'Use .NET Core sdk'
packageType: sdk
version: $(dotnetCoreVersion)

- task: DotNetCoreCLI@2
displayName: Build
command: 'build'
projects: $(buildProjects)
arguments: --configuration $(buildConfiguration)

- task: DotNetCoreCLI@2
displayName: Test
command: 'test'
projects: $(testProjects)
publishWebProjects: true
arguments: --configuration $(buildConfiguration)

- task: DotNetCoreCLI@2
displayName: Publish
command: 'publish'
publishWebProjects: true
arguments: --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)
zipAfterPublish: true

The deploy stage will just have a single step, although you may wish to add your own custom conditions.

- task: AzureWebApp@1
displayName: 'Deploy Azure Web App: $(webAppName)'
azureSubscription: $(azureSubscription)
appType: webApp
appName: $(webAppName)
package: $(Pipeline.Workspace)/**/*.zip