codebuild-notifier 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,261 @@
1
+ # codebuild-notifier
2
+ Reports status of AWS CodeBuild CI jobs to slack.
3
+
4
+ # Infrastructure Requirements
5
+
6
+ ### Slack App or Bot in your workspace
7
+
8
+ Notifications will be sent as slack Direct Messages to users from the default
9
+ Slack bot in your workspace (e.g. @slackbot)
10
+ - Go to <a href="https://api.slack.com/apps">https://api.slack.com/apps</a>
11
+ - Create a New App, e.g. App Name: CodeBuild Notifier
12
+ - Under *Add features and functionality* select "Permissions" and grant these scopes:
13
+ - chat:write:bot
14
+ - users:read
15
+ - users:read.email
16
+ - Click *Install App To Workspace* and store the OAuth token generated in a new
17
+ secret in AWS Secrets Manager (<a href="#secret-in-aws-secrets-manager">see below</a>)
18
+
19
+ *Optional* Add a Bot User to your app - instead of the default Slack bot, messages
20
+ will come from a user with a name you choose, e.g. CodeBuildBot
21
+ - Under Features / Bot Users, click *Add a Bot User*
22
+ - Select a name and display name; the always show as online option does not matter
23
+ - After adding the Bot User, re-install the app to your workspace
24
+ - A new OAuth token will be generated specific to the Bot User. Store this in
25
+ AWS Secrets Manager instead of the App token.
26
+
27
+ ### DynamoDB table
28
+ - expected to be named 'branch-build-status', but can be configured
29
+ - the following definition:
30
+
31
+ ```ruby
32
+ AttributeDefinitions [
33
+ { AttributeName: 'source_id', AttributeType: 'S' },
34
+ { AttributeName: 'commit_hash', AttributeType: 'S' }
35
+ ]
36
+ GlobalSecondaryIndexes [
37
+ {
38
+ IndexName: 'commit_hash_index',
39
+ KeySchema: [
40
+ { AttributeName: 'commit_hash', KeyType: 'HASH' }
41
+ ],
42
+ Projection: { ProjectionType: 'ALL' },
43
+ }
44
+ ]
45
+ KeySchema [
46
+ { AttributeName: 'source_id', KeyType: 'HASH' }
47
+ ]
48
+ ```
49
+
50
+ ### Secret in AWS Secrets Manager
51
+ - expected to be named 'slack/codebuild', but can be configured
52
+ - contents should be:
53
+ ```json
54
+ { "token": "xoxo-your-slack-app-token" }
55
+ ```
56
+
57
+ ### IAM Service Role for CodeBuild projects
58
+
59
+ You will likely already have a service role granting CloudWatch access, to
60
+ which you will want to add the following, substituing your region,
61
+ account id, and if different, dynamo table name and secrets-manager secret
62
+ name:
63
+ ```json
64
+ {
65
+ "Action": [
66
+ "dynamodb:BatchGetItem",
67
+ "dynamodb:GetItem",
68
+ "dynamodb:PutItem",
69
+ "dynamodb:Query",
70
+ "dynamodb:Scan",
71
+ "dynamodb:UpdateItem"
72
+ ],
73
+ "Effect": "Allow",
74
+ "Resource": [
75
+ "arn:aws:dynamodb:<your-region>:<your-account-id>:table/branch-build-status",
76
+ "arn:aws:dynamodb:<your-region>:<your-account-id>:table/branch-build-status/*"
77
+ ]
78
+ },
79
+ {
80
+ "Action": "secretsmanager:GetSecretValue",
81
+ "Effect": "Allow",
82
+ "Resource": [
83
+ "arn:aws:secretsmanager:<your-region>:<your-account-id>:secret:slack/codebuild*"
84
+ ]
85
+ }
86
+ ```
87
+
88
+ # Configuration
89
+
90
+ ## Installation
91
+
92
+ ### Pre-requisites
93
+
94
+ The base docker image used for your CodeBuild project must include ruby, or
95
+ you must install it using the project's buildspec.yml file.
96
+ Any ruby from 2.3.x to 2.5.x will work.
97
+
98
+ ### Using buildspec
99
+
100
+ Add to the `install:` phase of your buildspec.yml
101
+
102
+ ```yml
103
+ phases:
104
+ install:
105
+ commands:
106
+ - gem install codebuild-notifier
107
+ ```
108
+
109
+ ### Using custom Docker image
110
+
111
+ Add to your Dockerfile
112
+ ```
113
+ RUN gem install codebuild-notifier
114
+ ```
115
+
116
+ ## Usage
117
+
118
+ Add to the `post_build:` phase of your buildspec.yml file
119
+
120
+ ```yml
121
+ phases:
122
+ post_build:
123
+ commands:
124
+ - update-build-status
125
+ ```
126
+
127
+ ## Configuration
128
+
129
+ ### ENV vars
130
+
131
+ ENV vars can either be set in Dockerfile e.g.
132
+ ```
133
+ ENV CBN_SLACK_ADMIN_USERNAMES scooby,shaggy
134
+ ```
135
+
136
+ Or in buildspec.yml
137
+ ```
138
+ env:
139
+ variables:
140
+ CBN_SLACK_ADMIN_USERNAMES: 'fred,velma'
141
+ ```
142
+
143
+ ### command-line
144
+
145
+ In buildspec.yml
146
+
147
+ ```yml
148
+ phases:
149
+ post_build:
150
+ commands:
151
+ - update-build-status --slack-admin-usernames="fred,velma"
152
+ ```
153
+
154
+ ### Options
155
+
156
+ <table>
157
+ <tr>
158
+ <th>ENV var</th>
159
+ <th>command-line</th>
160
+ <th>Default value</th>
161
+ <th>Notes</th>
162
+ </tr>
163
+ <tr>
164
+ <th>
165
+ CBN_ADDITIONAL_CHANNEL
166
+ </th>
167
+ <td>
168
+ <nobr>--additional-channel</nobr>
169
+ </td>
170
+ <td>
171
+ not set
172
+ </td>
173
+ <td>
174
+ If whitelist branches are set, status notifications for these
175
+ branches can be sent to this channel, as well as direct messages
176
+ to the author/committer of the commit triggering the build.
177
+ </td>
178
+ </tr>
179
+ <tr>
180
+ <th>
181
+ CBN_AWS_REGION
182
+ </th>
183
+ <td>
184
+ <nobr>--region</nobr>
185
+ </td>
186
+ <td>
187
+ value of AWS_REGION env var in CodeBuild container
188
+ </td>
189
+ <td>
190
+ If for some reason the dynamo table and secrets-manager live in a
191
+ different region than where CodeBuild is executing, you can specify
192
+ that region.
193
+ </td>
194
+ </tr>
195
+ <tr>
196
+ <th>
197
+ CBN_DYNAMO_TABLE
198
+ </th>
199
+ <td>
200
+ <nobr>--dynamo-table</nobr>
201
+ </td>
202
+ <td>
203
+ branch-build-status
204
+ </td>
205
+ <td>
206
+ This table must be created and permissions granted to it as described
207
+ in <a href="#infrastructure-requirements">Infrastructure Requirements</a>
208
+ </td>
209
+ </tr>
210
+ <tr>
211
+ <th>
212
+ CBN_SLACK_ADMIN_USERNAMES
213
+ </th>
214
+ <td>
215
+ <nobr>--slack-admin-usernames</nobr>
216
+ </td>
217
+ <td>
218
+ not set
219
+ </td>
220
+ <td>
221
+ If no slack user can be found in your workspace with the email
222
+ address of the author or committer of a commit, a message will be
223
+ sent to the slack usernames specified.<br />
224
+ Separate multiple values with commas, with no spaces.<br />
225
+ e.g. fred,velma
226
+ </td>
227
+ </tr>
228
+ <tr>
229
+ <th>
230
+ CBN_SLACK_SECRET_NAME
231
+ </th>
232
+ <td>
233
+ <nobr>--slack-secret-name</nobr>
234
+ </td>
235
+ <td>
236
+ slack/codebuild
237
+ </td>
238
+ <td>
239
+ The name of a secret in AWS Secrets Manager with the app or bot auth token.
240
+ </td>
241
+ </tr>
242
+ <tr>
243
+ <th>
244
+ CBN_WHITELIST_BRANCHES
245
+ </th>
246
+ <td>
247
+ <nobr>--whitelist-branches</nobr>
248
+ </td>
249
+ <td>
250
+ master,release
251
+ </td>
252
+ <td>
253
+ Normally statuses will be stored and notifications sent only for builds
254
+ triggered by commits to branches with open Pull Requests. However, it
255
+ can be useful to get notifications for all commits to certain branches,
256
+ regardless of Pull Request status.<br />
257
+ Separate multiple values with commas, without spaces.<br />
258
+ e.g. 'master,nightly,jira-50012'
259
+ </td>
260
+ </tr>
261
+ </table>
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # codebuild-notifier
4
+ # Copyright © 2018 Adam Alboyadjian <adam@cassia.tech>
5
+ # Copyright © 2018 Vista Higher Learning, Inc.
6
+ #
7
+ # codebuild-notifier is free software: you can redistribute it
8
+ # and/or modify it under the terms of the GNU General Public
9
+ # License as published by the Free Software Foundation, either
10
+ # version 3 of the License, or (at your option) any later version.
11
+ #
12
+ # codebuild-notifier is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
+ # General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with codebuild-notifier. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ require 'codebuild-notifier'
21
+ require 'optparse'
22
+ # This script updates a DynamoDb table with the last codebuild build status
23
+ # for the current project and branch or pr. If the current build status is
24
+ # different from the previous status, the email address of the author of the
25
+ # last commit is extracted from the git commit, and a notification is sent
26
+ # via slack.
27
+
28
+ def quit(message)
29
+ cb_puts(message)
30
+ exit
31
+ end
32
+
33
+ def cb_puts(message)
34
+ puts "CODEBUILD NOTIFIER: #{message}"
35
+ end
36
+
37
+ def not_latest_commit_in_branch_message(build, config)
38
+ "Commit #{build.commit_hash} in project #{build.project_code} did " \
39
+ 'not match the most recent commit for any Pull Requests or the ' \
40
+ "whitelisted branches: #{config.whitelist}. Skipping status updates."
41
+ end
42
+
43
+ def not_pr_or_whitelisted_branch_message(config)
44
+ 'Build is neither for a Pull Request nor for one of whitelisted ' \
45
+ "branches: #{config.whitelist}. Skipping status updates."
46
+ end
47
+
48
+ def no_status_diff_message(build)
49
+ "Current status #{build.status} is same as last status. " \
50
+ 'Skipping slack notifications.'
51
+ end
52
+
53
+ command_line_opts = {}
54
+
55
+ # rubocop:disable Metrics/BlockLength
56
+ OptionParser.new do |opts|
57
+ opts.banner = 'Usage: update-build-status [OPTIONS]'
58
+
59
+ opts.on(
60
+ '--additional-channel=CHANNEL',
61
+ 'status notifications for whitelisted branches will be sent here ' \
62
+ 'as well as to author/committer'
63
+ ) { |usernames| command_line_opts[:slack_admin_users] = usernames }
64
+
65
+ opts.on(
66
+ '--slack-admin-usernames=USERS',
67
+ 'comma-separated list of slack users to be notified if build status ' \
68
+ 'notifications fail to send'
69
+ ) { |usernames| command_line_opts[:slack_admin_users] = usernames }
70
+
71
+ opts.on(
72
+ '--dynamo-table=TABLE',
73
+ 'table for storing build statuses'
74
+ ) { |table| command_line_opts[:dynamo_table] = table }
75
+
76
+ opts.on(
77
+ '--slack-secret-name=SECRET',
78
+ 'name of Secrets Manager secret with slack app/bot auth token'
79
+ ) { |slack_secret| command_line_opts[:slack_secret_name] = slack_secret }
80
+
81
+ opts.on(
82
+ '--whitelist-branches=BRANCHES',
83
+ 'comma-separated list of branches that will have build notifications ' \
84
+ 'sent even if there is no open Pull Request'
85
+ ) { |whitelist| command_line_opts[:whitelist_branches] = whitelist }
86
+
87
+ opts.on('--region=REGION', 'AWS region') do |region|
88
+ command_line_opts[:region] = region
89
+ end
90
+ end.parse!
91
+ # rubocop:enable Metrics/BlockLength
92
+
93
+ config = CodeBuildNotifier::Config.new(command_line_opts)
94
+ build = CodeBuildNotifier::CurrentBuild.new
95
+ history = CodeBuildNotifier::BuildHistory.new(config, build)
96
+
97
+ last_build = history.last_entry
98
+
99
+ if build.launched_by_retry?
100
+ # Whenever a build is triggered by a PR or whitelisted branch, we update
101
+ # the record for that trigger with the commit hash. If a build is then
102
+ # launched using Retry, the status is updated and are notifications sent
103
+ # only if the re-tried build was for the latest commit. Otherwise re-trying
104
+ # an older commit could result in inaccurate notifications.
105
+ quit(not_latest_commit_in_branch_message(build, config)) unless last_build
106
+
107
+ source_id = last_build.source_id
108
+ source_ref = last_build.source_ref
109
+ else
110
+ # We only want to track information for whitelisted branches and branches
111
+ # with open Pull Requests.
112
+ unless config.non_pr_branch_ids.include?(build.trigger) || build.for_pr?
113
+ quit(not_pr_or_whitelisted_branch_message(config))
114
+ end
115
+ source_id = build.source_id
116
+ source_ref = build.trigger
117
+ end
118
+
119
+ # Update record for this project + branch/pr in DynamoDb even if the
120
+ # status hasn't changed, so the latest commit hash is stored.
121
+ history.write_entry(source_id) do |new_item|
122
+ cb_puts "Updating dynamo table #{config.dynamo_table} with: #{new_item}"
123
+ end
124
+
125
+ quit(no_status_diff_message(build)) if last_build&.status == build.status
126
+
127
+ slack_message = CodeBuildNotifier::SlackMessage.new(build, config, source_ref)
128
+ sender = CodeBuildNotifier::SlackSender.new(config)
129
+ sender.send(slack_message)
@@ -0,0 +1,50 @@
1
+ # codebuild-notifier
2
+ # Copyright © 2018 Adam Alboyadjian <adam@cassia.tech>
3
+ # Copyright © 2018 Vista Higher Learning, Inc.
4
+ #
5
+ # codebuild-notifier is free software: you can redistribute it
6
+ # and/or modify it under the terms of the GNU General Public
7
+ # License as published by the Free Software Foundation, either
8
+ # version 3 of the License, or (at your option) any later version.
9
+ #
10
+ # codebuild-notifier is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with codebuild-notifier. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ lib = File.expand_path('lib', __dir__)
19
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
20
+
21
+ require 'codebuild-notifier/version'
22
+
23
+ Gem::Specification.new do |spec|
24
+ spec.name = 'codebuild-notifier'
25
+ spec.version = CodeBuildNotifier::VERSION
26
+ spec.authors = ['VHL Ops Team']
27
+ spec.email = ['ops@vistahigherlearning.com']
28
+ spec.summary = 'Slack notifications for CodeBuild jobs.'
29
+ spec.description = 'CodeBuild Notifier tracks the past status of CI jobs ' \
30
+ 'run on AWS CodeBuild for each pr or whitelisted branch' \
31
+ 'for a given project, and sends slack notifications ' \
32
+ 'when a branch/pr changes build status.'
33
+ spec.homepage = 'https://github.com/vhl/codebuild-notifier'
34
+ spec.executables = ['update-build-status']
35
+
36
+ spec.files = `git ls-files -z`.split("\x0").reject do |file|
37
+ file.match(%r{^(test|spec|features)/})
38
+ end
39
+
40
+ spec.add_dependency 'activesupport', '> 2.0', '< 6.0'
41
+ spec.add_dependency 'aws-sdk-dynamodb', '~> 1.16'
42
+ spec.add_dependency 'aws-sdk-secretsmanager', '~> 1.19'
43
+ spec.add_dependency 'hashie', '> 1.0', '< 4.0'
44
+ spec.add_dependency 'slack-ruby-client', '~> 0.13'
45
+
46
+ spec.add_development_dependency 'rspec', '~> 3.8'
47
+ spec.add_development_dependency 'rubocop', '0.58.2'
48
+ spec.add_development_dependency 'rubocop-rspec', '1.30.0'
49
+ spec.add_development_dependency 'simplecov', '~> 0.16'
50
+ end
@@ -0,0 +1,24 @@
1
+ # codebuild-notifier
2
+ # Copyright © 2018 Adam Alboyadjian <adam@cassia.tech>
3
+ # Copyright © 2018 Vista Higher Learning, Inc.
4
+ #
5
+ # codebuild-notifier is free software: you can redistribute it
6
+ # and/or modify it under the terms of the GNU General Public
7
+ # License as published by the Free Software Foundation, either
8
+ # version 3 of the License, or (at your option) any later version.
9
+ #
10
+ # codebuild-notifier is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with codebuild-notifier. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'codebuild-notifier/config'
19
+ require 'codebuild-notifier/build_history'
20
+ require 'codebuild-notifier/current_build'
21
+ require 'codebuild-notifier/git'
22
+ require 'codebuild-notifier/slack_message'
23
+ require 'codebuild-notifier/slack_sender'
24
+ require 'codebuild-notifier/version'