When a project grows, and developers are pushing code frequently, there is always a chance that working pull requests might break somewhere.
It could be because one PR was merged before another, or the destination branch moved a few commits ahead, causing conflicts.
Or maybe because a developer didn’t run tests before pushing and unknowingly introduced a bug in some other part of the product. And the list goes on.
But this shouldn't be a problem. Every organization has a workflow for Code Reviews, right? But it still takes up a lot of time. Especially for those PR's that break and are not even ready for review.
We can manually build and test our code every time before a proper code review, no doubt. But after a certain point, it seems better to automate it.
Imagine a medium size organization with 100–150 PR’s every week. The time spent repetitively validating those might give that firm a whole set of new features. Well then, let’s go get those features!
You should have some familiarity with AWS Services.
I assume you know how to create and manage Lambda Functions, Codebuild Projects, Cloudwatch Events, IAM Roles, and that you’re using CodeCommit to version your codebase.
Let’s understand, at a high level, how we are going to tackle this project.
Step by step, let’s understand our workflow better.
- Let’s say a new PR is created / an existing PR is updated.
- A Cloudwatch event that is watching our repository will be activated and will send relevant data to a lambda function.
- This function will do two things
→ Trigger CodeBuild Project to build our latest commit and run tests.
→ Comment any custom message that we want on our PR.
- After CodeBuild finishes running the build, another Cloudwatch event will send those build results to a lambda function.
- This function will comment the build results on our PR.
Alright then, let’s get started!
Setting up our App
For the sake of simplicity, I’ve created a simple Node.js application. It’s written in TypeScript and all the build phase does is compile the ‘app.ts’ to ‘app.js’.
Here’s the link to the repo – clone and use it if you want to follow along.
All the relevant code used in this article can be found there.
build command here is a simple
tsc app.ts , but you can change it to your project’s build command.
Also to keep it simple, I’ve not included test cases. You can link them to
test in the script section of
package.json and follow along.
First, you'll want to set up a basic CodeBuild project for your repository.
To do this, do the following:
- Set up source as your Codecommit repository
- Reference type should be branch
- Environment should be per the project's requirements
- You should use a buildspec file
- The rest should just be defaults.
Make sure you have a
buildspec.yml file in your repo’s root folder.
Note: this might differ if you’re dealing with a MonoRepo. In that case, you might have separate buildspec.yml files for each App and will have to selectively pass the buildspec file path as an environment variable depending on the files changed inside the commit.
We have a similar setup at our organization, and we're loving the results at present!
What does this buildspec.yml do? Well, it passes runtime commands for each build to our CodeBuild project.
And then does what? ?
- Installs node 12.12.0
- Installs yarn
- Installs our project’s dependencies.
- yarn test (It runs our test cases. There are none here, but you can uncomment that section if you need it.)
- yarn build (Builds our project.)
Let’s set up two functions as discussed in the architecture section above.
The function TriggerCodebuildStart will receive a Cloudwatch Event (which we will set up in a bit) and it will trigger our CodeBuild project to start a fresh build.
It will also post a Build Started comment with the timestamp and a neat link to the build logs in our PR’s comment section.
The function TriggerCodebuildResult will receive a Cloudwatch Event from our CodeBuild project which will have the build results.
It will also post the Codebuild Results comment with the timestamp and a neat link to the build logs in our PR’s comment section.
Here’s the code. That’s what you were waiting for, weren’t you! ?
You'll need to fill in the appropriate environment variables before using these functions. Read the code once and you’ll know what to do.
In case you need to refer to the docs, just go here and there. ?
Configure Cloudwatch Events
Okay, now to hook it all up, let’s configure our Cloudwatch events.
We’ll create two events: One will receive new commit data from our repository, and the other will receive the Codebuild Results. The targets for these events will be our lambda functions.
I'm attaching full-page screenshots here. This will make it easier for you to understand the references.
I’ve chosen to trigger the lambda function on FAILED and SUCCEEDED events. But you can select All Events too and tailor it to your needs.
Okay, you’re super cool if you made it to this point. ? After so much work, Let’s see what we’ve achieved.
Let’s make two pull requests, one which is works well and the other which has an intentional build error.
Now, let's create a PR with bugs. See here, instead of app.get there’s ap.get. It’s intentional and silly. But it will do the job for now.
To take this a step further, you could trigger an API call to your Slack webhook URL to immediately notify in a channel in case of build failures. Awesome, right?
Also, this is a very simple set up and real-world projects might be more complex.
For example, MonoRepos might have multiple apps and builds, and tests for each of those apps are different.
Triggering all those tests every time will be of no use, and it'll be costlier and create confusion. You might need to selectively trigger those builds depending on which files were committed and which apps were affected.
However, this article should build up a decent base for you. And you can definitely expand on it. After all, you’re awesome too. :)
Thanks for reading! If you need some help regarding this, feel free to reach out to me on LinkedIn. Looking forward to helping however I can.