From 9e04939cfb3d96cd5069020f48eb6c9aea3a9846 Mon Sep 17 00:00:00 2001 From: cellar-door Date: Wed, 20 Apr 2016 14:56:38 -0700 Subject: [PATCH 1/3] New Quicksilver Examples --- slack_notification_drupal_key/README.md | 41 +++++ slack_notification_drupal_key/key.php | 55 +++++++ .../slack_notification.php | 149 ++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 slack_notification_drupal_key/README.md create mode 100644 slack_notification_drupal_key/key.php create mode 100644 slack_notification_drupal_key/slack_notification.php diff --git a/slack_notification_drupal_key/README.md b/slack_notification_drupal_key/README.md new file mode 100644 index 0000000..77aff9f --- /dev/null +++ b/slack_notification_drupal_key/README.md @@ -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 the `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 +``` + diff --git a/slack_notification_drupal_key/key.php b/slack_notification_drupal_key/key.php new file mode 100644 index 0000000..4f79fbd --- /dev/null +++ b/slack_notification_drupal_key/key.php @@ -0,0 +1,55 @@ + '#quicksilver', + 'slack_username' => 'Pantheon-Quicksilver', + 'always_show_text' => false, +); + +require __DIR__ . '/key.php'; + +// Load our hidden credentials. +// See the README.md for instructions on storing secrets. +$secrets = key_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, + 'title' => 'Environment', + 'value' => '', + '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' => '', + '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 .= ' '; + $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']); + +/** + * 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); +} From 41b2878e261c15a9578a683b6ff0ce1159bce2cd Mon Sep 17 00:00:00 2001 From: cellar-door Date: Wed, 20 Apr 2016 15:00:20 -0700 Subject: [PATCH 2/3] Cleanup README for key.php file --- slack_notification_drupal_key/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slack_notification_drupal_key/README.md b/slack_notification_drupal_key/README.md index 77aff9f..729fb40 100644 --- a/slack_notification_drupal_key/README.md +++ b/slack_notification_drupal_key/README.md @@ -5,8 +5,8 @@ This script shows how easy it is to integrate Slack notifications from your Pant ## 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 the `private` directory in the root of your site's codebase, that is under version control. +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` and `key.php` scripts 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! From 5dd4bd2c3bd729531198a6e9316910cf119ca3d5 Mon Sep 17 00:00:00 2001 From: cellar-door Date: Fri, 22 Apr 2016 13:20:09 -0700 Subject: [PATCH 3/3] Combined key.php into single example script --- slack_notification_drupal_key/README.md | 2 +- slack_notification_drupal_key/key.php | 55 ------------------ .../slack_notification.php | 58 ++++++++++++++++++- 3 files changed, 56 insertions(+), 59 deletions(-) delete mode 100644 slack_notification_drupal_key/key.php diff --git a/slack_notification_drupal_key/README.md b/slack_notification_drupal_key/README.md index 729fb40..0f82201 100644 --- a/slack_notification_drupal_key/README.md +++ b/slack_notification_drupal_key/README.md @@ -6,7 +6,7 @@ This script shows how easy it is to integrate Slack notifications from your Pant 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` and `key.php` scripts to `private` directory in the root of your site's codebase, that is under version control. +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! diff --git a/slack_notification_drupal_key/key.php b/slack_notification_drupal_key/key.php deleted file mode 100644 index 4f79fbd..0000000 --- a/slack_notification_drupal_key/key.php +++ /dev/null @@ -1,55 +0,0 @@ - false, ); -require __DIR__ . '/key.php'; - // Load our hidden credentials. // See the README.md for instructions on storing secrets. -$secrets = key_get_secrets(array('slack_url_secret'), $defaults); +$secrets = _get_secrets(array('slack_url_secret'), $defaults); $secrets['slack_url'] = 'https://hooks.slack.com/services/' . $secrets['slack_url_secret']; @@ -115,6 +113,60 @@ _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 */