Using AWS CodeBuild and CodePipeline for Easy Continuous Integration
In a previous post, we setup a Gatsby blog and pushed the codebase to AWS CodeCommit.
This post will primarily focus on adding test coverage to our codebase and integrating AWS CodeBuild and CodePipeline for continuous integration.
Let's get started.
What exactly is AWS CodeBuild?
AWS CodeBuild is a fully managed continuous integration service that compiles source code, runs tests, and produces software packages that are ready to deploy. With CodeBuild, you don’t need to provision, manage, and scale your own build servers. CodeBuild scales continuously and processes multiple builds concurrently, so your builds are not left waiting in a queue. You can get started quickly by using prepackaged build environments, or you can create custom build environments that use your own build tools. With CodeBuild, you are charged by the minute for the compute resources you use.
what exactly is AWS CodePipeline?
AWS CodePipeline is a fully managed continuous delivery service that helps you automate your release pipelines for fast and reliable application and infrastructure updates. CodePipeline automates the build, test, and deploy phases of your release process every time there is a code change, based on the release model you define. This enables you to rapidly and reliably deliver features and updates. You can easily integrate AWS CodePipeline with third-party services such as GitHub or with your own custom plugin. With AWS CodePipeline, you only pay for what you use. There are no upfront fees or long-term commitments.
Our goal -- run our test suite after every commit to the master
branch of our remote repository.
Add Test Coverage
add Jest
First, let's install Jest:
npm i -G jest
Lets test the CLI is working correctly:
jest --version
you should see the version number:
configure Jest for Gatsby
Please refer to the Jest configuration setup from Gatsby
once you completed the setup, you should have these files:
__mocks__/file-mock.js
__mocks__/gatsby.js
jest-preprocess.js
jest.config.js
loadershim.js
add our first test
cd src/pages
touch index.test.js
inside src/pages/index.test.js
, paste:
import React from "react"
import renderer from "react-test-renderer"
import { useStaticQuery } from "gatsby"
import BlogIndex from "./index"
beforeEach(() => {
useStaticQuery.mockImplementation(() =>
({
avatar: {
childImageSharp: {
fixed: {
base64: "data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAUABQDASIAAhEBAxEB/8QAGQABAQADAQAAAAAAAAAAAAAAAAUBAgME/8QAFwEBAQEBAAAAAAAAAAAAAAAAAgEAA//aAAwDAQACEAMQAAABtTuWpVlGTeDJ0HAG/wD/xAAbEAACAgMBAAAAAAAAAAAAAAABAgMTABEjJP/aAAgBAQABBQJzpYGe3LyGZxGLsY+mY8Yzof/EABgRAAIDAAAAAAAAAAAAAAAAAAABEBIh/9oACAEDAQE/AcKjj//EABkRAAEFAAAAAAAAAAAAAAAAAAABEBIhMf/aAAgBAgEBPwGyQmN//8QAIBAAAQIFBQAAAAAAAAAAAAAAAQARAgMQEiExQWGBkf/aAAgBAQAGPwItqmMdwNLT4n2JbCwQU/Kl90//xAAaEAEAAwEBAQAAAAAAAAAAAAABABEhMUFR/9oACAEBAAE/IUVXGRfSl5yZEWkVrGwnd6EDTA+sdXqEbIVcO+k//9oADAMBAAIAAwAAABD76AH/xAAZEQACAwEAAAAAAAAAAAAAAAAAAREhMVH/2gAIAQMBAT8QhKbI9NMWH//EABkRAAIDAQAAAAAAAAAAAAAAAAABEBEhUf/aAAgBAgEBPxBNtovyBrT/xAAeEAEBAQACAQUAAAAAAAAAAAABEQAhMUFRYXGBkf/aAAgBAQABPxClwdnV04hQo4lO/wAzyyjjEGw+cZbkILxecbfXBH7zaeBntAmta2n5uEBbXZ4N/9k=",
height: 50,
src: "/static/4f27694bd7811d13157e5e488ad64f43/9b664/profile-pic.jpg",
srcSet: "/static/4f27694bd7811d13157e5e488ad64f43/9b664/profile-pic.jpg 1x,↵/static/4f27694bd7811d13157e5e488ad64f43/06a10/profile-pic.jpg 1.5x,↵/static/4f27694bd7811d13157e5e488ad64f43/f1b5a/profile-pic.jpg 2x",
width: 50
}
}
},
site: {
siteMetadata: {
title: `Default Starter`,
description: 'test',
author: 'Frank',
social: {
twitter: 'fjhancock',
}
},
},
})
)
})
describe("BlogIndex", () => {
it("renders correctly", () => {
const data = {
site: {
siteMetadata: {
author: "Your name",
},
},
allMarkdownRemark: {
edges: [
{
node: {
excerpt: "Awesome new content goes here…",
fields: {
slug: "/my-first-post/"
},
frontmatter: {
date: "August 29, 2019",
title: "My First Post",
description: null
}
}
},
]
}
}
const tree = renderer.create(<BlogIndex data={data} location={{ pathname: '/' }} />).toJSON()
expect(tree).toMatchSnapshot()
})
})
Save the file. Now lets run Jest:
jest
We should now have a passing test suite!
commit our changes
git add -A
git commit -m 'setup jest, added first test'
git push origin master
Update our codebase for CodeBuild
create a buildspec.yml
in the root of the repo:
version: 0.2
env:
variables:
NODE_ENV: "development"
phases:
install:
runtime-versions:
nodejs: 10
commands:
- npm install -g jest
- npm install -g gatsby
pre_build:
commands:
- npm install
build:
commands:
- gatsby build
- npm run test:ci
cache:
paths:
- './node_modules/**/*'
add a new script to package.json
:
commit our changes
git add -A
git commit -m 'added buildspec for codebuild'
git push origin master
Configure AWS CodeBuild
We need to create a build project -- which we then can include in our pipeline.
You should now see your new build project listed.
Let's add a trigger to automate our builds.
Configure AWS CodePipeline
After a few minutes, we should see the pipeline successfully run:
If we dig into the logs of codebuild, we can see that our test suite has successfully passed:
At this stage, we have successfully created a continuous integration pipeline, using AWS CodeCommit, CodeBuild and CodePipeline!
In a later post, I will show you have to deploy your Gatsby blog using AWS Amplify.