GitHub Actions: PRのdiff行数に応じてラベルを貼る簡単なスニペット
ちょっとしたスニペットの紹介です。結構雑なやつです
PRのdiff行数に応じてラベルを貼るワークフローの書き方の紹介です。完成図は以下のようになります
このようなラベルが、PRを作成した際に自動的に作られるようになります。PRが更新されればラベルも貼り直しされます。
スニペット
.github/workflows/label-pr-diff-line-count.yml
などのような名前で(ファイル名は何でもいいです)以下のような内容のワークフローを作成します。
name: PRの差分行数カウント
on:
pull_request:
types: [opened, synchronize]
env:
# 差分行数を表すラベルのprefix
DIFF_LABEL_PREFIX: diff/
# 差分行数を表すラベルの色
DIFF_LABEL_COLOR: '63D2D0'
jobs:
label:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: チェックアウト
uses: actions/checkout@v4
- name: 差分の情報を取得
id: diff
uses: technote-space/get-diff-action@v6
with:
GET_FILE_DIFF: true
- name: 差分行数をもとにラベルを設定
run: |
node -e "$(cat <<'EOF'
const { execSync } = require('node:child_process');
const repo = process.env.REPO;
const prefix = process.env.DIFF_LABEL_PREFIX ?? '';
const labelColor = process.env.DIFF_LABEL_COLOR;
const prNumber = ${{ github.event.pull_request.number }};
const diffLines = ${{ steps.diff.outputs.lines }};
// ラベル区分を追加する場合は以下を編集する
const labels = [
{ name: 'tiny', max: 10 },
{ name: 'small', max: 30 },
{ name: 'middle', max: 100 },
{ name: 'large', max: 200 },
{ name: 'xl', max: 400 },
{ name: 'xxl', max: 800 },
{ name: '3xl', max: 1500 },
{ name: '4xl', max: 5000 },
{ name: '5xl', max: 10_000 },
{ name: '6xl', max: 20_000 },
{ name: 'too-large', max: Number.POSITIVE_INFINITY },
];
const createdLabels = new Set(JSON.parse(execSync(`gh label list --repo ${repo} --json name`).toString()).map(({ name }) => name));
const addIndex = labels.findIndex(({ max }) => diffLines <= max);
const add = labels[addIndex];
const remove = labels.filter(e => e !== add && createdLabels.has(`${prefix}${e.name}`));
const addArgs = `--add-label '${prefix}${add.name}'`
const removeArgs = remove.length === 0 ? '' : `--remove-label '${remove.map(({ name }) => `${prefix}${name}`).join(',')}'`;
const addedLabelDescription = `PRの差分が${(labels[addIndex - 1]?.max ?? -1) + 1}行以上${Number.isFinite(add.max) ? `${add.max}行以下` : ""}`;
execSync(`gh label create '${prefix}${add.name}' --description '${addedLabelDescription}' --color ${labelColor} --repo ${repo} --force`);
execSync(`gh issue edit ${prNumber} ${addArgs} ${removeArgs}`);
EOF
)"
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
これさえ配置すれば完了です。
このまま使うと、
- 0行〜10行の変化で
diff/tiny
ラベル - 11行〜30行の変化で
diff/small
ラベル - ...
- 10001行〜20000行の変化で
diff/6xl
ラベル - 20001行〜の変化で
diff/too-large
ラベル
がつくというような設定になっています。
const labels = [ ... ]
の中身の部分を書き換えれば独自の設定にできますので、ぜひ書き換えて利用してみてください。
使用したアクションの紹介
-
actions/checkout@v4
-
technote-space/get-diff-action@v6
- https://github.com/technote-space/get-diff-action
- アーカイブされていますが使えます
- git diffに関する情報を取れます
最新版に関する情報は各自でご確認ください
専用のパブリックな共有アクションがほしい
私もそう思います。気が向いたら...
でも部品となるアクションが充実していると、nodeを使ってサクッとこういうのがかけるのはいいですよね。
シンプルなバージョン
ラベルの用意は事前にやっておくのでよい、という場合は以下のシンプルなバージョンで良いかもしれません。
シンプルなバージョン
name: PRの差分行数カウント
on:
pull_request:
types: [opened, synchronize]
env:
# 差分行数を表すラベルのprefix
DIFF_LABEL_PREFIX: diff/
jobs:
label:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: チェックアウト
uses: actions/checkout@v4
- name: 差分の情報を取得
id: diff
uses: technote-space/get-diff-action@v6
with:
GET_FILE_DIFF: true
- name: 差分行数をもとにラベルを設定
run: |
node -e "$(cat <<'EOF'
const { execSync } = require('node:child_process');
const prefix = process.env.DIFF_LABEL_PREFIX ?? '';
const prNumber = ${{ github.event.pull_request.number }};
const diffLines = ${{ steps.diff.outputs.lines }};
// ラベル区分を追加する場合は以下を編集する
const labels = [
{ name: 'tiny', max: 10 },
{ name: 'small', max: 30 },
{ name: 'middle', max: 100 },
{ name: 'large', max: 200 },
{ name: 'xl', max: 400 },
{ name: 'xxl', max: 800 },
{ name: '3xl', max: 1500 },
{ name: '4xl', max: 5000 },
{ name: '5xl', max: 10_000 },
{ name: '6xl', max: 20_000 },
{ name: 'too-large', max: Number.POSITIVE_INFINITY },
];
const add = labels.find(({ max }) => diffLines <= max);
const remove = labels.filter(e => e !== add);
const addArgs = `--add-label '${prefix}${add.name}'`
const removeArgs = `--remove-label '${remove.map(({ name }) => `${prefix}${name}`).join(',')}'`;
execSync(`gh issue edit ${prNumber} ${addArgs} ${removeArgs}`);
EOF
)"
env:
GH_TOKEN: ${{ github.token }}
で、ラベルはスクリプトなどで事前に用意しておく。
ラベル用意のスクリプトの例(雑なやつ)
import { execFileSync } from "node:child_process";
export const deleteLabel = (name: string, repo: string) => {
execFileSync("gh", ["label", "delete", name, "--repo", repo, "--yes"], {
stdio: ["ignore", "inherit", "inherit"],
});
};
export const createLabel = (
name: string,
description: string,
color: string,
repo: string,
) => {
execFileSync(
"gh",
[
"label",
"create",
name,
"--description",
description,
"--color",
color,
"--repo",
repo,
],
{
stdio: ["ignore", "inherit", "inherit"],
},
);
};
// ワークフローyamlと同じもの
const labels = [
// ...
];
const repo = "<your org>/<your repo>";
const prefix = "diff/";
const color = "63D2D0";
let last = 0;
for (const { name, max } of labels) {
const label = `${prefix}${name}`;
const description = `PRの差分が${last}行以上${
Number.isFinite(max) ? `${max}行以下` : ""
}`;
createLabel(label, description, color, repo);
// deleteLabel(label, repo);
last = max + 1;
}
bunで実行する前提でTypeScriptです。
diffのラベルの何が嬉しいか
私のチームではPRサイズを(小さくするように)意識しようという話があって、その際に一覧ページを見た際にラベルがあったほうが振り返りがしやすかったり、PR担当者も意識しやすくなったりするだろうと思い、ラベルをつけることにしました。
世界のラストワンマイルを最適化する、OPTIMINDのテックブログです。「どの車両が、どの訪問先を、どの順に、どういうルートで回ると最適か」というラストワンマイルの配車最適化サービス、Loogiaを展開しています。recruit.optimind.tech/
Discussion