Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slack Notifications using Key Module #74

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions slack_notification_drupal_key/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Slack Integration Using Drupal Key Module #

This script shows how easy it is to integrate Slack notifications from your Pantheon project using Quicksilver. As a bonus, we also show you how to manage API keys outside of your site repository using the [Drupal Key](https://www.drupal.org/project/key) module. This module allows you to centralize all keys into one place and give Quicksilver access to them using Drush.

## Instructions ##

1. [Enable Incoming Webhooks](https://my.slack.com/services/new/incoming-webhook/) for your Slack instance.
2. Examine the secret Webhook URL, which should look something like: `https://hooks.slack.com/services/MY/SECRET/URL` and copy everything after `https://hooks.slack.com/services/` and put it into a new key with a machine name of `slack_url_secret`. You can add this key at: /admin/config/system/keys/add of your site.
3. Add the example `slack_notification.php` script to `private` directory in the root of your site's codebase, that is under version control.
4. Add Quicksilver operations to your `pantheon.yml`
5. Test a deploy out!

For secrets such as this webhook it's recommended to use a secure secrets management system like [Lockr](https://www.drupal.org/project/lockr). Lockr is free to use in all development environments and integrates seamlessly into the key module, just select it under the **Key Provider** dropdown when entering your key.

Optionally, you may want to use the `terminus workflows watch` command to get immediate debugging feedback. You may also want to customize your notifications further. The [Slack API](https://api.slack.com/incoming-webhooks) documentation has more on your options.

### Example `pantheon.yml` ###

Here's an example of what your `pantheon.yml` would look like if this were the only Quicksilver operation you wanted to use. Pick and choose the exact workflows that you would like to see notifications for.

```yaml
api_version: 1

workflows:
deploy:
after:
- type: webphp
description: Post to Slack on deploy
script: private/scripts/slack_notification.php
sync_code:
after:
- type: webphp
description: Post to Slack on sync code
script: private/scripts/slack_notification.php
clear_cache:
after:
- type: webphp
description: Someone is clearing the cache again
script: private/scripts/slack_notification.php
```

201 changes: 201 additions & 0 deletions slack_notification_drupal_key/slack_notification.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
<?php

// Important constants :)
$pantheon_yellow = '#EFD01B';

// Default values for parameters
$defaults = array(
'slack_channel' => '#quicksilver',
'slack_username' => 'Pantheon-Quicksilver',
'always_show_text' => false,
);

// Load our hidden credentials.
// See the README.md for instructions on storing secrets.
$secrets = _get_secrets(array('slack_url_secret'), $defaults);

$secrets['slack_url'] = 'https://hooks.slack.com/services/' . $secrets['slack_url_secret'];

// Build an array of fields to be rendered with Slack Attachments as a table
// attachment-style formatting:
// https://api.slack.com/docs/attachments
$fields = array(
array(
'title' => 'Site',
'value' => $_ENV['PANTHEON_SITE_NAME'],
'short' => 'true'
),
array( // Render Environment name with link to site, <http://{ENV}-{SITENAME}.pantheon.io|{ENV}>
'title' => 'Environment',
'value' => '<http://' . $_ENV['PANTHEON_ENVIRONMENT'] . '-' . $_ENV['PANTHEON_SITE_NAME'] . '.pantheonsite.io|' . $_ENV['PANTHEON_ENVIRONMENT'] . '>',
'short' => 'true'
),
array( // Render Name with link to Email from Commit message
'title' => 'By',
'value' => $_POST['user_email'],
'short' => 'true'
),
array( // Render workflow phase that the message was sent
'title' => 'Workflow',
'value' => ucfirst($_POST['stage']) . ' ' . str_replace('_', ' ', $_POST['wf_type']),
'short' => 'true'
),
array(
'title' => 'View Dashboard',
'value' => '<https://dashboard.pantheon.io/sites/'. PANTHEON_SITE .'#'. PANTHEON_ENVIRONMENT .'/deploys|View Dashboard>',
'short' => 'true'
),
);

// Customize the message based on the workflow type. Note that slack_notification.php
// must appear in your pantheon.yml for each workflow type you wish to send notifications on.
switch($_POST['wf_type']) {
case 'deploy':
// Find out what tag we are on and get the annotation.
$deploy_tag = `git describe --tags`;
$deploy_message = $_POST['deploy_message'];

// Prepare the slack payload as per:
// https://api.slack.com/incoming-webhooks
$text = 'Deploy to the '. $_ENV['PANTHEON_ENVIRONMENT'];
$text .= ' environment of '. $_ENV['PANTHEON_SITE_NAME'] .' by '. $_POST['user_email'] .' complete!';
$text .= ' <https://dashboard.pantheon.io/sites/'. PANTHEON_SITE .'#'. PANTHEON_ENVIRONMENT .'/deploys|View Dashboard>';
$text .= "\n\n*DEPLOY MESSAGE*: $deploy_message";
// Build an array of fields to be rendered with Slack Attachments as a table
// attachment-style formatting:
// https://api.slack.com/docs/attachments
$fields[] = array(
'title' => 'Deploy Message',
'value' => $text,
'short' => 'false'
);
break;

case 'sync_code':
// Get the committer, hash, and message for the most recent commit.
$committer = `git log -1 --pretty=%cn`;
$email = `git log -1 --pretty=%ce`;
$message = `git log -1 --pretty=%B`;
$hash = `git log -1 --pretty=%h`;

// Prepare the slack payload as per:
// https://api.slack.com/incoming-webhooks
$text = 'Code sync to the ' . $_ENV['PANTHEON_ENVIRONMENT'] . ' environment of ' . $_ENV['PANTHEON_SITE_NAME'] . ' by ' . $_POST['user_email'] . "!\n";
$text .= 'Most recent commit: ' . rtrim($hash) . ' by ' . rtrim($committer) . ': ' . $message;
// Build an array of fields to be rendered with Slack Attachments as a table
// attachment-style formatting:
// https://api.slack.com/docs/attachments
$fields += array(
array(
'title' => 'Commit',
'value' => rtrim($hash),
'short' => 'true'
),
array(
'title' => 'Commit Message',
'value' => $message,
'short' => 'false'
)
);
break;

default:
$text = $_POST['qs_description'];
break;
}

$attachment = array(
'fallback' => $text,
'pretext' => 'Deploying :rocket:',
'color' => $pantheon_yellow, // Can either be one of 'good', 'warning', 'danger', or any hex color code
'fields' => $fields
);

_slack_notification($secrets['slack_url'], $secrets['slack_channel'], $secrets['slack_username'], $text, $attachment, $secrets['always_show_text']);

/**
* Get secrets from secrets file and Drupal Key if present
*
* @param array $requiredKeys List of keys in secrets file that must exist.
*/
function _get_secrets($requiredKeys, $defaults) {
$secrets = array();
$error_message = array();

//Get our secrets.json file if it exists and pull in any values to the secrets array
$secretsFile = $_SERVER['HOME'] . '/files/private/secrets.json';
if (!file_exists($secretsFile)) {
$error_message[] = 'No secrets file found. ';
}
else{
$secretsContents = file_get_contents($secretsFile);
$secrets = json_decode($secretsContents, 1);
if ($secrets == FALSE) {
$error_message[] = 'Could not parse json in secrets file. ';
}
}

$secrets += $defaults;

$missing = array_diff($requiredKeys, array_keys($secrets));
if (!empty($missing)) {
foreach($missing as $key) {
print("\n==== Missing the key $key in the secrets.json so looking for it in Drush ====\n");
$key_value = exec("drush key-get-key " . $key);
if($key_value != 'Sorry no key found by that name') {
$secrets[$key] = $key_value;
}
}
}
$missing = array_diff($requiredKeys, array_keys($secrets));
if (!empty($missing)) {
$error_message[] = 'Missing required keys in json secrets file: ' . implode(',', $missing) . '. ';
}
else{
//Clear out any errors as we have all the required keys
$error_message = array();
}

//Output any errors and die
if(!empty($error_message)) {
print("\n==== Errors found in getting secrets ====\n");
foreach($error_message as $message) {
print($message);
}
die("Aborting!");
}
return $secrets;
}

/**
* Send a notification to slack
*/
function _slack_notification($slack_url, $channel, $username, $text, $attachment, $alwaysShowText = false)
{
$attachment['fallback'] = $text;
$post = array(
'username' => $username,
'channel' => $channel,
'icon_emoji' => ':lightning_cloud:',
'attachments' => array($attachment)
);
if ($alwaysShowText) {
$post['text'] = $text;
}
$payload = json_encode($post);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $slack_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
// Watch for messages with `terminus workflows watch --site=SITENAME`
print("\n==== Posting to Slack ====\n");
$result = curl_exec($ch);
print("RESULT: $result");
// $payload_pretty = json_encode($post,JSON_PRETTY_PRINT); // Uncomment to debug JSON
// print("JSON: $payload_pretty"); // Uncomment to Debug JSON
print("\n===== Post Complete! =====\n");
curl_close($ch);
}