Using a combination of Cloud PubSub and Cloud Functions, it is possible to create status badges everytime you build. Read more to find out how!

Recently, Github announced easy integration with Google Cloud Build to simplify our continuous-integration (CI) processes. Because our company leverages the Google Cloud Platform, it is a great boon to unify our DevOps process inside a single infrastructure—especially for our private projects. Furthermore, with the first 120 build-minutes per day free-of-charge, Cloud Build is virtually free!

However, unlike Travis or AppVeyor, Cloud Build does not provide badges out-of-the-box. As open-source enthusiasts, we love badges, so wouldn’t it be nice to create one for Cloud Build? In this post, I’ll be talking about how to add badges to your Google Cloud Builds. The full architecture would look like this:

Full Architecture

Then, we’ll follow the steps below:

  1. Create SVG files for your badges then store to Cloud Storage
  2. Write a Cloud Function to deploy
  3. Deploy, trigger a build, then embed the generated badge into your README

To whom is this tutorial good for?

I realized that there is still a lot of configuration needed in order to proceed with this tutorial. So I think this post is highly recommended for people who already…

  • …have experience working with the Google Cloud Platform.
  • …doing continuous integration and deployment (CI/CD) with Cloud Build and Github, and just want to add badges in their project.

Before we begin, some prerequisites and assumptions:

Step 1: Create SVG files for badge templates

The first step is to create badge templates as SVG files then store them in Google Cloud Storage. We use this file format because of its “highly-scalable” resolution. For now, we need to create a badge for both SUCCESS and FAILURE status. You can create custom SVG files from Shields.IO, or just use our badges here:

  • Success badge (link): link
  • Failure badge (link): link

These badges will serve as your badge templates whenever a specific badge for your project is created. So if your latest build was successful, then the success template is used. The same goes whenever the build fails.

Note that these badges should be stored in your project’s Google Cloud Storage (GCS) bucket. It is preferable to have a directory, build, that will contain all badge-related artifacts.

# Directory structure for gs://<MY-PROJECT-BUCKET> 
.
./build/
./build/success.svg
./build/failure.svg

Step 2: Write a Cloud Function to deploy

The next step is to write a Cloud Function for deployment. The purpose of the Cloud Function is to listen for the build status (in terms of PubSub events), and performs some logic to update the build badge for our specific project. For the purposes of this tutorial, we’ll use Javascript (Node.js 6)1. You can use Python too, as long as you follow the execution pattern below:

  1. Check if the PubSub event corresponds to the repository and branch
  2. Get the event status for the latest build (success or fail)
  3. Depending on the status, copy the badge over to a new file (preferably <PROJECT>-<BRANCH>.svg)
  4. Update the newly-created badge’s accessibility to public

I’ll walk you through each step of the execution process (with some code snippets along the way), then I’ll put the overall deploy code that I often use at the end. Note that your Cloud Function must be stored either in index.js or function.js

Check PubSub event details

The way we’ll know the status of our builds is through GCP’s messaging tool called PubSub. Cloud Build automatically publishes build details as a topic and we just need to set-up a subscriber to receive push messages. We’ll start by writing a scaffold of our main method. We should export this function and it must take an event and a callback as its arguments:

  • event (Object): an object representing the event that triggered the function
  • callback (Function): a signal indicating the completion of the function execution.

The event object that we’ll obtain is a PubSubMessage. It contains the field, data, where we can access all relevant details from our build. We do this by parsing the JSON object:

const pubsubMessage = event.data;
if (pubsubMessage.data) {
    buildResource = JSON.parse(Buffer.from(pubsubMessage.data, "base64").toString());
}

Get the event status of the latest build

From our snippet above, all event details will be stored inside the variable buildResource. You can actually check all available fields by running gcloud builds describe <BUILD-ID>. For our specific use-case, we’ll need the following2:

  • Repository name: buildResource.substitutions.REPO_NAME
  • Branch name: buildResource.substitutions.BRANCH_NAME
  • Build status: buildResource.status

So let’s access the variables now:

// If using Github as your source repository 
repoName = buildResource.substitutions.REPO_NAME;
branch = buildResource.substitutions.BRANCH_NAME;

// If using Cloud Source Repository
repoName = buildResource.source.repoSource.repoName;
branch = buildResource.source.repoSource.branchName;

// Doesn't matter where your repository is
status = buildResource.status;

Perform some logic depending on the build status

Now, depending on the build status, we can update the SVG file in GCS that we will use for our README. If the build succeeds, then we copy over the “success” badge onto our new badge. The same goes whenever the build fails.

In my workflow, I usually name the output badge as {repoName}-{branch}.svg. Note that we also need to set the resulting object as publicly accessible for it to show up.

So here’s how the update works whenever the build succeeds:

const filename = "build/myRepoName-" + branch + ".svg";
const storage = new Storage();

if (repo && branch && status == "SUCCESS") {
    console.log("Detected a successful build!")
    // Copy file from our success.svg template onto
    // {repoName}-{branch}.svg
    storage
        .bucket("my-bucket")
        .file("build/success.svg")
        .copy(storage.bucket("my-bucket").file(filename))
    // Set the output artifact to public        
    storage
        .bucket("my-bucket")
        .file(filename)
        .makePublic(function(err, apiResponse) {});
}

Then we just repeat the same thing whenever the build fails. In this case, we just change the status string to "FAILURE" and copy over build/failure.svg to our filename.

If you want to see the full template for writing a Cloud Function, you can definitely check the gist below, and this small project of mine called cloud-build-badge3!

Step 3: Deploy and trigger a build

Recall that your cloud function should be stored either in index.js or function.js. This is crucial because the deployment tool will look for these files and vomit an error if not found. To deploy your function, execute the following command:

gcloud functions deploy myFunction              \
    --runtime nodejs6                           \
    --trigger-resource cloud-builds             \
    --trigger-event google.pubsub.topic.publish \

You can find the official documentation for gcloud functions deploy here. If deployment is successful, you should see an SVG file in your GCS bucket that you can use for your markdown READMEs!

Summary

In this tutorial, we have learned how to add status badges to our Google Cloud Builds using Cloud Functions and PubSub. The main idea is that we copy over the appropriate badge (stored in GCS) into the new badge given the latest build’s status. To accomplish that, we wrote a Cloud Functions script that reads the build status as a PubSubMessage and applies the appropriate logic depending on the result. The final product is a continually updating status badge for our specific project and branch that we can use in our READMEs!

Footnotes

  1. I chose to write in Javascript because I want to use this project as an opportunity to learn the language. 

  2. If your repository is in Cloud Source Repositories, you need to use buildResource.source.repoSource to access the repository and branch respectively (repoName, branchName). 

  3. cloud-build-badge was actually inspired by Samuel Sendelback’s tutorial on creating cloud build badges in 2 minutes. I decided to create a more verbose version of his work in this blog, and an easy tool to create deploy functions using the npm package.