Appearance
Are you an LLM? You can read better optimized documentation at /deployment/ci_cd.md for this page in Markdown format
CI/CD
This document provides a detailed overview of the Continuous Integration and Continuous Deployment (CI/CD) pipeline for TheExampleApp. It covers the configuration, build process, testing strategy, and deployment automation used to ensure code quality and streamline releases.
CircleCI Configuration
The project utilizes CircleCI for its CI/CD pipeline, configured via the .circleci/config.yml file. This file defines the jobs, steps, and execution environment for automating the build and test processes.
The configuration is based on CircleCI version: 2 and defines a single job named build.
Execution Environment
The pipeline runs within a custom Docker image, specified in the configuration:
yaml
# .circleci/config.yml
version: 2
jobs:
build:
docker:
- image: roblinde/the-example-app-build-assets
steps:
# ... steps are defined here1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
image: roblinde/the-example-app-build-assets: This custom image is pre-configured with all the necessary dependencies to build and test the application. This includes the .NET Core 2.1 SDK, Node.js, and npm (for E2E test dependencies), which optimizes build times by avoiding repeated installations.
Build Pipeline
The build job in CircleCI executes a series of sequential steps to compile the application, run tests, and prepare for deployment.
yaml
# .circleci/config.yml
steps:
- checkout
- run:
name: Build
command: dotnet build
- run:
name: Unit tests
command: dotnet test TheExampleApp.Tests
- run:
name: Integration tests
command: dotnet test TheExampleApp.IntegrationTests
- run:
name: Run app
command: |
cd TheExampleApp
dotnet run
background: true
# ... E2E test steps follow1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
The pipeline flow is as follows:
checkout: The source code is checked out from the version control repository.dotnet build: The .NET Core CLI compiles the entire solution, including the main application and test projects. This ensures the code is syntactically correct and all dependencies are resolved.- Test Execution: The pipeline runs unit and integration tests. This is covered in detail in the next section.
dotnet run: The main web application is started in the background (background: true). This is a critical step that makes the running application available atlocalhostfor the End-to-End (E2E) tests to run against.
Running Tests
The CI pipeline is configured to run a comprehensive suite of tests to validate application correctness at multiple levels.
Unit & Integration Tests
Unit and integration tests are executed directly using the .NET Core CLI.
- Unit Tests: These tests validate individual components in isolation. The pipeline uses Moq for mocking dependencies. For more details on the unit testing strategy, see the Unit Tests documentation.bash
# Command from .circleci/config.yml dotnet test TheExampleApp.Tests1
2 - Integration Tests: These tests verify the interactions between different components of the application.bash
# Command from .circleci/config.yml dotnet test TheExampleApp.IntegrationTests1
2
End-to-End (E2E) Tests
The project includes two types of E2E tests:
- In-Repository Selenium Tests (
TheExampleApp.E2eTests): A .NET-based E2E test suite using Selenium WebDriver - External Cypress Tests: JavaScript-based tests from a separate repository
External Cypress Tests
The CircleCI pipeline executes external E2E tests that reside in a separate repository and are written in JavaScript using the Cypress framework.
The pipeline performs the following steps to execute them:
yaml
# .circleci/config.yml
- run:
name: Clone e2e tests
command: |
git clone https://github.com/contentful/the-example-app-e2e-tests.git ./test/e2e
cd test/e2e
- restore_cache:
keys:
- package-v2-{{ checksum "./test/e2e/package.json" }}
- package-v2
- run:
name: Install e2e tests dependencies
command: |
cd test/e2e
npm i
- save_cache:
key: package-v2-{{ checksum "./test/e2e/package.json" }}
paths:
- ./test/e2e/node_modules
- run:
name: Run e2e tests
command: |
cd test/e2e
node_modules/.bin/cypress run --project /root/project --env LANGUAGE=dotnet1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- Clone Repository: The E2E test repository is cloned into the build environment.
- Cache & Install: CircleCI's caching mechanism is used to store and retrieve
node_modules, speeding up thenpm istep significantly on subsequent builds. - Execute Cypress: The tests are run using the
cypress runcommand.--project /root/project: This tells Cypress where the application's root directory is located.--env LANGUAGE=dotnet: This passes an environment variable to the Cypress tests, likely to configure them to target the specific selectors and behavior of the .NET version of the example application.
In-Repository Selenium Tests
The TheExampleApp.E2eTests project provides an alternative E2E testing approach using Selenium WebDriver. This test suite is written in C# and uses xUnit as the test runner.
Key Dependencies (from TheExampleApp.E2eTests.csproj):
- Selenium.WebDriver (v3.7.0): Browser automation framework
- xUnit (v2.3.1): Test runner
- Microsoft.NET.Test.Sdk (v15.5.0): Test infrastructure
The project includes ChromeDriver executables (chromedriver and chromedriver.exe) that are copied to the output directory during build, enabling automated Chrome browser testing.
While the CircleCI pipeline currently executes the external Cypress tests, the Selenium-based tests can be run locally or integrated into the CI pipeline using:
bash
dotnet test TheExampleApp.E2eTests1
For more information, refer to the E2E Tests documentation.
Docker Image Building
The project includes a Dockerfile that defines how to build a production-ready container image for the application. It utilizes a multi-stage build pattern to create a small, secure, and efficient final image.
dockerfile
# Dockerfile
# Stage 1: The Builder
# Uses the full .NET SDK to build and publish the application.
FROM microsoft/dotnet:2.1-sdk AS builder
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o /app/
# Stage 2: The Final Image
# Uses the lightweight ASP.NET Core runtime image.
FROM microsoft/aspnetcore:2.1
WORKDIR /app
ENV ASPNETCORE_ENVIRONMENT=Heroku
# Copies only the published artifacts from the builder stage.
COPY --from=builder /app .
ENTRYPOINT ["dotnet", "TheExampleApp.dll"]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- Builder Stage: This stage uses the
microsoft/dotnet:2.1-sdkimage, which contains the full toolchain needed to compile the application. It restores dependencies and publishes the release build to the/app/directory. - Final Stage: This stage starts from the lean
microsoft/aspnetcore:2.1runtime image. It copies only the published application artifacts from the builder stage. This practice significantly reduces the final image size and attack surface by excluding the .NET SDK and source code. ENTRYPOINT: This command specifies that the container will executedotnet TheExampleApp.dllupon startup, running the application.
Further details on containerization can be found in the Docker documentation.
Deployment Automation
Deployment is primarily configured for the Heroku platform, as indicated by the app.json file.
json
// app.json
{
"name": "The example app .NET",
"description": "This is \"The Example App\", a reference for building your own applications using Contentful.",
"repository": "https://github.com/contentful/the-example-app.csharp",
"stack": "heroku-16",
"buildpacks": [
{
"url": "https://github.com/jincod/dotnetcore-buildpack"
}
]
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
This file enables one-click deployments via Heroku's "Deploy to Heroku" button.
stack: Specifies theheroku-16stack, which is based on Ubuntu 16.04.buildpacks: Defines the buildpack required to run a .NET Core application on Heroku. Since .NET Core is not a natively supported language on theheroku-16stack, a custom buildpack (jincod/dotnetcore-buildpack) is used.
While the CircleCI configuration provided only covers build and test, a typical workflow would involve adding a deployment step that triggers on successful builds on the main or master branch, pushing the application to Heroku.
For more details, see the Heroku Deployment documentation.
Environment Management
The application manages environment-specific configurations using standard ASP.NET Core and containerization practices.
ASPNETCORE_ENVIRONMENT: TheDockerfilesets this environment variable toHeroku.dockerfileENV ASPNETCORE_ENVIRONMENT=Heroku1This allows the application to load environment-specific settings at runtime, for example from an
appsettings.Heroku.jsonfile, overriding base configurations fromappsettings.json.CI Environment: The CircleCI pipeline manages its own environment, primarily through the custom Docker image and the environment variables passed to the E2E tests (
LANGUAGE=dotnet).
Secrets Management
The application requires API keys and other secrets to connect to the Contentful CMS. The provided configuration files correctly omit these sensitive values.
In a CI/CD context like CircleCI, secrets should be managed using Environment Variables configured in the project's settings within the CircleCI UI. The application is built to read these values from the environment at runtime.
The following environment variables are expected to be configured in the CI environment:
CONTENTFUL_SPACE_IDCONTENTFUL_DELIVERY_API_KEYCONTENTFUL_PREVIEW_API_KEY
These are read by the contentful.aspnetcore SDK to fetch content from the CMS. Storing them as environment variables is a security best practice that avoids hardcoding secrets in the source code.
Build Notifications
The provided .circleci/config.yml does not explicitly configure notifications. By default, CircleCI sends email notifications for build status changes to the user who triggered the build.
For more advanced notifications, the pipeline can be extended to integrate with services like Slack or Microsoft Teams. This is typically done using CircleCI Orbs (e.g., the slack/notify orb) or by adding a custom run step that sends a webhook to the desired service upon build completion or failure.
Alternative CI Systems
While this project uses CircleCI, the core logic of the pipeline is highly portable to other CI/CD platforms.
GitHub Actions: The pipeline could be replicated by creating a workflow YAML file in the
.github/workflows/directory. Theruncommands (dotnet build,dotnet test, etc.) would be nearly identical, defined withinstepsin a job that runs on anubuntu-latestrunner with the .NET Core SDK and Node.js set up.Azure DevOps: An Azure Pipeline could be created using either the visual editor or a YAML file (
azure-pipelines.yml). The pipeline would consist of tasks that map directly to the shell commands:- A
.NET Coretask forbuild,test, andpublish. - A
Node.js Tool Installertask andnpmtask to install and run the E2E tests. - A
Dockertask to build and push the container image to a registry like Azure Container Registry.
- A