10
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-06-11

概要

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を追加します。

10
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?