🚀

LambdaからCodeCommitのプルリクの承認状況を問い合わせてSlackに通知する

2023/03/26に公開

動機

  • 複数人でCodeCommitのリポジトリを使用
  • プルリクエスト(PR)を出したときは、出した人以外の全員の承認が揃うことが必要
  • 課題: 誰がどれに承認したかが分からないので、一覧で出したい

構成

  • Lambda(Python)で確認、通知する
  • Slack(Chatbot)から呼び出してSlackに通知する

(今回はプロジェクトの制限もあり、この構成にしていますが、入力や出力の方法は他にもいろいろあります。入力:CodeCommitのEvent、EventBridegeを使う。出力:SNS、Slack Webhookなどなど)
(ChatBotの導入に関してはここでは書きませんのでクラスメソッドさんのブログを読むなどしてください)

Slack通知の例

モザイクだらけなのですが、この例では3人でやっていて、2つのPRの状況が見えています。上のPRはレビュアー2人とも未承認、下のPRは2人とも承認済みという状況です。このときは、ワークフローからLambdaを呼んで結果を受け取る、というやり方でした。

Code

※ Python3.9以上を想定(zoneinfoを使うため。LambdaのPy39にも標準で入っています)
url_codecommit_prの部分はSlackのリンク記法です
※ LabmadaにはAWSCodeCommitReadOnlyポリシーを追加

import boto3
from zoneinfo import ZoneInfo

"""
CodeCommitのRepositoryからPR情報を取得して、
承認した人、してない人を判別して返す
※一度承認したあとに未承認状態になっても検知できない(承認のまま)

出力例
--
1234: <プルリクエストのタイトル>
作成者: USER1 (2023-02-22 13:00)
承認済: USER2
未承認: USER3

パラメータ: members, reviewers
user.name はアカウント名。USERは出力表示名

(actor名に、actorArnの最後の部分を使っている)
'actorArn': 'arn:aws:iam::111122223333:user/user.name',
'actorArn': 'arn:aws:sts::111122223333:assumed-role/user.name/user.name',
"""

members = {
    'user.name1': 'USER1',
    'user.name2': 'USER2',
    'user.name3': 'USER3',
}
reviewers = ['USER1', 'USER2', 'USER3']
repo_name = 'MyRepo'

tokyo = ZoneInfo('Asia/Tokyo')
region = 'ap-northeast-1'


def get_pr_ids(client, repo_name, status='OPEN'):
    """
    Return a list of Pull Request Ids. Sorted desc.
    """
    response = client.list_pull_requests(
        repositoryName=repo_name,
        pullRequestStatus=status
    )
    return sorted(response['pullRequestIds'], reverse=True)


def get_pr_title(client, pullRequestId):
    response = client.get_pull_request(
        pullRequestId=pullRequestId
    )
    return response['pullRequest']['title']


def get_pr_events(client, pullRequestId):
    response = client.describe_pull_request_events(
        pullRequestId=pullRequestId,
    )
    return response['pullRequestEvents']


def set_line(base: str, add: list):
    """
    Join add:list into str,
    then append the string to base.
    """
    return base + '、'.join(add)


def get_approval_status(client, pullRequestId):
    approved = []
    unapproved = reviewers
    out = ''
    out += '\n' + '--'

    title = get_pr_title(client, pullRequestId)
    id_title = f'{pullRequestId}: {title}'
    # out += '\n' + id_title

    # link in Slack message: <http://www.example.com|This message *is* a link>
    url_codecommit_pr = f'https://{region}.console.aws.amazon.com/codesuite/codecommit/repositories/{repo_name}/pull-requests/{pullRequestId}/details?region={region}'
    out += '\n' + f'<{url_codecommit_pr}|{id_title}>'

    # pr_idの全イベント
    events = get_pr_events(client, pullRequestId)

    for event in events:
        event_date = event['eventDate'].astimezone(tokyo).strftime('%Y-%m-%d %H:%M')
        event_type = event['pullRequestEventType']
        actor = event['actorArn'].split('/')[-1]

        if actor_ja := members.get(actor):
            actor = actor_ja

        if event_type == 'PULL_REQUEST_CREATED':
            out += '\n' + f'作成者: {actor} ({event_date})'
        elif event_type == 'PULL_REQUEST_APPROVAL_STATE_CHANGED':
            approved.append(actor)

        # 未承認のreviewerを残す
        unapproved = [i for i in unapproved if i != actor]

    out += '\n' + set_line('承認済: ', approved)
    out += '\n' + set_line('未承認: ', unapproved)
    return out


def lambda_handler(event, context):
    out = ''

    client = boto3.client('codecommit')

    sorted_desc_pr_ids = get_pr_ids(client, repo_name)

    for pullRequestId in sorted_desc_pr_ids:

        out += get_approval_status(client, pullRequestId)

    return out

まとめ

  • Lambda, Chatbotを使ってSlackにCodeCommitプルリクエストの状況を通知できるようにしました
  • 書いている時点でこのやり方は使わなっています。CodeCommitからGithubに移行したためですが、同様のことはGithub APIでできるはずなので試してみたいですね

Discussion