Perl Hackers Hub

第67回 GitHub APIによるチーム開発の効率化 ―基本操作から,GitHub Webhooksの活用まで(2)

この記事を読むのに必要な時間:およそ 3 分

前回の(1)こちらから。

GitHub WebhooksによるSlackとの連携

前回の(1)まではPerlスクリプト単体でGitHub APIを実行する例でしたが,応用編として,GitHubが提供しているGitHub Webhooksを利用してSlackと連携するサンプルアプリケーションを紹介します。

GitHubを活用した開発ワークフロー

Pull Requestやissueなどの通知について,公式のSlack用GitHubアプリケーションを利用するなどして,Slackとの連携を行っているケースがあると思います。また,GitHub Actionsを利用できる環境であれば,GitHub MarketplaceにあるSlack連携などのツールを組み合わせて,任意のワークフローを構築することもできるでしょう。

しかし,GitHub Enterpriseのバージョンの問題でGitHub Actionsを利用できない環境や,既存のツールだけでは実現できない込み入った処理を自分で作成したい場合もあるでしょう。GitHub Webhooksは,そのような場合に活躍します。

GitHub Webhooksの設定

GitHub Webhooksは,GitHubリポジトリへの各種操作(コミットのPush,Pull Requestの作成,issueの作成など)を受けた際に,外部のWebサーバへイベントの通知を行うしくみです。このWebhookとGitHub APIを組み合わせることによって,Webhookの通知を受けたタイミングでSlackに通知したり,任意のリポジトリ操作を行ったりすることができます。

Webhookの設定方法については,公式ドキュメントを参照してください。

Webhookを受信するアプリケーションの作成

では,Webhookを受信するWebアプリケーションを作成しましょう。前回のPull Requestのレビュアーをランダムに設定する処理を組み込んだWebアプリケーションを作成します。

PerlとSlackの連携には,WebService::Slack::WebApiモジュールを用います注1⁠。Webアプリケーションフレームワークには,Mojolicious::Liteを用います注2⁠。

前回の例ではチーム内のメンバーを取得していましたが,今回はチームが存在しない場合の例として,リポジトリのコラボレータからランダムに1人を抽出するように変更します。また,レビュアーがアサインされた直後のタイミングでSlackにその旨を通知します。

use Mojolicious::Lite -signatures;
use JSON;
use Pithub;
use WebService::Slack::WebApi;

my $slack_token = 'my_slack_token_xxxxxxxx';
my $github_token = 'my_github_token_xxxxxxx';
# 連携したいSlackのチャンネルID
my $slack_channel_id = 'XXXXXXXX';
my $pit = Pithub->new(token => $github_token);

post '/:payload' => sub ($c) {
    # トリガしたイベント名
    my $method =
      $c->req->headers->header('X-GitHub-Event');
    # ペイロード(イベントデータ本体のJSON)
    my $payload = eval { decode_json $c->req->body };
    # イベントをトリガした際のアクション
    my $action = $payload->{action};

    # Pull Requestイベント受信時
    if ($method eq 'pull_request') {
        # Pull Request作成時にレビュアーが未設定の場合
        if ($action eq 'opened'
          && !$payload->{requested_reviewers}) {
            update_random_reviewer($payload);
        }
        # レビュアーが設定された場合
        if ($action eq 'review_requested') {
          # アサインされたユーザーをSlackに通知
          post_slack($payload->{requested_reviewer}
            ->{login} . " さんがアサインされました。\n"
            . $payload->{pull_request}->{html_url});
        }
    }

    # レビューコメント受信時
    if ($method eq 'pull_request_review_comment') {
        if ($action =~ /created|edited/) {
            # レビューコメントをSlackに通知
            post_slack($payload->{comment}->{body}
            . "\n" . $payload->{comment}->{html_url});
        }
    }

    $c->render(json => {success => 1});
};

sub update_random_reviewer {
    my $payload = shift;
    my $random_user = random_collaborator($payload);
    my $pull = $payload->{pull_request};

    # GitHub APIで取得したコラボレータをレビュアーに設定
    $pit->repos->pull_requests->reviewers->update(
        repo      => $pull->{base}->{repo}->{name},
        user      => $pull->{base}->{user}->{login},
        data      => { reviewers => [$random_user] },
        pull_request_id => $pull->{number},
    )->response;
}

sub random_collaborator {
    my $payload = shift;

    my $pull = $payload->{pull_request};
    # GitHub APIでリポジトリのコラボレータ一覧を取得
    my $res = $pit->repos->collaborators->list(
        user => $pull->{base}->{user}->{login},
        repo => $pull->{base}->{repo}->{name},
    )->response;
    my $json = decode_json($res->content);
    my @users = ();
    for my $user (@{$json}) {
        # Pull Request作成者以外を対象にする
        if ($user->{login} ne $pull->{user}->{login}) {
            push @users, $user->{login};
        }
    }
    # ランダムに1人抽出
    my $randomized_user = $users[int(rand(@users))];
    return $randomized_user;
}

sub post_slack {
    my $message = shift;
    my $slack_api =
      WebService::Slack::WebApi->new(
        token => $slack_token
      );
    # Slackにメッセージを通知
    $slack_api->chat->post_message(
        channel => $slack_channel_id,
        text    => $message,
    );
}

app->start;

このアプリケーションを,Webhookが受信できる場所に設置します。動作確認は,ローカルホストやNATNetwork Address Translationの内側で動作しているサーバを外部公開することが可能なngrokを利用して行うのが簡単でよいでしょう注3⁠。WebhookのPayload URLには,https://xxxxxxx.ngrok.io/payloadのように/payloadを付けてください。

このように,Webhookを受信するイベントとGitHub APIを組み合わせて追加していくことで,任意の操作の自動化や,好きなタイミングでのSlackなどの外部ツールとの連携が可能となります。前回で解説したように公開リポジトリが作成されたタイミングで通知したり,リポジトリ内のWikiが更新されたら通知したりといったこともできます。

注1)
WebService::Slack::WebApiについて詳しくは,本連載第37回PerlでInfrastructure as Code ─⁠─IaaSやSaaSをコードで自動化を参照してください。
注2)
Mojolicious::Liteについて詳しくは,本連載第33回MojoliciousでかんたんWebアプリケーション開発を参照してください。
注3)
ngrokについては,本連載第44回LINE Messaging APIで作るchatbot ─⁠─LINE::Bot::APIとngrokでお手軽に!でも紹介しています。

まとめ

駆け足になりましたが,GitHub APIを使用してチーム開発をスムーズに進めるためのアイデアを紹介しました。みなさんのプロジェクトに合わせて,これまで手動で行っていた操作を自動化したり,外部ツールとうまく連携させたりすることで,本来のタスクに集中しやすい環境を作っていけるでしょう。

さて,次回の執筆者はutgwkkさんで,テーマは「他言語のライブラリをPerlに移植する」です。お楽しみに。

WEB+DB PRESS

本誌最新号をチェック!
WEB+DB PRESS Vol.123

2021年6月24日発売
B5判/168ページ
定価1,628円
(本体1,480円+税10%)
ISBN978-4-297-12207-2

  • 特集1
    ついに登場!!! より速く,より安全に,より効率的に
    HTTP/3入門
  • 特集2
    Reactベースの柔軟・省設定フレームワーク
    いまどきNext.js
  • 特集3
    脆弱性への緊急対応の手立て
    実践WAF

著者プロフィール

菅井茂樹(すがいいしげき)

SIerを経て2005年よりシーサー株式会社所属。ブログサービスを中心にPerlを使ったWeb開発に携わる。

最近ではレガシーなサービスのクラウド移行・インフラ構築・保守・運用を中心に行っている。趣味はラーメンの食べ歩きと旅行,ここ数年はキャンプにハマり中。

Twitter:@teckl