この記事はモチベーションクラウドシリーズ Advent Calendar 2022 6日目の投稿です。
はじめに
私のチームではプロダクト開発の生産性指標としてFour keys metricsを利用しています。
Four Keys Metricsは DevOps Research and Assesment (DORA) チームが確立した開発組織のパフォーマンスに関する指標です。1
その中の一つである変更のリードタイムは下記のように定義されています。
変更のリードタイム - commit から本番環境稼働までの所要時間2
このリードタイムを削減するために作成した、3つのgithub actionsを実際のワークフローの中身とともに紹介できればと思います。
リードタイム削減に向けた方針
私のチームではgithubのPull Requestを使っているので、リードタイムを分解すると
初回コミットからブランチがマージされPull Requestがクローズされるまでの時間
+
開発のメインブランチが本番環境に反映されるまでの時間
になることから、
前者の初回コミットからブランチがマージされるまでの時間というのをとにかく状況を見えるようにすることにこだわりました。
私のチームではまずこの「初回コミットからブランチがマージされPull Requestがクローズされるまでの時間」を変更のリードタイム
としてみていくことにしました。
自分自身がコードを書く際も、ついつい自分がOpenしたPull Requestを放置していたことも多かったです。
また、マージした際も「なんとなく長かった気がするなー」といったような感覚でしか長さを感じることができていませんでした。
そこで、開発ブランチへのマージの促進、結果のフィードバック、チーム全体での状況の可視化を行うためのワークフローの追加を行いました。
あくまでも実装例ですが、よろしければご参考になさってください
見づらく拙い記法もあるかと思いますので、コメントいただけると幸いです
1. Pull Requestが作成されてから時間がたつとお知らせ
放置されているPull Requestを作成者に伝えるようにしました。
ルールとしては、平日9時にOpenになってから2日以上経過しているPull Request上で作成者に対して2日経過している旨をコメントするようにしました。
またPull Request一覧で見たさいにわかりやすく絞り込めるように2d-passed
というラベルも付与しています。
name: 作成されてからマージされていないPRをチェック
on:
schedule:
# AM 09:00, 月-金
- cron: '0 0 * * 1-5'
jobs:
set-label-and-comment:
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: 作成して2日以上経っているのに2d-passed labelのついてないPRにコメントしてラベルをつける
run: |
# 適宜変更してください
LABEL="2d-passed"
# 適宜変更してください
DATE=$(date +'%Y-%m-%d' --date '2 day ago')
gh pr list -L 100 \
--search "is:pr is:open created:<${DATE} -label:${LABEL}" \
--json url,author \
--jq '.[] | [.url, .author.login] | join(" ")' |
xargs -r -n2 bash -c 'gh pr comment $0 -b "@$1 マージを促進するコメント" && gh pr edit --add-label "2d-passed" $0'
こんな形でコメントがされます
私のチームではマージを促進するコメントを投票で決めたところ「良い時計してますなあ」になりましたw
2. マージされたあとに初回のコミットから何時間でされたかお知らせ
Pull Request作成者が最初のコミットをしてから何時間でbaseブランチにマージされたかを計算し、コメントするようにしました。
具体的な数字をコメントすることで、作成者だけではなくレビュワーにもフィードバックされ、レビューまでの時間などを意識させることができたかなと思います。
name: 初回コミットからマージまでの時間を計測してコメント
on:
pull_request:
branches:
- develop # baseブランチの指定
types: [closed]
jobs:
lead-time:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: calc time
run: |
gh api graphql -f id="${{ github.event.pull_request.node_id }}" -f query='
query ($id: ID!) {
node(id: $id) {
... on PullRequest {
mergedAt
commits(first: 1) {
nodes {
commit {
authoredDate
}
}
}
}
}
}
' |
jq -r '.data.node | .mergedAt, .commits.nodes[0].commit.authoredDate | fromdate | strftime("%s")' |
xargs -n 2 |
awk '{printf "%d", ($1 - $2) / 60 / 60 }'|
xargs -I{} echo LEAD_TIME_HOUR={} >> $GITHUB_ENV
- name: comment
run: |
gh pr comment ${{ github.event.pull_request.html_url }} \
-b "@${{ github.event.pull_request.user.login }} お疲れ様でした!!🍺 リードタイムは$(expr ${LEAD_TIME_HOUR} / 24)日 (${LEAD_TIME_HOUR}時間)でした!!🎉"
こんな形でコメントされます。
ちなみにgh cliはめちゃくちゃ便利でなんでもできます。
この初回コミットからマージまでの時間計測には gh api graphql
を使用しており、githubの公開されているGraphQLをcliから簡単に実行することができます。
Github GraphQL APIでどんな情報が取得できるか試すにはこちらのエクスプローラを試すのがおすすめです。
3. 結果を分析するために情報を取得して蓄積
各Pull Requestがマージされてクローズされる時、時間のコメントだけではなく、時間、変更数などを合わせてspreadsheetに書き込むようにしています。
具体的には
- Pull Resquestがクローズされたら関連する情報を整形して送信
- 送信先はspreadsheet上のGoogle Apps Scriptでシートに追加
のステップで情報の蓄積を行なっています。
以下の情報を取得しています。
- リポジトリ名
- Pull Requestのタイトル
- 作成者
- url
- baseブランチ
- headブランチ
- 初回コミット日時
- 作成日時
- 初回レビュー日時
- マージ日時
- 追加行数
- 削除行数
name: SpreadsheetにPull Requestの情報を送信
on:
pull_request:
types:
- closed
jobs:
metrix-info:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: post github info
run: |
gh api graphql -f id="${{ github.event.pull_request.node_id }}" -f query='
query ($id: ID!) {
node(id: $id) {
... on PullRequest {
repository {
name
}
title
author {
login
}
url
baseRefName
headRefName
commits(first: 1) {
nodes {
commit {
authoredDate
}
}
}
createdAt
mergedAt
additions
deletions
reviews(first: 1) {
nodes {
... on PullRequestReview {
createdAt
}
}
}
}
}
}
' |
# tokenで認証を行なっている
jq -r --arg token ${{ secrets.GAS_POST_API_METRIX_TOKEN }} '.data.node |
{
data: {
repository: .repository.name,
title: .title,
user: .author.login,
url: .url,
base: .baseRefName,
head: .headRefName,
firstCommittedAt: .commits.nodes[0].commit.authoredDate,
createdAt: .createdAt,
firstReviewedAt: .reviews.nodes[0].createdAt,
mergedAt: .mergedAt,
additions: .additions,
deletions: .deletions
},
type: "lead_time",
token: $token
} |
@json' |
curl -L -H "Content-Type: application/json" -d @- "https://script.google.com/macros/s/*******/exec"
受信先のGoogle Apps Script例 (少し長いのでこちらをクリック)
function doPost(e) {
const postData = JSON.parse(e.postData.contents);
// ここでtoken認証
if (postData.token !== PropertiesService.getScriptProperties().getProperty('GAS_POST_API_METRIX_TOKEN')) {
return (createResponse('invalid token error'))
}
switch (postData.type) {
case 'lead_time':
insertLeadTimeData(postData);
return (createResponse('ok'))
break;
default:
return (createResponse('invalid type error'))
}
}
function insertLeadTimeData(postData) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('シート名');
const data = postData.data
sheet.appendRow([
data.repository,
data.title,
data.user,
data.url,
data.base,
data.head,
data.firstCommittedAt,
data.createdAt,
data.firstReviewedAt,
data.mergedAt,
data.additions,
data.deletions
]);
}
function createResponse(message) {
const output = ContentService.createTextOutput();
output.setMimeType(ContentService.MimeType.JSON);
output.setContent(JSON.stringify({ message: message }));
return output;
}
こんな形で蓄積されていっています。
spreadsheetに蓄積することでメンバー間の比較やチーム全体の変化を可視化できるようになりました。
結果の可視化はGoogleのLooker Studioで行い、他のFour keys metricsと一緒にダッシュボードにしています。
Four keys metricsのダッシュボードについては弊社テックブログに詳細を投稿予定ですので、読者登録いただけると幸いです。
導入してみてどうだったか
元々5~10日ほどだったマージまでの時間が平均値、中央値ともに1日前後になりました。
もちろんこのワークフローたちのみの結果ではなく、ペアプロによるレビューコスト削減、Pull Requestのサイズ軽減、レビューファーストなチームの文化づくりの結果だとは思います。
マージまでの時間が減ることで、コンフリクトも起きづらくなり、開発者体験も向上したのではないかなと思います。