codebuild-notifier 0.3.2 → 1.0.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.
- 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
|