Send commit status from CodePipeline to GitHub
GitHub is a great tool for developers to collaborate on projects. Using it just as a service for hosting the source code of a project is only the tip of the iceberg. GitHub provides lots of other great features to streamline the development process and support collaboration. It also provides a way to show information along with changes made to the source code. One of these bits of information is the CI status of a commit. For this, GitHub provides the Commit Status API. It allows CI tools to notify GitHub whether a commit passed or failed a CI build. Using this information, the development workflow can be controlled.
This blog post shows how to integrate AWS CodePipeline with the GitHub Commit Status API. In previous blog posts CodePipeline has been used for publishing changes to Jekyll sites like this blog or driving a CI/CD pipeline for Serverless Applications based on the Serverless Application Model (SAM).
What are the building blocks?
Unfortunately, AWS CodePipeline does not provide out of the box support for pushing commit status information to GitHub yet. Fortunately, it is part of an ecosystem that provides a lot of extension points which allow such an integration to be build pretty easily.
The solution is based on the AWS services CloudWatch Events and Lambda. Whenever certain things happen during the execution of a pipeline CodePipeline sends events to CloudWatch, like the execution is started or an action fails. In CloudWatch Events rules can be set up to invoke a Lambda function for matching events. The Lambda function gets the event to act on them. In case of an integration with the GitHub Commit Status API some information can be send to the appropriate API endpoint provided by GitHub.
Let’s go through this step by step.
How does it work?
As mentioned before, CodePipeline sends events about state changes to CloudWatch Events. For example, when the execution of the pipeline some-pipeline
starts the following event is send:
{
"version": "0",
"id": "CWE-event-id",
"detail-type": "CodePipeline Pipeline Execution State Change",
"source": "aws.codepipeline",
"account": "123456789012",
"time": "2017-04-22T03:31:47Z",
"region": "us-east-1",
"resources": [
"arn:aws:codepipeline:us-east-1:123456789012:pipeline:some-pipeline"
],
"detail": {
"pipeline": "some-pipeline",
"version": "1",
"state": "STARTED",
"execution-id": "01234567-0123-0123-0123-012345678901"
}
}
Basically, the events for successful or failed pipeline executions differ only in the value for the state
attribute.
For our use case we are only interested in getting notified when the execution of a pipeline starts, succeeds or finishes. To filter for these kinds of events, a CloudWatch Event Rule must be created which matches these events. The rule looks like this:
{
"detail-type": [
"CodePipeline Pipeline Execution State Change"
],
"source": [
"aws.codepipeline"
],
"detail": {
"state": [
"STARTED",
"SUCCEEDED",
"FAILED"
]
}
}
When the Lambda function is triggered it gets the event shown above as the input. From this event it extracts some information, like the pipeline that has send the event. The function then gets information about the execution of the pipeline, like the commit ID and the owner and name of the GitHub repository. It then sends the appropriate information to the GitHub Commit Status API. The Lambda handler looks like this:
exports.handler = async (event) => {
const region = event.region;
const pipelineName = event.detail.pipeline;
const executionId = event.detail['execution-id'];
const state = transformState(event.detail.state);
if (state === null) {
return;
}
const result = await this.getPipelineExecution(pipelineName, executionId);
const payload = createPayload(pipelineName, region, state);
await this.postStatusToGitHub(result.owner, result.repository, result.sha, payload);
return null;
};
The detailed implementation of the functions the handler calls can be found in the GitHub repository.
What can it be used for?
With this solution deployed to an AWS region in which AWS CodePipeline pipelines are set up the CI status of a commit is automatically pushed to GitHub. This can be used for various things.
First, the commit status is immediately visible to project members in the commit history of a project. Green checkmarks symbolize successful builds whereas red crosses represent failed ones. Additionally, orange dots show that a build is currently performed. This helps to figure out when a commit did break a CI build. Clicking on the icon provides some more details and contains a link to the CodeBuild project to directly jump there, e.g. to looks into the logs.
On the other hand, the commit status can be used in the development workflow to make it more secure. In GitHub the master
branch can be configured as a Protected Branch. It is protected from commits and merges that do not adhere to the development workflow, e.g. have not been reviewed. One such rule is that the CI build needs to succeed. This acts like a safety net to ensure that only changes are merged into the master
branch if a general set of controls are validated, like all unit tests are succeeding.
Summary
This blog showed how easy it is to integrate the CodePipeline services offered by AWS with the Commit Status feature provided by GitHub. The actual costs for using this integration can basically be neglected since the AWS services used for this solution are either free or are probably covered via the free tier.
The code of this project can be found on GitHub. With this it should already be pretty easy to get started. Of course, this only is a very basic implementation. There’s probably lots of room for improvement. But it provides a good starting point.