Google Apps ScriptでGitHubのプルリクエスト作成時に自動的にレビュアーをアサインする

## 概要
Githubでプルリクエストが作成されたらwebhookでGoogle Apps Scriptを実装し、レビュアーをランダムアサインします。

## Githubのアクセストークン作成
Githubの[Personal access tokens] (https://github.com/settings/tokens)から作成します。

1. Generate new tokenから新規作成
2. Full control of private repositoriesの権限を許可



## スプレッドシートを準備
新規でスプレッドシートを作成します。
リポジトリ名とレビューチーム名を入力します。

|No.(A列)|リポジトリ名(B列)|チーム名(C列)|
|:--|:--|:--|
|1|hoge1|team_hoge1|
|2|hoge2|team_hoge2|
|3|hoge3|team_hoge3|
|4|hoge4|team_hoge4|
|5|hoge5|team_hoge5|


## Google Apps Scriptを実装

スプレッドシートのメニューから「ツール -> スクリプトエディタ」を開いて以下のファイルを配置する。
以下の設定値は変更する。

|スクリプトファイル名|定数名|
|:--|:--|
|Spreadsheet.gs|SHEET_NAME|
|Github.gs|OWNER|
|Github.gs|ACCESS_TOKEN|

```Main.gs
/*
 * プルリク作成時にレビュアーをアサインする
 *　＠param request HttpRequest
 */
function doPost(request) {
  
  // JSON形式に変換
  var jsonData = JSON.parse(request.postData.getDataAsString());
  
  // プルリク作成時のみ実行する
  if ("opened" == jsonData["action"] && jsonData["assignees"] != null) {
    // リポジトリ名
    var repository = jsonData["repository"]["name"];
    // プルリク番号
    var number = jsonData["pull_request"]["number"];
    // プルリク作成者
    var sender = jsonData["pull_request"]["user"]["login"];
    
    // レビューするチーム名を取得
    var teamName = fetchTeamName_(repository);
    if (teamName != null) {
      reviewAssign_(repository, teamName, sender, number);
    }
  }
}
```

```Spreadsheet.gs
// シート名
var SHEET_NAME = "シート名";

/*
 * スプレッドシートからチーム名を返却する
 *　＠param repository リポジトリ名
 *　＠return チーム名
 */
function fetchTeamName_(repository) {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);
  // B列を全て取得する(ヘッダー行を考慮して-1をする)
  var values = sheet.getRange(2, 2, sheet.getRange("B:B").getLastRow() - 1, 3).getValues();

  for(var index = 0; index < values.length; index++) {
    if (repository == values[index][0]) {
      // リポジトリ名が一致したらチーム名を返却する
      return　values[index][1];
    }
  }
}
```

```Github.gs
// OWNER
var OWNER = "xxxxxxxx";
// ACCESS_TOKEN
var ACCESS_TOKEN = "xxxxxxxxxxxxxxxxxxxxxxxx";

/*
 * スプレッドシートからチーム名を返却する
 *　＠param repository リポジトリ名
 *　＠param teamName チーム名
 *　＠param sender プルリク作成者
 *　＠param number プルリク番号
 */
function reviewAssign_(repository, teamName, sender, number) {
  // チームIDを取得する
  var teamId =  fetchTeamId_(repository, teamName);

  if (teamId != null) {
    // プルリク作成者を除いたチームメンバーを取得
    var members = fetchReviewAssignMembers_(teamId, sender);
    // レビュアーをランダムで選抜
    var reviewer = members[Math.floor(Math.random() * members.length)];
    reviewAssignRegistration_(repository, number, reviewer);
  } 
}

/*
 * GithubAPIを作成する
 *　＠param url URL
 *　＠return GithubAPI
 */
function createGithubAPI_(url, repository) {
  url = "https://api.github.com" + url;
  url = url.replace(":owner",OWNER);
  url = url.replace(":org",OWNER);
  url = url.replace(":repo",repository);
  return url;
}

/*
 * リクエストパラメータを作成する
 *　＠param method method
 *　＠return リクエストパラメーター
 */
function params_(method) {
  return　params_(method,"");
}

/*
 * リクエストパラメータを作成する
 *　＠param method method
 *　＠param payload payload
 *　＠return リクエストパラメーター
 */
function params_(method, payload) {
  var params =
      {
        "method": method,
        "Content-Type": "application/json",
        "muteHttpExceptions": true,
        "payload": payload,
        "headers": {
          "Authorization":" token " + ACCESS_TOKEN
        }
      };
  return params;
}

/*
 * GithubAPIにアクセスしてチームIDを返却する
 *　＠param repository リポジトリ名
 *　＠param teamName チーム名
 *　＠return チームID
 */
function fetchTeamId_(repository, teamName) {
  // https://developer.github.com/v3/orgs/teams/#list-teams
  var url = createGithubAPI_("/orgs/:org/teams", repository);
  var params = params_("GET");
  var response = JSON.parse(UrlFetchApp.fetch(url, params));

  for(var index = 0; index < response.length; index++) {
    if (response[index]["name"] == teamName) {
      return response[index]["id"];
    }
  }
}

/*
 * チームメンバーを取得してプルリク作成者を除外した一覧を返却する
 *　＠param repository リポジトリ名
 *　＠param teamName チーム名
 *　＠param sender プルリク作成者
 *　＠param number プルリク番号
 *　＠return プルリク作成者以外のメンバー一覧
 */
function fetchReviewAssignMembers_(teamId, sender) {
  // https://developer.github.com/v3/orgs/teams/#list-team-members
  var url = createGithubAPI_("/teams/:id/members", "");
  url = url.replace(":id",teamId);
  var params = params_("GET");
  var response = JSON.parse(UrlFetchApp.fetch(url, params));
  
  var members = [];
  for(var index = 0; index < response.length; index++) {
    // プルリク作成者はレビュアーから除外
    if (sender != response[index]["login"]) {
      members[index] = response[index]["login"];
    }
  }
  return members;
}

/*
 * プルリクにレビュアーをアサインする
 *　＠param repository リポジトリ名
 *　＠param number プルリク番号
 *　＠param reviewer レビュアー
 */
function reviewAssignRegistration_(repository, number, reviewer) {
  // https://developer.github.com/v3/issues/assignees/#add-assignees-to-an-issue
  var url = createGithubAPI_("/repos/:owner/:repo/issues/:number/assignees", repository);
  url = url.replace(":number",number);
  var payload = JSON.stringify({ 
    "assignees" : reviewer
  });
  var params = params_("POST", payload);
  var response = JSON.parse(UrlFetchApp.fetch(url, params));
}

```

## スプレッドシートのAPIを公開
スクリプトエディタのメニューバーから公開できます。
1. 公開 > ウェブアプリケーションとして公開
2. プロジェクト バージョンを入力（例：v.1.0）
3. 次のユーザーとしてアプリケーションを実行は自分を選択
4. アプリケーションにアクセスできるユーザーは全員を選択
5. 現在のウェブ アプリケーションの URLをGithubのWebhooksに設定

## GithubのWebhooksを設定
Githubで自動レビュアーアサインを設定したいリポジトリのSettingsを開きます。
Webhooksでadd webhookを追加します。