Skip to content

Commit

Permalink
Create Slack integration for Google Cloud Build
Browse files Browse the repository at this point in the history
Posts notifications to a Slack channel whenever a Google Cloud Build
completes.
  • Loading branch information
Rob Percival authored and AlCutter committed Dec 12, 2022
1 parent 86d5fa8 commit b5e4e10
Show file tree
Hide file tree
Showing 8 changed files with 838 additions and 0 deletions.
16 changes: 16 additions & 0 deletions scripts/gcb2slack/.gcloudignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# This file specifies files that are *not* uploaded to Google Cloud Platform
# using gcloud. It follows the same syntax as .gitignore, with the addition of
# "#!include" directives (which insert the entries of the given .gitignore-style
# file at that point).
#
# For more information, run:
# $ gcloud topic gcloudignore
#
.gcloudignore
# If you would like to upload your .git directory, .gitignore file or files
# from your .gitignore file, remove the corresponding line
# below:
.git
.gitignore

node_modules
1 change: 1 addition & 0 deletions scripts/gcb2slack/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules/
43 changes: 43 additions & 0 deletions scripts/gcb2slack/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Slack Notifications for Google Cloud Build

> This is not an officially supported Google product.
This Google Cloud Function sends a notification to a Slack channel whenever a
Google Cloud Build completes.

![Success notification](success.png)
![Failure notification](failure.png)

## Installation

### Create a Slack Webhook

Follow
[Slack's "Getting Started with Incoming Webhooks" instructions](https://api.slack.com/messaging/webhooks)
to create a Slack app and incoming webhook URL. This URL will be required in the
next installation step.

### Deploy Google Cloud Function

If you don't already have a Google Cloud project, [create one now](https://cloud.google.com/resource-manager/docs/creating-managing-projects). Ensure that you have the [Google Cloud SDK](https://cloud.google.com/sdk/) installed as well.

Run the following command to deploy this Google Cloud Function, after changing
`$PROJECT` to your
[Google Cloud project ID](https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects)
and `$SLACK_WEBHOOK_URL` to the webhook URL you created in the previous
installation step. You may also customise `$FUNC_NAME` and `$STAGE_BUCKET`.

```shell
# Required parameters
$ PROJECT="YOUR-GOOGLE-CLOUD-PROJECT-ID"
$ SLACK_WEBHOOK_URL="YOUR-SLACK-WEBHOOK-URL"
# Customisable parameters
$ FUNC_NAME="cloudbuild2slack"
$ STAGE_BUCKET="${PROJECT}-cloudbuild2slack"

$ gcloud --project="${PROJECT}" functions deploy "${FUNC_NAME}" \
--stage-bucket="${STAGE_BUCKET}" \
--trigger-topic="cloud-builds" \
--runtime="nodejs10" \
--set-env-vars="SLACK_WEBHOOK_URL=${SLACK_WEBHOOK_URL}"
```
Binary file added scripts/gcb2slack/failure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
112 changes: 112 additions & 0 deletions scripts/gcb2slack/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
const {
IncomingWebhook
} = require('@slack/webhook');

const {
auth
} = require('google-auth-library');

const webhook = new IncomingWebhook(process.env.SLACK_WEBHOOK_URL);

// Add additional statues to list if you'd like:
// QUEUED, WORKING, CANCELLED
const interestingStatuses = ['SUCCESS', 'FAILURE', 'INTERNAL_ERROR', 'TIMEOUT'];

// cloudbuildToSlack reads from a Google Cloud Build Pub/Sub topic and writes
// to a Slack webhook.
module.exports.cloudbuildToSlack = async (pubSubEvent, context) => {
if (!pubSubEvent.data) {
console.info("No `data` field in pubSubEvent");
return;
}
const build = eventToBuild(pubSubEvent.data);

// Skip if the current status is not in the status list.
if (interestingStatuses.indexOf(build.status) === -1) {
console.log("Build status %s is ignored", build.status);
return;
}

// Send message to Slack.
const message = await createSlackMessage(build);
await webhook.send(message);
};

// eventToBuild transforms pubsub event message to a build object.
const eventToBuild = (data) => {
return JSON.parse(Buffer.from(data, 'base64').toString());
}

const getTrigger = async (triggerId) => {
const client = await auth.getClient({
scopes: 'https://www.googleapis.com/auth/cloud-platform',
});
const projectId = await auth.getProjectId();
const res = await client.request({
url: `https://cloudbuild.googleapis.com/v1/projects/${projectId}/triggers/${triggerId}`,
});
return res.data;
}

// createSlackMessage creates a message from a build object.
const createSlackMessage = async (build) => {
const buildStatus = build.statusDetail || build.status;
const trigger = await getTrigger(build.buildTriggerId);

let {
REPO_NAME: repo,
BRANCH_NAME: branch,
} = build.substitutions;

// If there was no substitution containing the repository name,
// take it from build.source. Unfortunately, this name will be the name
// used by Google Cloud Source Repositories, not the name on GitHub.
if (!repo && build.source.repoSource) {
repo = build.source.repoSource.repoName;
}

let messagePrefix, color;
switch (build.status) {
case 'TIMEOUT':
messagePrefix = "Timeout of ";
color = 'danger';
break;
case 'FAILURE':
messagePrefix = "Failed to ";
color = 'danger';
break;
case 'INTERNAL_ERROR':
messagePrefix = "Internal error while trying to ";
color = 'warning';
break;
case 'SUCCESS':
messagePrefix = "Completed ";
color = 'good';
break;
}

let message = {
text: `${messagePrefix}'${trigger.description}' for ${repo} repo`,
attachments: [{
fallback: buildStatus,
color: color,
fields: [{
title: 'Status',
value: buildStatus,
short: true,
}, {
title: 'Build Logs',
value: build.logUrl,
}],
}],
};

if (branch) {
message.attachments[0].fields.push({
title: 'Branch',
value: branch,
})
}

return message
}
Loading

0 comments on commit b5e4e10

Please sign in to comment.