Skip to content

Commit 3c86048

Browse files
committed
Automating CodeCommit Change Notifications
1 parent fbdf8ba commit 3c86048

File tree

4 files changed

+248
-0
lines changed

4 files changed

+248
-0
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Automating CodeCommit Change Notifications
2+
3+
In this lesson, we'll demonstrate how to receive detailed email notifications
4+
about file changes and commit messages when a code update is pushed to
5+
CodeCommit. A code reviewer may subscribe to the SNS topic and recieve updates
6+
for any changes.
7+
8+
## Create the CodeCommit Repository
9+
10+
```sh
11+
aws codecommit create-repository --repository-name ChangeNotification
12+
```
13+
14+
Note the `cloneUrlHttp` and `Arn` values in the response.
15+
16+
## Create and Subscribe to the SNS Topic
17+
18+
## Create IAM Lambda Execution Role
19+
20+
1. Add `AWSLambdaBasicExecutionRole`
21+
2. Add the following policy `LambdaCodeCommitSnsPolicy`:
22+
23+
```json
24+
{
25+
"Version": "2012-10-17",
26+
"Statement": [{
27+
"Effect": "Allow",
28+
"Action": [
29+
"codecommit:*",
30+
"sns:*"
31+
],
32+
"Resource": "*"
33+
}]
34+
}
35+
```
36+
37+
## Create the Lambda Function
38+
39+
Name: **CodeCommitChangeNotification**
40+
41+
## Create the CloudWatch Event Rule
42+
43+
This rule will detect branch or repository changes:
44+
45+
1. Choose **Event Pattern**.
46+
2. Service Name: **CodeCommit**
47+
3. Event Type: **CodeCommit Repository State Change**
48+
4. Specific resource(s) by ARN: **CodeCommit Repository ARN**
49+
50+
Select the **referenceCreated** and **referenceUpdated** events.
51+
52+
Event Pattern:
53+
54+
```json
55+
{
56+
"source": [
57+
"aws.codecommit"
58+
],
59+
"detail-type": [
60+
"CodeCommit Repository State Change"
61+
],
62+
"resources": [
63+
"arn:aws:codecommit:us-east-1:123456789012:ChangeNotification"
64+
]
65+
}
66+
```
67+
68+
5. Target: **CodeCommitChangeNotification**
69+
70+
## Commit a Change
71+
72+
1. Create and commit a file.
73+
2. Edit the file and commit it.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"Version": "2012-10-17",
3+
"Statement": [{
4+
"Effect": "Allow",
5+
"Action": [
6+
"codecommit:*",
7+
"sns:*"
8+
],
9+
"Resource": "*"
10+
}]
11+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import datetime
2+
import json
3+
import os
4+
5+
import boto3
6+
7+
AWS_DEFAULT_REGION = os.environ["AWS_DEFAULT_REGION"]
8+
MAIN_BRANCH_NAME = os.getenv('MAIN_BRANCH_NAME', 'master')
9+
REPOSITORY_NAME = os.environ['REPOSITORY_NAME']
10+
SNS_TOPIC_ARN = os.environ['SNS_TOPIC_ARN']
11+
12+
codecommit = boto3.client('codecommit')
13+
sns = boto3.client('sns')
14+
15+
16+
def get_diff(repository_name, last_commit_id, previous_commit_id):
17+
response = None
18+
19+
if previous_commit_id is not None:
20+
response = codecommit.get_differences(
21+
repositoryName=repository_name,
22+
beforeCommitSpecifier=previous_commit_id,
23+
afterCommitSpecifier=last_commit_id
24+
)
25+
else:
26+
# This was the first commit (no previous, omit beforeCommitSpecifier)
27+
response = codecommit.get_differences(
28+
repositoryName=repository_name,
29+
afterCommitSpecifier=last_commit_id
30+
)
31+
32+
differences = []
33+
34+
if response is None:
35+
return differences
36+
37+
while "nextToken" in response:
38+
response = codecommit.get_differences(
39+
repositoryName=repository_name,
40+
beforeCommitSpecifier=previous_commit_id,
41+
afterCommitSpecifier=last_commit_id,
42+
nextToken=response["nextToken"]
43+
)
44+
differences += response.get("differences", [])
45+
else:
46+
differences += response["differences"]
47+
48+
return differences
49+
50+
51+
def get_diff_change_message_type(change_type):
52+
type = {
53+
'M': 'Modification',
54+
'D': 'Deletion',
55+
'A': 'Addition'
56+
}
57+
return type[change_type]
58+
59+
60+
def get_last_commit_id(repository, branch):
61+
response = codecommit.get_branch(
62+
repositoryName=repository,
63+
branchName=branch
64+
)
65+
commit_id = response['branch']['commitId']
66+
return commit_id
67+
68+
69+
def get_last_commit_log(repository, commit_id):
70+
response = codecommit.get_commit(
71+
repositoryName=repository, commitId=commit_id)
72+
73+
return response['commit']
74+
75+
76+
def get_message_text(differences, last_commit):
77+
print(last_commit)
78+
text = ''
79+
commit_id = last_commit['commitId']
80+
text += f'commit ID: {commit_id}\n'
81+
text += 'author: {0} ({1})\n'.format(
82+
last_commit['author']['name'],
83+
last_commit['author']['email'])
84+
date = last_commit['author']['date'].split()[0]
85+
t_utc = datetime.datetime.utcfromtimestamp(int(date))
86+
timestamp = t_utc.strftime('%Y-%m-%d %H:%M:%S UTC')
87+
text += f'date: {timestamp}\n'
88+
text += f"message: {last_commit['message']}\n"
89+
for diff in differences:
90+
if 'afterBlob' in diff:
91+
text += 'After - File: {0} {1} - Blob ID: {2}\n'.format(
92+
diff['afterBlob']['path'],
93+
get_diff_change_message_type(diff['changeType']),
94+
diff['afterBlob']['blobId'])
95+
if 'beforeBlob' in diff:
96+
text += 'Before - File: {0} {1} - Blob ID: {2}\n'.format(
97+
diff['beforeBlob']['path'],
98+
get_diff_change_message_type(diff['changeType']),
99+
diff['beforeBlob']['blobId'])
100+
101+
text += (f'Commit: https://{AWS_DEFAULT_REGION}.console.aws.amazon.com/codesuite/'
102+
f'codecommit/repositories/{REPOSITORY_NAME}/commit/{commit_id}?'
103+
f'region={AWS_DEFAULT_REGION}')
104+
105+
return text
106+
107+
108+
def publish(repository, message):
109+
sns.publish(
110+
TopicArn=SNS_TOPIC_ARN,
111+
Subject='CodeCommit Update - Repository: {0}'.format(repository),
112+
Message=message
113+
)
114+
115+
116+
def lambda_handler(event, context):
117+
print(json.dumps(event))
118+
119+
repository = REPOSITORY_NAME
120+
121+
try:
122+
last_commit_id = get_last_commit_id(repository, MAIN_BRANCH_NAME)
123+
last_commit = get_last_commit_log(repository, last_commit_id)
124+
125+
previous_commit_id = None
126+
if len(last_commit['parents']) > 0:
127+
previous_commit_id = last_commit['parents'][0]
128+
129+
print(f'Last commit ID: {last_commit_id}')
130+
print(f'Previous commit ID: {previous_commit_id}')
131+
132+
differences = get_diff(repository, last_commit_id, previous_commit_id)
133+
message_text = get_message_text(differences, last_commit)
134+
135+
return publish(repository, message_text)
136+
137+
except Exception as e:
138+
import traceback
139+
print(e)
140+
traceback.print_exc()
141+
print(f'Error getting repository {repository}. Ensure it exists in the '
142+
'same region as this function.')
143+
raise e
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"version": "0",
3+
"id": "c795db7d-0239-f083-da1e-6d3ba7ab8c9e",
4+
"detail-type": "CodeCommit Repository State Change",
5+
"source": "aws.codecommit",
6+
"account": "123456789012",
7+
"time": "2019-03-15T14:40:40Z",
8+
"region": "us-east-1",
9+
"resources": ["arn:aws:codecommit:us-east-2:123456789012:ChangeNotification"],
10+
"detail": {
11+
"referenceFullName": "refs/heads/master",
12+
"repositoryId": "a2d1f4ae-02d0-4c1c-8bd3-cba39b73ed90",
13+
"referenceType": "branch",
14+
"commitId": "91bf2aa7c2d04523df8b7cfbfee20c53d7be77e4",
15+
"event": "referenceUpdated",
16+
"repositoryName": "ChangeNotification",
17+
"callerUserArn": "arn:aws:iam::123456789012:user/mrichman",
18+
"oldCommitId": "b4940d6b9ac85469a351b4da1769e4301c9c3fc",
19+
"referenceName": "master"
20+
}
21+
}

0 commit comments

Comments
 (0)