Madogiwa Blog

主に技術系の学習メモに使っていきます。

GitHub上のPRがクローズされるまでの時間を計測するMEMO

開発チームの健全性とかを定量的に計測する指標としてFour Keysがあると思うのですが、この辺の変更のリードタイムを表す数値として厳密では無いですが、PRが作成されてからクローズされるまでの時間を簡単に計測できないかなと思い調べてみたら結構すぐ取れそうだったのでメモ🗒

cloud.google.com

GitHub上のPRがクローズされるまでの時間を計測する

やり方は簡単でGitHubAPIでPRのリストを取得できるので、それを使って取得してPRの作成日時とクローズされた日時の差分を計算します。 ※ページングとかを考慮すると実装が複雑になるのと全部のPRを計算しなくても良いのでparametersで指定可能な直近100件で計測します。

その後は好きに計算すればいいのですが、今回はパーセンタイルを指定して出力するようにしてみました。

以下はOktokitを使って実装してみたサンプルです。

const { Octokit } = require("@octokit/core");

const repoOwner = "owner name";
const repoName = "repo name";
const repoType = "private or public";
const percentile = 50;
const baseBranch = "master";
const token = process.env.GITHUB_TOKEN;

// NOTE: initialize OktoKit
const octokit = new Octokit({ auth: token });

// main
const dateHourDiff = (before, after) => (after - before) / 1000 / 60 / 60;
const getPulls = async () => {
  return await octokit.request(
    "GET /repos/{owner}/{repo}/pulls?state=closed&per_page=100&sort=created&direction=desc&base={base}",
    {
      owner: repoOwner,
      repo: repoName,
      type: repoType,
      base: baseBranch,
    }
  );
};

try {
  (async () => {
    const res = await getPulls();
    const result = res.data.map((pr) => {
      return {
        number: pr.number,
        title: pr.title,
        created_at: pr.created_at,
        merged_at: pr.merged_at,
        closed_at: pr.closed_at,
        duration_hour: dateHourDiff(
          new Date(pr.created_at),
          new Date(pr.closed_at)
        ),
      };
    });
    durations = result.map((r) => r.duration_hour).sort();
    percentile_index = Math.floor(durations.length * (percentile / 100.0));
    console.log("durations", durations[percentile_index]);
  })();
} catch (error) {
  console.error(error);
}

実行すると以下のような感じです。

$ node index.js
durations 32.27638888888889

APIの詳細な使用は以下に記載されています。

docs.github.com

Github Actionにしてみる

任意のタイミングでリポジトリ上から計測できると便利そうなので以下を参考にGitHub Actionにしてみます。

docs.github.com

const core = require("@actions/core");
const github = require("@actions/github");

// NOTE: configure parameters
const repoOwner = github.context.repo.owner;
const repoName = github.context.repo.repo;
const repoType = core.getInput("repo-type");
const percentile = core.getInput("percentile");
const baseBranch = core.getInput("base-branch");
const token = core.getInput("repo-token");

// NOTE: initialize OktoKit
const octokit = github.getOctokit(token);

// main
const dateHourDiff = (before, after) => (after - before) / 1000 / 60 / 60;
const getPulls = async () => {
  return await octokit.request(
    "GET /repos/{owner}/{repo}/pulls?state=closed&per_page=100&sort=created&direction=desc&base={base}",
    {
      owner: repoOwner,
      repo: repoName,
      type: repoType,
      base: baseBranch,
    }
  );
};

try {
  (async () => {
    const res = await getPulls();
    const result = res.data.map((pr) => {
      return {
        number: pr.number,
        title: pr.title,
        created_at: pr.created_at,
        merged_at: pr.merged_at,
        closed_at: pr.closed_at,
        duration_hour: dateHourDiff(
          new Date(pr.created_at),
          new Date(pr.closed_at)
        ),
      };
    });
    const durations = result.map((r) => r.duration_hour).sort((a, b) => a - b);
    const percentile_index = Math.floor(durations.length * (percentile / 100.0));
    core.setOutput("duration", durations[percentile_index]);
  })();
} catch (error) {
  core.setFailed(error.message);
}

使うには以下のような感じです。

on: workflow_dispatch

jobs:
  pr_close_duration:
    runs-on: ubuntu-latest
    name: A job calc close PR duration
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: run pr-close-duration
        uses: ./pr-close-duration
        id: pr-close-duration
        with:
          base-branch: main
          repo-type: private
          percentile: 50
          repo-token: ${{ secrets.MY_GITHUB_TOKEN }}
      - name: Get the output time
        run: echo "The duration was ${{ steps.pr-close-duration.outputs.duration }} hour."

以下のような感じで見れます。

f:id:madogiwa0124:20220327131219p:plain

GitHub Actionは以下で公開してみました。

github.com

おしまい。