LoginSignup
13
12

More than 5 years have passed since last update.

JIRAのチケット番号をspreadsheetに記載するgasを書いてみた

Posted at

やりたいこと

弊社では開発の進捗はJIRAで管理、現場からの開発依頼はスプレッドシート、開発と現場のコミュニケーションツールはslackを使っています。

ディレクターはJIRAをスプレッドシートを行ったり来たり、slackに開発の進捗を報告したりしてめんどくさいので、
①JIRAのチケットが発行された、またかチケットのステータスが完了した際にスプレッドシートに情報を転記。
②開発内容をslackんい投稿する。
をGoogle Apps Script(以下gas)で自動化しました。

そのためにやったことは以下のことです。
①JIRAからスプレッドシートにチケットデータ送る(JIRAのwebhookを利用)
②チケットデータをスプレッドシートに転記(gasを利用)
③slackにスプレッドに転記したデータを投稿(incoming webhookを利用)
になります。順に説明していきます。

JIRA→spreadSheet→slack
FireShot Capture 3 - アイコン - draw.io_ - https___www.draw.io_.png (244.7 kB)

①JIRAのwebhookの作成する

webhook作成は超簡単です。
システム>詳細_Webフック>Webフックの作成をクリックします。
今回はJIRAチケット作成時、ステータス完了の更新時にスプレッドシートにデータを送りたいので、課題の作成済、更新済にチェックマークをつけて、保存します。
※課題関連イベントにproject = hogehoge と記載すると特定のプロジェクトのデータのみを送ることができます。
スクリーンショット 2017-12-11 16.32.01.png (121.7 kB)

ああああ.png (298.0 kB)

webhookから送られるデータは以下のようなjson形式になっています。

jirawebhook.json
{
    "changelog": {
        "id": "id", 
        "items": [
            {
                "field": "assignee", 
                "fieldId": "assignee", 
                "fieldtype": "jira", 
                "from": null, 
                "fromString": null, 
                "to": "name", 
                "toString": "name"
            }, 
            {
                "field": "priority", 
                "fieldId": "priority", 
                "fieldtype": "jira", 
                "from": null, 
                "fromString": null, 
                "to": "3", 
                "toString": "Medium"
            }, 
            {
                "field": "reporter", 
                "fieldId": "reporter", 
                "fieldtype": "jira", 
                "from": null, 
                "fromString": null, 
                "to": "name", 
                "toString": "name"
            }, 
            {
                "field": "ステータス", 
                "fieldId": "status", 
                "fieldtype": "jira", 
                "from": null, 
                "fromString": null, 
                "to": "1", 
                "toString": "Open"
            }
        ]
    }, 
    "issue": {
        "fields": {
            "aggregateprogress": {
                "progress": 0, 
                "total": 0
            }, 
            "aggregatetimeestimate": null, 
            "aggregatetimeoriginalestimate": null, 
            "aggregatetimespent": null, 
            "assignee": {
                "accountId": "hoge", 
                "active": true, 
                "avatarUrls": {
                    "16x16": "hoge", 
                    "24x24": "hoge", 
                    "32x32": "hoge", 
                    "48x48": "hoge"
                }, 
                "displayName": "mail", 
                "emailAddress": "mail", 
                "key": "name", 
                "name": "name", 
                "self": "hoge", 
                "timeZone": "Asia/Tokyo"
            }, 
            "attachment": [], 
            "comment": {
                "comments": [], 
                "maxResults": 0, 
                "startAt": 0, 
                "total": 0
            }, 
            "components": [], 
            "created": "datetim", 
            "creator": {
                "accountId": "hoge", 
                "active": true, 
                "avatarUrls": {
                    "16x16": "hoge", 
                    "24x24": "hoge", 
                    "32x32": "hoge", 
                    "48x48": "hoge"
                }, 
                "displayName": "name", 
                "emailAddress": "mail", 
                "key": "name", 
                "name": "name", 
                "self": "hoge", 
                "timeZone": "Asia/Tokyo"
            }, 
            "customfield_10900": null, 
            "description": null, 
            "duedate": null, 
            "environment": null, 
            "issuelinks": [], 
            "issuetype": {
                "avatarId": 10308, 
                "description": "雑作業", 
                "iconUrl": "hoge" 
                "id": "10300", 
                "name": "雑作業", 
                "self": "hoge", 
                "subtask": false
            }, 
            "labels": [], 
            "lastViewed": "2017-12-11T16:26:07.383+0900", 
            "priority": {
                "iconUrl": "hoge", 
                "id": "3", 
                "name": "普通", 
                "self": "hoge"
            }, 
            "progress": {
                "progress": 0, 
                "total": 0
            }, 
            "project": {
                "avatarUrls": {
                    "16x16": "hoge", 
                    "24x24": "hoge", 
                    "32x32": "hoge", 
                    "48x48": "hoge"
                }, 
                "id": "id", 
                "key": "hoge", 
                "name": "hoge", 
                "self": "hoge"
            }, 
            "reporter": {
                "accountId": "hoge", 
                "active": true, 
                "avatarUrls": {
                    "16x16": "hoge", 
                    "24x24": "hoge", 
                    "32x32": "hoge", 
                    "48x48": "hoge"
                }, 
                "displayName": "name", 
                "emailAddress": "mail", 
                "key": "name", 
                "name": "name", 
                "self": "hoge", 
                "timeZone": "Asia/Tokyo"
            }, 
            "resolution": null, 
            "resolutiondate": null, 
            "security": null, 
            "status": {
                "description": "担当者が作業を開始できる状態を表します。", 
                "iconUrl": "hoge", 
                "id": "1", 
                "name": "オープン", 
                "self": "hoge", 
                "statusCategory": {
                    "colorName": "blue-gray", 
                    "id": 2, 
                    "key": "new", 
                    "name": "To Do", 
                    "self": "hoge"
                }
            }, 
            "subtasks": [], 
            "summary": "てすと", 
            "timeestimate": null, 
            "timeoriginalestimate": null, 
            "timespent": null, 
            "timetracking": {}, 
            "updated": "2017-12-11T16:26:07.184+0900", 
            "versions": [], 
            "votes": {
                "hasVoted": false, 
                "self": "hoge", 
                "votes": 0
            }, 
            "watches": {
                "isWatching": true, 
                "self": "hoge", 
                "watchCount": 0
            }, 
            "workratio": -1
        }, 
        "id": "id", 
        "key": "key", 
        "self": "hoge"
    }, 
    "issue_event_type_name": "issue_created", 
    "timestamp": 1512977167230, 
    "user": {
        "accountId": "id", 
        "active": true, 
        "avatarUrls": {
            "16x16": "hoge", 
            "24x24": "hoge", 
            "32x32": "hoge", 
            "48x48": "hoge"
        }, 
        "displayName": "name", 
        "emailAddress": "mail", 
        "key": "name", 
        "name": "name", 
        "self": "hoge", 
        "timeZone": "Asia/Tokyo"
    }, 
    "webhookEvent": "jira:issue_created"
}

正直、長っ!!って感じです。さて、次はこのデータをスプレッドシートにカキカキしていきましょう。

②Google App Scriptの作成する

まずはコードを書くためのgasのファイルを作成します。
Google driveにアクセスして新規→その他→Google Apps Scriptをクリックします。(Google Apps Scriptがない方はアプリを追加からインストールしましょう。

これから作成したファイルにgasをカキカキしていきます。
っっっっっt.png (92.6 kB)

gas.js
function doPost(response) {
  //jira webfookのdataを取得
  var json = response.postData.contents;
  var data = JSON.parse(json);

  var jiraNumber = data.issue.key; // チケットIDを取得
  var title = data.issue.fields.summary; // summaryを取得
  var status = data.issue.fields.status.statusCategory.name;  // 解決状況を取得
  var developlistId = data.issue.fields.customfield_[hoge];  // 開発依頼番号取得
  var issueCreated = data.webhookEvent; // チケット作成フラグ

  //スプレッドシートを取得
  var sheets = SpreadsheetApp.openById('spreadsheetId');
  var sheet = sheets.getSheetByName("sheetName");
  var sheetData = sheet.getDataRange().getValues();
  var sheetLastRow = sheet.getLastRow();

  //スプレッドシートで取得する列番号(配列なので実列番号-1)今回は開発IDとタイトルを取得しています
  var developIdColumn = 0;
  var developTitleColumn = 0;

  //スプレッドシートで書き込む列番号。今回はチケットID, 検証環境のリリース日、リリースフラグに書き込みます
  var jiraNumberColumn = 0;
  var stagingReleaseDayColumn = 0;
  var stagingReleaseFlagColumn = 0;

  //本日の曜日を取得。弊社では火、金に検証環境にリリースするので曜日を比較するために本日の曜日を取得しています
  var today = new Date();
  var dayOfTheWeek = today.getDay();

  //slack投稿用変数
  var message = "";

  //jira作った場合、jira完了した場合
  if(status == "完了"){
      for(var j=0;j<sheetLastRow;j++){ //スプレッド最終行までループまわす
        if(developlistId == sheetData[j][developIdColumn]){  //jiraの開発依頼番号とスプレッドの開発依頼番号が等しい場合
          var stagingReleaseDay = stagingReleaseDayfnc(dayOfTheWeek); //検証環境のリリース日を取得
          var writeRowNumber = j + 1; //スプレッドシート入力用の行番号を格納
          sheet.getRange(writeRowNumber,stagingReleaseDayColumn).setValue(stagingReleaseDay); //スプレッドシートにjiraで取得した内容を書き込む
          sheet.getRange(writeRowNumber,stagingReleaseFlagColumn).setValue(1);
          message = "開発依頼ID: " + String(sheetData[j][developIdColumn]) + "\n" + sheetData[j][developTitleColumn] + "の開発が完了しました。" //slackに送るメッセージ作成
          slackpost(message); //slack送信
        }
      }
   } else if(issueCreated == 'jira:issue_created' ){
      for(var i=0;i<sheetLastRow;i++){ //スプレッド最終行までループまわす
        if(developlistId == sheetData[i][developIdColumn]){ //開発依頼番号があった場合
                 var writeRowNumber = i + 1; //スプレッドシート入力用の行番号を格納
          sheet.getRange(writeRowNumber, jiraNumberColumn).setValue(jiraNumber); //スプレッドシートにjiraで取得した内容を書き込む
          message = "開発依頼ID: " + String(sheetData[j][developIdColumn]) + "\n" + sheetData[j][developTitleColumn] + "の開発が開始しました。"; //slackに送るメッセージ作成
          slackpost(message); //slack送信
        }
      }
   }

}

//検証環境のリリース日を出力する関数
function stagingReleaseDayfnc(dayOfTheWeek) {
  //当日の日付を格納
  var today = new Date();

  //翌日の日付を格納
  var tomorrow = new Date();
  tomorrow.setDate(tomorrow.getDate()+1);
  var formatTomorrow = Utilities.formatDate(tomorrow,"JST","yyyy/MM/dd");

  //2日後の日付を格納
  var twoDaysLater = new Date();
  twoDaysLater.setDate(twoDaysLater.getDate()+2);
  var formattwoDaysLater = Utilities.formatDate(twoDaysLater,"JST","yyyy/MM/dd");

  //3日後の日付を格納
  var threeDaysLater = new Date();
  threeDaysLater.setDate(threeDaysLater.getDate()+3);
  var formatthreeDaysLater = Utilities.formatDate(threeDaysLater,"JST","yyyy/MM/dd");

  //検証環境のリリース日を出力
  if(dayOfTheWeek == 0){
    return twoDaysLater
  }else if(dayOfTheWeek == 1){
    return tomorrow
  } else if(dayOfTheWeek == 2){
    return threeDaysLater
  } else if(dayOfTheWeek == 3){
    return twoDaysLater
  } else if(dayOfTheWeek == 4){
    return tomorrow
  } else if(dayOfTheWeek == 5){
    return threeDaysLater
  } else if(dayOfTheWeek == 6){
    return twoDaysLater
  }
}


 //slackへの投稿する関数
function slackpost(message) {

  var postUrl = 'hoge'; //Incoming WebHooksのWebhook URLをコピペする
  var username = 'jirabot';  // 通知時に表示されるユーザー名

  //slackで送るのもを格納
  var jsonData =
      {
        "username": username,
        "text":message
      };

  var payload = JSON.stringify(jsonData); //jsonデータに変換

  //送り方とかもろもろの設定を記入
  var options =
      {
        "method" : "post",
        "contentType" : "application/json",
        "payload" : payload
      };

  UrlFetchApp.fetch(postUrl, options);  //postUrlにデータを送る
}

③slackにスプレッドに転記したデータを投稿する

webhookの設定
slackでデータを送信したいチャネルに移動し、Add an appをクリック
スクリーンショット 2017-12-14 15.04.16.png (61.4 kB)

Get Essential Appsをクリック
スクリーンショット 2017-12-14 15.04.49.png (260.8 kB)

検索でincoming WebHooksを検索してクリック
add configuration

Add Configurationをクリック
add configuration

投稿したいチャネルを選択してAdd Incoming WebHooks integrationをクリック
スクリーンショット 2017-12-14 15.05.29.png (238.5 kB)

iconなどを適当に変更してSave Settingsをクリック
名称未設定 2.png (204.4 kB)

動かしてみた

JIRAでステータスを完了にしてみる
名称未設定 2.png (202.9 kB)

無事slackに投稿されました
スクリーンショット 2017-12-14 15.15.54.png (43.7 kB)

13
12
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
13
12