codebuild-notifier 0.3.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -9
- data/bin/update-build-status +5 -8
- data/lib/codebuild-notifier.rb +3 -0
- data/lib/codebuild-notifier/branch_entry.rb +54 -0
- data/lib/codebuild-notifier/build_history.rb +68 -53
- data/lib/codebuild-notifier/config.rb +2 -2
- data/lib/codebuild-notifier/current_build.rb +35 -2
- data/lib/codebuild-notifier/dynamo_base.rb +57 -0
- data/lib/codebuild-notifier/git.rb +1 -1
- data/lib/codebuild-notifier/project_summary.rb +125 -0
- data/lib/codebuild-notifier/slack_message.rb +3 -9
- data/lib/codebuild-notifier/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c165c9da8f00e25f828f0eb5839011c2e28f517289429999a56571c431c766cb
|
4
|
+
data.tar.gz: 5b9da99ae167f4ed3e3ce49db33b1f95b738820f5ac9acbacc8c416dd2cec2a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d19c47a78c414223965f09c4221c383aca9faee389b35ec3c924d8603a3ae00076ac9c8b1e9bdae2a01233c46f40facf0076229f5498c4c020ea33e67b8f55d
|
7
|
+
data.tar.gz: d4d63e87575aa32907807cd0157df12da2b60fb6c812bdeca407fb2c6306d34dd2be23ae8f361eab4dcf0879ace21d40711fc75378fee2cdaa34a867a34190e5
|
data/README.md
CHANGED
@@ -25,25 +25,28 @@ will come from a user with a name you choose, e.g. CodeBuildBot
|
|
25
25
|
AWS Secrets Manager instead of the App token.
|
26
26
|
|
27
27
|
### DynamoDB table
|
28
|
-
- expected to be named '
|
28
|
+
- expected to be named 'codebuild-history', but can be configured
|
29
29
|
- the following definition:
|
30
30
|
|
31
31
|
```ruby
|
32
32
|
AttributeDefinitions [
|
33
|
+
{ AttributeName: 'commit_hash', AttributeType: 'S' },
|
33
34
|
{ AttributeName: 'source_id', AttributeType: 'S' },
|
34
|
-
{ AttributeName: '
|
35
|
+
{ AttributeName: 'version_key', AttributeType: 'S' }
|
35
36
|
]
|
36
37
|
GlobalSecondaryIndexes [
|
37
38
|
{
|
38
39
|
IndexName: 'commit_hash_index',
|
39
40
|
KeySchema: [
|
40
|
-
{ AttributeName: 'commit_hash', KeyType: 'HASH' }
|
41
|
+
{ AttributeName: 'commit_hash', KeyType: 'HASH' },
|
42
|
+
{ AttributeName: 'version_key', KeyType: 'RANGE' }
|
41
43
|
],
|
42
|
-
Projection: { ProjectionType: 'ALL' }
|
44
|
+
Projection: { ProjectionType: 'ALL' }
|
43
45
|
}
|
44
46
|
]
|
45
47
|
KeySchema [
|
46
|
-
{ AttributeName: 'source_id', KeyType: 'HASH' }
|
48
|
+
{ AttributeName: 'source_id', KeyType: 'HASH' },
|
49
|
+
{ AttributeName: 'version_key', KeyType: 'RANGE' }
|
47
50
|
]
|
48
51
|
```
|
49
52
|
|
@@ -72,8 +75,8 @@ name:
|
|
72
75
|
],
|
73
76
|
"Effect": "Allow",
|
74
77
|
"Resource": [
|
75
|
-
"arn:aws:dynamodb:<your-region>:<your-account-id>:table/
|
76
|
-
"arn:aws:dynamodb:<your-region>:<your-account-id>:table/
|
78
|
+
"arn:aws:dynamodb:<your-region>:<your-account-id>:table/codebuild-history",
|
79
|
+
"arn:aws:dynamodb:<your-region>:<your-account-id>:table/codbuild-history/*"
|
77
80
|
]
|
78
81
|
},
|
79
82
|
{
|
@@ -221,7 +224,7 @@ phases:
|
|
221
224
|
<nobr>--dynamo-table</nobr>
|
222
225
|
</td>
|
223
226
|
<td>
|
224
|
-
|
227
|
+
codebuild-history
|
225
228
|
</td>
|
226
229
|
<td>
|
227
230
|
This table must be created and permissions granted to it as described
|
@@ -287,7 +290,7 @@ phases:
|
|
287
290
|
<nobr>--whitelist-branches</nobr>
|
288
291
|
</td>
|
289
292
|
<td>
|
290
|
-
master
|
293
|
+
master
|
291
294
|
</td>
|
292
295
|
<td>
|
293
296
|
Normally statuses will be stored and notifications sent only for builds
|
data/bin/update-build-status
CHANGED
@@ -122,17 +122,16 @@ end.parse!
|
|
122
122
|
config = CodeBuildNotifier::Config.new(command_line_opts)
|
123
123
|
build = CodeBuildNotifier::CurrentBuild.new
|
124
124
|
history = CodeBuildNotifier::BuildHistory.new(config, build)
|
125
|
-
|
126
125
|
last_build = history.last_entry
|
127
126
|
build.previous_build = last_build
|
128
127
|
|
129
128
|
if build.launched_by_retry?
|
130
129
|
# Whenever a build is triggered by a PR or whitelisted branch, we update
|
131
|
-
# the record for that trigger with the commit hash. If a build is
|
132
|
-
# launched using Retry,
|
133
|
-
#
|
134
|
-
#
|
135
|
-
quit(
|
130
|
+
# the record for that trigger with the commit hash. If a build is
|
131
|
+
# launched using Retry, and there is no history entry for the current
|
132
|
+
# commit hash, then the build is not for a PR or a whitelisted branch,
|
133
|
+
# and should not be tracked.
|
134
|
+
quit(not_pr_or_whitelisted_branch_message(config)) unless last_build
|
136
135
|
else
|
137
136
|
# We only want to track information for whitelisted branches and branches
|
138
137
|
# with open Pull Requests.
|
@@ -141,8 +140,6 @@ else
|
|
141
140
|
end
|
142
141
|
end
|
143
142
|
|
144
|
-
# Update record for this project + branch/pr in DynamoDb even if the
|
145
|
-
# status hasn't changed, so the latest commit hash is stored.
|
146
143
|
history.write_entry(build.source_id) do |new_item|
|
147
144
|
cb_puts "Updating dynamo table #{config.dynamo_table} with: #{new_item}"
|
148
145
|
end
|
data/lib/codebuild-notifier.rb
CHANGED
@@ -16,9 +16,12 @@
|
|
16
16
|
# along with codebuild-notifier. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
|
18
18
|
require 'codebuild-notifier/config'
|
19
|
+
require 'codebuild-notifier/dynamo_base'
|
20
|
+
require 'codebuild-notifier/branch_entry'
|
19
21
|
require 'codebuild-notifier/build_history'
|
20
22
|
require 'codebuild-notifier/current_build'
|
21
23
|
require 'codebuild-notifier/git'
|
24
|
+
require 'codebuild-notifier/project_summary'
|
22
25
|
require 'codebuild-notifier/slack_message'
|
23
26
|
require 'codebuild-notifier/slack_sender'
|
24
27
|
require 'codebuild-notifier/version'
|
@@ -0,0 +1,54 @@
|
|
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 'digest'
|
19
|
+
|
20
|
+
module CodeBuildNotifier
|
21
|
+
class BranchEntry < DynamoBase
|
22
|
+
# Creates entries in a partition key with hardcoded primary key.
|
23
|
+
# Within the partition key, there is one record for each github repo.
|
24
|
+
# The 'projects' field in that record is a Map data type, keyed off
|
25
|
+
# project code, containing the status of the last build for that code.
|
26
|
+
def update
|
27
|
+
updates = hash_to_dynamo_update(branch_entry).merge(
|
28
|
+
key: { source_id: source_id, version_key: version_key }
|
29
|
+
)
|
30
|
+
yield updates if block_given?
|
31
|
+
update_item(updates)
|
32
|
+
end
|
33
|
+
|
34
|
+
private def branch_entry
|
35
|
+
{
|
36
|
+
branch_name: current_build.branch_name,
|
37
|
+
build_id: current_build.build_id,
|
38
|
+
commit_hash: source_id,
|
39
|
+
git_repo_url: current_build.git_repo_url,
|
40
|
+
source_ref: current_build.source_ref,
|
41
|
+
status: current_build.status,
|
42
|
+
timestamp: current_build.start_time.to_i
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
private def source_id
|
47
|
+
current_build.project_code
|
48
|
+
end
|
49
|
+
|
50
|
+
private def version_key
|
51
|
+
Digest::MD5.hexdigest(current_build.source_ref)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -15,74 +15,94 @@
|
|
15
15
|
# You should have received a copy of the GNU General Public License
|
16
16
|
# along with codebuild-notifier. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
|
18
|
-
require 'active_support'
|
19
|
-
require 'active_support/core_ext'
|
20
|
-
require 'aws-sdk-dynamodb'
|
21
|
-
require 'hashie'
|
22
|
-
|
23
18
|
module CodeBuildNotifier
|
24
|
-
class BuildHistory
|
25
|
-
attr_reader :config, :current_build
|
26
|
-
|
27
|
-
delegate :dynamo_table, to: :config
|
19
|
+
class BuildHistory < DynamoBase
|
28
20
|
delegate :branch_name, :launched_by_retry?, to: :current_build
|
29
21
|
|
30
|
-
def initialize(config, current_build)
|
31
|
-
@config = config
|
32
|
-
@current_build = current_build
|
33
|
-
end
|
34
|
-
|
35
22
|
def last_entry
|
23
|
+
return @last_entry if defined?(@last_entry)
|
36
24
|
# If this build was launched using the Retry command from the console
|
37
25
|
# or api we don't have a Pull Request or branch name to use in the
|
38
26
|
# primary key, so we query by commit hash and project instead.
|
39
|
-
item = launched_by_retry? ?
|
27
|
+
item = launched_by_retry? ? find_by_commit : find_by_id
|
40
28
|
|
41
29
|
# Provide .dot access to hash values from Dynamo item.
|
42
|
-
item && Hashie::Mash.new(item)
|
30
|
+
@last_entry = item && Hashie::Mash.new(item)
|
43
31
|
end
|
44
32
|
|
45
33
|
def write_entry(source_id)
|
46
34
|
updates = hash_to_dynamo_update(new_entry).merge(
|
47
|
-
key: { source_id: source_id }
|
35
|
+
key: { source_id: source_id, version_key: version_key }
|
48
36
|
)
|
49
37
|
|
50
38
|
yield updates if block_given?
|
51
39
|
|
52
|
-
|
53
|
-
|
54
|
-
)
|
40
|
+
update_item(updates)
|
41
|
+
|
42
|
+
ProjectSummary.new(config, current_build).update
|
43
|
+
BranchEntry.new(config, current_build).update
|
55
44
|
end
|
56
45
|
|
57
46
|
# The commit hash and project code are used to find which Pull Request
|
58
47
|
# or branch the current build belongs to, and the previous build status
|
59
48
|
# for that Pull Request or branch.
|
60
|
-
private def
|
61
|
-
|
62
|
-
expression_attribute_values:
|
63
|
-
|
64
|
-
':project_code' => current_build.project_code
|
65
|
-
},
|
66
|
-
filter_expression: 'project_code = :project_code',
|
49
|
+
private def find_by_commit
|
50
|
+
find_latest_version(
|
51
|
+
expression_attribute_values: commit_values,
|
52
|
+
filter_expression: commit_filter,
|
67
53
|
index_name: 'commit_hash_index',
|
68
|
-
key_condition_expression: 'commit_hash = :commit_hash'
|
69
|
-
|
70
|
-
|
54
|
+
key_condition_expression: 'commit_hash = :commit_hash'
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
# When searching by commit hash, if the current build source version
|
59
|
+
# is for a PR, only return commits with that PR as the source ref.
|
60
|
+
# If source version is not for a pr, only return source refs beginning
|
61
|
+
# with branch/. This helps protect against the edge case where the same
|
62
|
+
# commit appears in two different PRs, or in a PR and whitelisted branch
|
63
|
+
# besides than the PR head.
|
64
|
+
private def commit_values
|
65
|
+
source_ref_val = if current_build.for_pr?
|
66
|
+
current_build.source_version
|
67
|
+
else
|
68
|
+
'branch/'
|
69
|
+
end
|
70
|
+
{
|
71
|
+
':commit_hash' => current_build.commit_hash,
|
72
|
+
':project_code' => current_build.project_code,
|
73
|
+
':source_ref' => source_ref_val
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
private def commit_filter
|
78
|
+
source_ref_condition = if current_build.for_pr?
|
79
|
+
'source_ref = :source_ref'
|
80
|
+
else
|
81
|
+
'begins_with(source_ref, :source_ref)'
|
82
|
+
end
|
83
|
+
"project_code = :project_code AND #{source_ref_condition}"
|
71
84
|
end
|
72
85
|
|
73
86
|
private def find_by_id
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
87
|
+
find_latest_version(
|
88
|
+
expression_attribute_values: {
|
89
|
+
':source_id' => current_build.source_id
|
90
|
+
},
|
91
|
+
key_condition_expression: 'source_id = :source_id'
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
private def find_latest_version(args)
|
96
|
+
dynamo_client.query(
|
97
|
+
args.merge(
|
98
|
+
scan_index_forward: false, # Reverse sort by range key
|
99
|
+
table_name: dynamo_table
|
100
|
+
)
|
101
|
+
).items.first
|
78
102
|
end
|
79
103
|
|
80
104
|
private def new_entry
|
81
|
-
|
82
|
-
commit_hash: current_build.commit_hash,
|
83
|
-
project_code: current_build.project_code,
|
84
|
-
status: current_build.status
|
85
|
-
}.tap do |memo|
|
105
|
+
current_build.history_fields.tap do |memo|
|
86
106
|
# If launched via manual re-try instead of via a webhook, we don't
|
87
107
|
# want to overwrite the current source_ref value that tells us which
|
88
108
|
# branch or pull request originally created the dynamo record.
|
@@ -93,21 +113,16 @@ module CodeBuildNotifier
|
|
93
113
|
end
|
94
114
|
end
|
95
115
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
116
|
+
# The first component of the version_key is the timestamp for the
|
117
|
+
# first build of the current commit hash. The second component
|
118
|
+
# is the timestamp for the current build. This allows easily finding
|
119
|
+
# either the latest commit, or the latest re-build of a commit.
|
120
|
+
private def version_key
|
121
|
+
if launched_by_retry?
|
122
|
+
"#{last_entry.start_time}_#{current_build.start_time}"
|
123
|
+
else
|
124
|
+
"#{current_build.start_time}_#{current_build.start_time}"
|
105
125
|
end
|
106
|
-
update.merge(update_expression: "SET #{update[:update_expression].join(', ')}")
|
107
|
-
end
|
108
|
-
|
109
|
-
private def dynamo_client
|
110
|
-
@dynamo_client || Aws::DynamoDB::Client.new(region: config.region)
|
111
126
|
end
|
112
127
|
end
|
113
128
|
end
|
@@ -17,7 +17,7 @@
|
|
17
17
|
|
18
18
|
module CodeBuildNotifier
|
19
19
|
class Config
|
20
|
-
DEFAULT_WHITELIST = %w[master
|
20
|
+
DEFAULT_WHITELIST = %w[master]
|
21
21
|
|
22
22
|
attr_reader :additional_channel, :default_strategy, :dynamo_table, :region,
|
23
23
|
:slack_admins, :slack_secret_name, :whitelist_branches
|
@@ -27,7 +27,7 @@ module CodeBuildNotifier
|
|
27
27
|
def initialize(
|
28
28
|
additional_channel: ENV['CBN_ADDITIONAL_CHANNEL'],
|
29
29
|
default_strategy: ENV['CBN_DEFAULT_NOTIFY_STRATEGY'] || 'fail_or_status_change',
|
30
|
-
dynamo_table: ENV['CBN_DYNAMO_TABLE'] || '
|
30
|
+
dynamo_table: ENV['CBN_DYNAMO_TABLE'] || 'codebuild-history',
|
31
31
|
region: ENV['CBN_AWS_REGION'] || ENV['AWS_REGION'],
|
32
32
|
slack_admins: ENV['CBN_SLACK_ADMIN_USERNAMES'],
|
33
33
|
slack_secret_name: ENV['CBN_SLACK_SECRET_NAME'] || 'slack/codebuild',
|
@@ -17,8 +17,13 @@
|
|
17
17
|
|
18
18
|
module CodeBuildNotifier
|
19
19
|
class CurrentBuild
|
20
|
-
attr_reader :build_id, :commit_hash, :git_repo_url, :status_code, :trigger
|
21
20
|
attr_accessor :previous_build
|
21
|
+
attr_reader :build_id, :commit_hash, :git_repo_url, :source_version,
|
22
|
+
:start_time, :status_code, :trigger
|
23
|
+
|
24
|
+
# attrs from git info
|
25
|
+
attr_reader :author_email, :author_name, :committer_email, :committer_name,
|
26
|
+
:commit_message_subject, :short_hash
|
22
27
|
|
23
28
|
# Default values are extracted from CODEBUILD_* ENV vars present in each
|
24
29
|
# CodeBuild # job container.
|
@@ -27,7 +32,9 @@ module CodeBuildNotifier
|
|
27
32
|
commit_hash: ENV['CODEBUILD_RESOLVED_SOURCE_VERSION'],
|
28
33
|
git_repo: ENV['CODEBUILD_SOURCE_REPO_URL'],
|
29
34
|
head_ref: ENV['CODEBUILD_WEBHOOK_HEAD_REF'],
|
35
|
+
start_time: ENV['CODEBUILD_START_TIME'],
|
30
36
|
status_code: ENV['CODEBUILD_BUILD_SUCCEEDING'],
|
37
|
+
source_version: ENV['CODEBUILD_SOURCE_VERSION'],
|
31
38
|
trigger: ENV['CODEBUILD_WEBHOOK_TRIGGER']
|
32
39
|
)
|
33
40
|
@build_id = build_id
|
@@ -35,8 +42,14 @@ module CodeBuildNotifier
|
|
35
42
|
# Handle repos specified with and without optional .git suffix.
|
36
43
|
@git_repo_url = git_repo.to_s.gsub(/\.git\z/, '')
|
37
44
|
@head_ref = head_ref
|
45
|
+
@source_version = source_version
|
46
|
+
@start_time = start_time || (Time.now.to_f * 1_000).to_i
|
38
47
|
@status_code = status_code
|
39
48
|
@trigger = trigger
|
49
|
+
|
50
|
+
@short_hash, @author_name, @author_email,
|
51
|
+
@committer_name, @committer_email,
|
52
|
+
@commit_message_subject = git_info
|
40
53
|
end
|
41
54
|
|
42
55
|
# If launched via retry, the webhook head ref env var is blank,
|
@@ -65,7 +78,7 @@ module CodeBuildNotifier
|
|
65
78
|
end
|
66
79
|
|
67
80
|
def for_pr?
|
68
|
-
%r{^pr/}.match?(
|
81
|
+
%r{^pr/}.match?(source_version.to_s)
|
69
82
|
end
|
70
83
|
|
71
84
|
# source_id, the primary key, is a composite of project_code and
|
@@ -97,5 +110,25 @@ module CodeBuildNotifier
|
|
97
110
|
trigger
|
98
111
|
end
|
99
112
|
end
|
113
|
+
|
114
|
+
def history_fields
|
115
|
+
{
|
116
|
+
author_email: author_email,
|
117
|
+
author_name: author_name,
|
118
|
+
build_id: build_id,
|
119
|
+
commit_hash: commit_hash,
|
120
|
+
commit_subject: commit_message_subject,
|
121
|
+
committer_email: committer_email,
|
122
|
+
committer_name: committer_name,
|
123
|
+
git_repo_url: git_repo_url,
|
124
|
+
project_code: project_code,
|
125
|
+
start_time: start_time,
|
126
|
+
status: status
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
private def git_info
|
131
|
+
Git.current_commit
|
132
|
+
end
|
100
133
|
end
|
101
134
|
end
|
@@ -0,0 +1,57 @@
|
|
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 'active_support'
|
19
|
+
require 'active_support/core_ext'
|
20
|
+
require 'aws-sdk-dynamodb'
|
21
|
+
require 'hashie'
|
22
|
+
|
23
|
+
module CodeBuildNotifier
|
24
|
+
class DynamoBase
|
25
|
+
attr_reader :config, :current_build
|
26
|
+
|
27
|
+
delegate :dynamo_table, to: :config
|
28
|
+
|
29
|
+
def initialize(config, build)
|
30
|
+
@config = config
|
31
|
+
@current_build = build
|
32
|
+
end
|
33
|
+
|
34
|
+
private def update_item(updates)
|
35
|
+
dynamo_client.update_item(
|
36
|
+
updates.merge(table_name: dynamo_table)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
private def dynamo_client
|
41
|
+
@dynamo_client || Aws::DynamoDB::Client.new(region: config.region)
|
42
|
+
end
|
43
|
+
|
44
|
+
private def hash_to_dynamo_update(hash)
|
45
|
+
update = hash.each_with_object(
|
46
|
+
expression_attribute_names: {},
|
47
|
+
expression_attribute_values: {},
|
48
|
+
update_expression: []
|
49
|
+
) do |(key, value), memo|
|
50
|
+
memo[:expression_attribute_names]["##{key}"] = key.to_s
|
51
|
+
memo[:expression_attribute_values][":#{key}"] = value
|
52
|
+
memo[:update_expression] << "##{key} = :#{key}"
|
53
|
+
end
|
54
|
+
update.merge(update_expression: "SET #{update[:update_expression].join(', ')}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,125 @@
|
|
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 'digest'
|
19
|
+
|
20
|
+
module CodeBuildNotifier
|
21
|
+
class ProjectSummary < DynamoBase
|
22
|
+
# Creates entries in a partition key with hardcoded primary key.
|
23
|
+
# Within the partition key, there is one record for each github repo.
|
24
|
+
# The 'projects' field in that record is a Map data type, keyed off
|
25
|
+
# project code, containing the status of the last build for that code.
|
26
|
+
def update
|
27
|
+
return unless whitelisted_branch?
|
28
|
+
|
29
|
+
updates = project_summary_entry
|
30
|
+
yield updates if block_given?
|
31
|
+
update_item(updates)
|
32
|
+
end
|
33
|
+
|
34
|
+
private def whitelisted_branch?
|
35
|
+
config.whitelist_branches.include?(current_build.branch_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
private def project_summary_entry
|
39
|
+
{
|
40
|
+
key: { source_id: source_id, version_key: version_key },
|
41
|
+
expression_attribute_names: attr_names,
|
42
|
+
expression_attribute_values: attr_values,
|
43
|
+
update_expression: update_expression
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
private def new_record?
|
48
|
+
return @new_record if defined?(@new_record)
|
49
|
+
|
50
|
+
item = dynamo_client.get_item(
|
51
|
+
key: {
|
52
|
+
'source_id' => source_id, 'version_key' => version_key
|
53
|
+
},
|
54
|
+
table_name: dynamo_table
|
55
|
+
).item
|
56
|
+
@new_record = item.nil?
|
57
|
+
end
|
58
|
+
|
59
|
+
private def source_id
|
60
|
+
'project_summary'
|
61
|
+
end
|
62
|
+
|
63
|
+
# The repo url isn't a good format to use as a URL param.
|
64
|
+
private def version_key
|
65
|
+
Digest::MD5.hexdigest(current_build.git_repo_url)
|
66
|
+
end
|
67
|
+
|
68
|
+
private def attr_names
|
69
|
+
{
|
70
|
+
'#commit_hash' => 'commit_hash',
|
71
|
+
'#git_repo_url' => 'git_repo_url',
|
72
|
+
'#timestamp' => 'timestamp',
|
73
|
+
'#projects' => 'projects'
|
74
|
+
}.merge(project_code_attr_name)
|
75
|
+
end
|
76
|
+
|
77
|
+
# For an existing record, the project key already exists, so
|
78
|
+
# an attribute name is needed to be able to update the nested item
|
79
|
+
# path. For a new record, the project code is specified as the root
|
80
|
+
# key of the value assigned to the projects field.
|
81
|
+
private def project_code_attr_name
|
82
|
+
if new_record?
|
83
|
+
{}
|
84
|
+
else
|
85
|
+
{ '#project_code' => current_build.project_code }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private def attr_values
|
90
|
+
{
|
91
|
+
':build_status' => status_value,
|
92
|
+
':commit_hash' => source_id,
|
93
|
+
':git_repo_url' => current_build.git_repo_url,
|
94
|
+
':timestamp' => current_build.start_time.to_i
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
private def status_map
|
99
|
+
{
|
100
|
+
'build_id' => current_build.build_id,
|
101
|
+
'status' => current_build.status,
|
102
|
+
'timestamp' => current_build.start_time.to_i
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
# If a record already exists, we can address the nested item path for
|
107
|
+
# the current project directly and just store the updated status.
|
108
|
+
# Otherwise, we have to create a new map object in the projects field.
|
109
|
+
private def status_value
|
110
|
+
if new_record?
|
111
|
+
{ current_build.project_code => status_map }
|
112
|
+
else
|
113
|
+
status_map
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
private def update_expression
|
118
|
+
projects_key = new_record? ? '#projects' : '#projects.#project_code'
|
119
|
+
'SET #commit_hash = :commit_hash, ' \
|
120
|
+
'#timestamp = :timestamp, ' \
|
121
|
+
'#git_repo_url = :git_repo_url, ' \
|
122
|
+
"#{projects_key} = :build_status"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -17,16 +17,14 @@
|
|
17
17
|
|
18
18
|
module CodeBuildNotifier
|
19
19
|
class SlackMessage
|
20
|
-
attr_reader :
|
21
|
-
:commit_message_subject, :config, :short_hash
|
20
|
+
attr_reader :build, :config
|
22
21
|
|
23
|
-
delegate :
|
22
|
+
delegate :author_email, :author_name, :committer_email,
|
23
|
+
:commit_message_subject, :short_hash, :source_ref, to: :build
|
24
24
|
|
25
25
|
def initialize(build, config)
|
26
26
|
@build = build
|
27
27
|
@config = config
|
28
|
-
@short_hash, @author_name, @author_email,
|
29
|
-
@committer_email, @commit_message_subject = git_info
|
30
28
|
end
|
31
29
|
|
32
30
|
def payload
|
@@ -46,10 +44,6 @@ module CodeBuildNotifier
|
|
46
44
|
!build.for_pr? && config.additional_channel
|
47
45
|
end
|
48
46
|
|
49
|
-
private def git_info
|
50
|
-
Git.current_commit
|
51
|
-
end
|
52
|
-
|
53
47
|
private def slack_color
|
54
48
|
{
|
55
49
|
'FAILED' => 'danger',
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: codebuild-notifier
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- VHL Ops Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -167,10 +167,13 @@ files:
|
|
167
167
|
- bin/update-build-status
|
168
168
|
- codebuild-notifier.gemspec
|
169
169
|
- lib/codebuild-notifier.rb
|
170
|
+
- lib/codebuild-notifier/branch_entry.rb
|
170
171
|
- lib/codebuild-notifier/build_history.rb
|
171
172
|
- lib/codebuild-notifier/config.rb
|
172
173
|
- lib/codebuild-notifier/current_build.rb
|
174
|
+
- lib/codebuild-notifier/dynamo_base.rb
|
173
175
|
- lib/codebuild-notifier/git.rb
|
176
|
+
- lib/codebuild-notifier/project_summary.rb
|
174
177
|
- lib/codebuild-notifier/slack_message.rb
|
175
178
|
- lib/codebuild-notifier/slack_sender.rb
|
176
179
|
- lib/codebuild-notifier/version.rb
|