
GAS(Google Apps Script)でJira課題を一括作成してみた
こんにちは、ふくろうです。
ナビタイムジャパンで、シンプルで使いやすい地図アプリ『ここ地図』のAndroid開発・運用を担当しています。
本記事では、Jira課題(issue)を一括作成できるツールをつくった話をしたいと思います。Googleスプレッドシート + GAS + JiraAPIで実現しています。
・Jira課題を複数作成するのに時間がかかっている
・JiraのGUIでの操作を自動化したい
・GAS(Google Apps Script)でAPIを叩く方法を知りたい
以上のような方の参考となれば幸いです!
Jiraとは
Jiraとは、Atlassian社が開発しているプロジェクト管理ツールです。
Jiraではタスクを課題として作成、以下のように進行状況等管理できます。
※ 私のチームではJira課題 = Jiraチケットと呼んでいます
どうしてJira課題一括作成ツールをつくったのか
きっかけは私が新卒1年目、配属された去年7月頃にさかのぼります。
当時私が配属されたチームでは、施策検討のため社内・社外問わず情報収集しており、特に社内の情報を集めるために以下の "キャッチアップ" と呼ばれる作業を行っていました。
・他プロジェクトのJira課題をエクポートし、スプレッドシートで表示
(※ 検討最中と作業後の閲覧性/検索性を鑑み、スプレッドシートを利用)
・自プロジェクトでも参考にしたいJira課題を、メンバーで検討し選定
・選定したJira課題を自プロジェクトに別途作成 ...[1]
この "キャッチアップ" 作業は、月2回・手動で1時間以上かけて実施していました。
中でも大変なのは[1]Jira課題の作成作業です。以下、Jira課題の作成画面をご覧ください。
一つ作成するだけならさほど問題ではないのですが、 "キャッチアップ" 作業では10枚など一気に作成することが間々ありました。
そう、内容が似通ったJira課題を作成する場合でも
入力 -> 作成、入力 -> 作成... を繰り返す必要があるのです...。
※ JiraではcsvインポートによるJira課題作成機能もサポートされているようですが、今回は採用しませんでした。csvインポートについて詳しくはJiraサポートページをご確認ください。
なんとか自動化できないか... そこで作成したのがJira課題一括作成ツールです。
Jira課題一括作成ツール(JiraSheet)とは
以下3ステップでJira課題をらくらく作成できるツールです。
① 専用のスプレッドシートを開き、内容を入力
② 作成したい項目にチェックを入れる
③ ◇Jiraメニュー を押下し、 "チケット作成"(=Jira課題作成) を選択
これだけでJira課題が作成されます...!!
そしてシートには作成したJira課題のリンクが貼られ、ブラウザで確認できます
大まかな処理の流れと実装
例を交えながら処理を追ってみます。
【注意】
・JiraにはServer版とCloud版が存在し、仕様が異なります
・本記事ではServer版 version8.5.2を前提とします
※ APIの詳細仕様は以下を始めとする各verのリファレンスをご確認下さい
今回は124行目に書き込んだ内容をJira課題化する流れを見ていきましょう。ちなみに簡単のため処理は簡略化しています。
まずスプレッドシートに記入した内容を取得(Spreadsheetクラスを利用)
const sheet = SpreadsheetApp.getActiveSheet();
const parentValue = sheet.getRange('C124').getValue(); // 親課題ID
const summaryValue = sheet.getRange('D124').getValue(); // 課題名
const descriptionValue = sheet.getRange('E124').getValue(); // 内容
const typeValue = sheet.getRange('F124').getValue(); // 課題タイプ
.
.
.
項目分だけ続く
次に、取得したデータから連想配列fieldDataとupdateDataを作成
(JiraAPIのJson形式に合わせます)
const fieldData = {
project: {key: 'PROJECT_KEY'}, // プロジェクトキー(ここでは固定)
summary: summaryValue, // 課題名
description: descriptionValue, // 内容
issuetype: {name: typeValue}, // 課題タイプ
.
.
.
項目分だけ続く
}
// 親課題など一部の項目はupdateに入れる必要あり。詳しくはリファレンス参考のこと
const updateData = {
issuelinks:[
{
// id:10400が親子リンク(WBSGantt)でしたが、環境により異なる可能性あり
// inwardIssueが親課題に、outwardIssueが小課題に当たるようです
add: {type: {id: "10400"},
inwardIssue: {key: parentValue}}
}
]
}
連想配列からリクエスト用パラメータparamsを作成
const payload = JSON.stringify({fields: fieldData, update:updateData});
// Basic認証用のtoken取得...Tips①参照のこと
var name = PropertiesService.getUserProperties().getProperty('name');
var pass = PropertiesService.getUserProperties().getProperty('pass');
const token = Utilities.base64Encode(name + ':' + pass);
const params = {
method: 'post',
payload: payload,
contentType: 'application/json',
headers: {'Authorization': ' Basic ' + token},
muteHttpExceptions:true // 詳細なエラー内容を見たい&自前で処理したいので例外をスローさせない(UrlFetchAppのリファレンス参照のこと)
};
UrlFetchAppクラスのfetchを用いてJiraAPIの /issue をPOSTで叩く
const requrestUrl = {JiraAPIのベースURL} + /issue; // 課題作成用のAPI
const response = UrlFetchApp.fetch(requestUrl, params);
/** 以下、参考までにレスポンス処理例 */
const responseCode = response.getResponseCode();
const responseBody = response.getContentText();
if(responseCode != 201){
// issueをPOSTで叩いた場合201以外はエラー扱いとする
throw new Error('エラーが発生しました(code:' + responseCode + ')');
}
const result = JSON.parse(responseBody);
Logger.log(result['id']); // '10000'
Logger.log(result['key']); // 'TST-24'
Logger.log(result['self']); // '{JiraAPIのベースURL}/issue/10000'
これでJira課題作成完了です。
Tips
①Basic認証に利用するユーザ情報をPropertiesServiceで保存
JiraAPIにリクエストする際には認証が必要です。今回は社内ツールということもあり、"username:password" 形式のBasic認証を利用しました。
※ 認証方法としてはOAuthも用意されており、こちらが推奨されます
そして、Jira課題を作成するたびにusernameとpasswordを求められると煩わしいため、事前にメニュー押下で保存しておく仕様にしています。
値の保存にはkey-value形式のPropertiesServiceを使用します。
PropertiesServiceには3種ありますが、今回はユーザ情報を保存するためUserPropertiesを採用しました。
# DocumentProperties
ドキュメント、スプレッドシート、またはフォーム内で全ユーザーがアクセスできる
# ScriptProperties
全ユーザーがこのスクリプト内でのみアクセスできる
# UserProperties ★ユーザ情報を保存したいのでこちらを利用
現在のユーザーのみがこのスクリプト内でのみアクセスできる
(※ deprecatedになっているUserPropertiesとは別物です)
実装例としては、UserPropertiesに保存しておき
const name = Browser.inputBox('JIRAのユーザー名を入力してください').trim();
PropertiesService.getUserProperties().setProperty('name', name);
const pass = Browser.inputBox('JIRAのパスワードを入力してください').trim();
PropertiesService.getUserProperties().setProperty('pass',pass);
リクエストの際に取得してパラメータに詰める、という流れです。
var name = PropertiesService.getUserProperties().getProperty('name');
var pass = PropertiesService.getUserProperties().getProperty('pass');
const token = Utilities.base64Encode(name + ':' + pass);
const params = {
method: 'post',
payload: payload,
contentType: 'application/json',
headers: {'Authorization': ' Basic ' + token},
muteHttpExceptions:true
};
UrlFetchApp.fetch(url, params);
【注意】
「username:password」形式のBasic認証を利用例として載せていますが、昨年時点で少なくともCloud版にて非推奨通知がされています。
代わりに「username:apitoken」が利用できるようです。
・「username:password」による基本認証のドキュメント - Server版
・非推奨の通知-パスワードによる基本認証とCookieベースの認証 - Cloud版
・APIトークンを利用した認証 - Cloud版
②必要な項目を柔軟に追加できるよう、Json形式で項目を設定
タイトルからだと何のことかわからないと思うのですが、
プロジェクトによってJira課題作成に必要な項目が異なるため
このオレンジ色で囲んだ箇所を固定にせず、列移動や項目追加を自由にできる仕様としました。
実現方法は
あらかじめ "課題タイプ" などの項目に対応したJsonを定義して
入力された行とヘッダの値を列を揃えて取得
display_nameとヘッダが一致した際に "?" を入力値に書き換えて
課題データ(dataMap)に追加
※ display_name分だけforを回し、display_nameがチケット名だった場合
if(display_nameとヘッダが一致したら){
// 最初だけsplitし、それより後の文字列はそのままの配列を返す
const splited = splitFirst('summary:?', ':');
const key = splited[0];
const value = splited[1].replace('?', '"'+ {GASから自動作成した課題} +'"');
dataMap.set(key, JSON.parse(value));
}
その後パラメータに追加して、JiraAPIを叩くという流れです。
泥臭いですが、その甲斐あってパラメータの追加も、他プロジェクトへの展開もスムーズに行うことができました。
ツールを導入してみて
まず発端である "キャッチアップ作業" のJira課題発行を1分でできるようになりました。スプレッドシートから項目をコピーしてのJira課題作成に、ひどいときは30分以上かかっていたことを考えると本当に感動ものでした。
また複数プロダクトで導入いただき、「10分で40課題作成できた」「Jira課題作成工数が半分近くになった」などの声を頂きました。お役に立てて嬉しいのはもちろん、気になったらとりあえずやってみることも大事だなと改めて実感しました。
まとめ
以上、Googleスプレッドシート + GAS + JiraAPI でJira課題を一括作成する方法をご紹介しました。
JiraAPIは今回 /issue しか使っていないですが、jql文を用いて柔軟な検索をしたり、既存のJira課題情報を書き換えたり、ほとんどのGUI操作をそのままAPIに置き換えることができます。
Jiraのあの操作がめんどくさいんだよな〜 とお思いの方はGASで手軽に自動化してみてはいかがでしょうか。