Skip to content

Commit b2c5f64

Browse files
author
rayj-pgi
committed
-added logging module so we get pretty JSON display in CloudWatch Logs
-added support for CodePipeline S3 Source (no revisionUrl) -log the error field if it is returned by channels.history or channels.list (helped with troubleshooting) -support new bot user with combined oauth bot user token and oauth token -updated readme with instructions to install with the new Slack App's bot user
1 parent e9943cf commit b2c5f64

File tree

7 files changed

+60
-26
lines changed

7 files changed

+60
-26
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,6 @@ venv.bak/
102102

103103
# mypy
104104
.mypy_cache/
105+
106+
# PyCharm
107+
.idea/

readme.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@ When creating the CloudFormation stack, you can customize:
2525
- `SlackBotName` (defaults to `PipelineBuildBot`).
2626
- `SlackBotIcon` (defaults to `:robot_face:` 🤖 ).
2727

28-
Additionally, you must provide `SlackToken`, (see [BotUsers](https://api.slack.com/custom-integrations/bot-users) for creating a slack bot user with an integration token).
28+
Additionally, you must provide both a `SlackOAuthAccessToken` and a `SlackBotUserOAuthAccessToken`, (see [BotUsers](https://api.slack.com/bot-users) for creating a slack bot user with an OAuth token). It is required to add the permission scope 'Access user’s public channels' (channels:history).
29+
2930

3031
## How it works
3132

3233
We utilize [CloudWatch Events](https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/WhatIsCloudWatchEvents.html) for CodePipline and CodeBuild to get notified of all status changes.
3334

3435
Using the notifications, as well as using the CodePipeline APIs, we are able to present a unified summary of your Pipeline and Build status.
3536

37+
3638
### IAM permissions
3739

3840
As part of the deployment, we create an IAM policy for the bot lambda function of:

src/build_info.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import json
2-
2+
import logging
3+
logger = logging.getLogger()
4+
logger.setLevel(logging.INFO)
35

46
class CodeBuildInfo(object):
57
def __init__(self, pipeline, buildId):
@@ -8,7 +10,7 @@ def __init__(self, pipeline, buildId):
810

911
@staticmethod
1012
def fromEvent(event):
11-
print(json.dumps(event, indent=2))
13+
logger.info(json.dumps(event, indent=2))
1214
# strip off leading 'codepipeline/'
1315
pipeline = event['detail']['additional-information']['initiator'][13:]
1416
bid = event['detail']['build-id']
@@ -39,9 +41,9 @@ def fromEvent(event):
3941
detail = event['detail']
4042
return BuildInfo(detail['execution-id'], detail['pipeline'])
4143
if event['source'] == "aws.codebuild":
42-
print(json.dumps(event, indent=2))
44+
logger.info(json.dumps(event, indent=2))
4345
ph = BuildInfo.pull_phase_info(event)
44-
print(json.dumps(ph, indent=2))
46+
logger.info(json.dumps(ph, indent=2))
4547

4648
return None
4749

src/message_builder.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# -*- coding: utf-8 -*-
22

33
import json
4+
import logging
5+
logger = logging.getLogger()
6+
logger.setLevel(logging.INFO)
7+
48
from collections import OrderedDict
59

610
class MessageBuilder(object):
@@ -10,12 +14,12 @@ def __init__(self, buildInfo, message):
1014
self.messageId = None
1115

1216
if message:
13-
print(json.dumps(message, indent=2))
17+
logger.info(json.dumps(message, indent=2))
1418
att = message['attachments'][0]
1519
self.fields = att['fields']
1620
self.actions = att.get('actions', [])
1721
self.messageId = message['ts']
18-
print("Actions {}".format(self.actions))
22+
logger.info("Actions {}".format(self.actions))
1923
else:
2024
self.fields = [
2125
{ "title" : buildInfo.pipeline,
@@ -32,11 +36,18 @@ def needsRevisionInfo(self):
3236

3337
def attachRevisionInfo(self, rev):
3438
if self.needsRevisionInfo() and rev:
35-
self.fields.append({
39+
if 'revisionUrl' in rev:
40+
self.fields.append({
3641
"title": "Revision",
3742
"value": "<{}|{}: {}>".format(rev['revisionUrl'], rev['revisionId'][:7], rev['revisionSummary']),
3843
"short": True
39-
})
44+
})
45+
else:
46+
self.fields.append({
47+
"title": "Revision",
48+
"value": rev['revisionSummary'],
49+
"short": True
50+
})
4051

4152
def attachLogs(self, logs):
4253
self.findOrCreateAction('Build Logs', logs['deep-link'])

src/notifier.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
import boto3
66
import time
77

8-
from test_events import TEST_EVENTS, TEST_ERROR_EVENTS
8+
# from test_events import TEST_EVENTS, TEST_ERROR_EVENTS
99
from build_info import BuildInfo, CodeBuildInfo
1010
from slack_helper import post_build_msg, find_message_for_build
1111
from message_builder import MessageBuilder
1212

13-
import re
14-
import sys
13+
# import re
14+
# import sys
1515

1616
client = boto3.client('codepipeline')
1717

src/slack_helper.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
from slackclient import SlackClient
22
import os
33
import json
4+
import logging
5+
logger = logging.getLogger()
6+
logger.setLevel(logging.INFO)
47

58
sc = SlackClient(os.getenv("SLACK_TOKEN"))
9+
sc_bot = SlackClient(os.getenv("SLACK_BOT_TOKEN"))
610
SLACK_CHANNEL = os.getenv("SLACK_CHANNEL", "builds2")
711
SLACK_BOT_NAME = os.getenv("SLACK_BOT_NAME", "BuildBot")
812
SLACK_BOT_ICON = os.getenv("SLACK_BOT_ICON", ":robot_face:")
@@ -14,10 +18,13 @@ def find_channel(name):
1418
return CHANNEL_CACHE[name]
1519

1620
r = sc.api_call("channels.list", exclude_archived=1)
17-
for ch in r['channels']:
18-
if ch['name'] == name:
19-
CHANNEL_CACHE[name] = ch['id']
20-
return ch['id']
21+
if 'error' in r:
22+
logger.error("error: {}".format(r['error']))
23+
else:
24+
for ch in r['channels']:
25+
if ch['name'] == name:
26+
CHANNEL_CACHE[name] = ch['id']
27+
return ch['id']
2128

2229
return None
2330

@@ -26,9 +33,13 @@ def find_msg(ch):
2633

2734
def find_my_messages(ch_name, user_name=SLACK_BOT_NAME):
2835
ch_id = find_channel(ch_name)
29-
for m in find_msg(ch_id)['messages']:
30-
if m.get('username') == user_name:
31-
yield m
36+
msg = find_msg(ch_id)
37+
if 'error' in msg:
38+
logger.error("error: {}".format(msg['error']))
39+
else:
40+
for m in msg['messages']:
41+
if m.get('username') == user_name:
42+
yield m
3243

3344
MSG_CACHE = {}
3445

@@ -57,7 +68,7 @@ def post_build_msg(msgBuilder):
5768
ch_id = find_channel(SLACK_CHANNEL)
5869
msg = msgBuilder.message()
5970
r = update_msg(ch_id, msgBuilder.messageId, msg)
60-
print(json.dumps(r, indent=2))
71+
logger.info(json.dumps(r, indent=2))
6172
if r['ok']:
6273
r['message']['ts'] = r['ts']
6374
MSG_CACHE[msgBuilder.buildInfo.executionId] = r['message']
@@ -72,7 +83,7 @@ def post_build_msg(msgBuilder):
7283
return r
7384

7485
def send_msg(ch, attachments):
75-
r = sc.api_call("chat.postMessage",
86+
r = sc_bot.api_call("chat.postMessage",
7687
channel=ch,
7788
icon_emoji=SLACK_BOT_ICON,
7889
username=SLACK_BOT_NAME,
@@ -81,9 +92,9 @@ def send_msg(ch, attachments):
8192
return r
8293

8394
def update_msg(ch, ts, attachments):
84-
r = sc.api_call('chat.update',
85-
channel=ch,
86-
ts=ts,
95+
r = sc_bot.api_call('chat.update',
96+
channel=ch,
97+
ts=ts,
8798
icon_emoji=SLACK_BOT_ICON,
8899
username=SLACK_BOT_NAME,
89100
attachments=attachments

template.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ Transform: AWS::Serverless-2016-10-31
33
Description: Code Pipeline Slack Notifier
44

55
Parameters:
6-
SlackToken:
6+
SlackOAuthAccessToken:
7+
MinLength: 1
8+
Type: String
9+
NoEcho: True
10+
SlackBotUserOAuthAccessToken:
711
MinLength: 1
812
Type: String
913
NoEcho: True
@@ -32,7 +36,8 @@ Resources:
3236
ReservedConcurrentExecutions: 1
3337
Environment:
3438
Variables:
35-
SLACK_TOKEN: !Ref SlackToken
39+
SLACK_TOKEN: !Ref SlackOAuthAccessToken
40+
SLACK_BOT_TOKEN: !Ref SlackBotUserOAuthAccessToken
3641
SLACK_BOT_NAME: !Ref SlackBotName
3742
SLACK_BOT_ICON: !Ref SlackBotIcon
3843

0 commit comments

Comments
 (0)