こんにちは。インフラグループの夏目です。
エニグモではメインのGitサービスとしてGitLabを使ってソースコードを管理しています。 GitLabはGitHubと同様に、IssueやMR(PR)にラベルを付与して作業の優先度やステータスを表すことができるのですが、このラベルの運用でちょっと困ったことが発生して泥臭く解消するはめになったので、経緯と顛末含めてご紹介します。
プロジェクトラベルとグループラベル
GitLabのラベルは、プロジェクト(リポジトリ)とプロジェクトを束ねたグループとでそれぞれ個別に定義できます。 グループラベルとして定義したものは配下のプロジェクトでも使用できるため、基本的にはグループ側で汎用的なラベルを設定して、プロジェクト固有のメタ情報としてプロジェクト名の省略形(例:Enigmo Greatest Project
→EGP
)や、リリースバージョン(例:v1.4.3
)などをプロジェクトラベルで定義するというのがベターな運用方法だと思われます。
グループラベルを設定しよう
ところがこのグループラベル、エニグモではあまり認識されていなかったため
- 各プロジェクトで汎用的な名前のプロジェクトラベルを個別に設定
- プロジェクトラベルを使ってIssueやMRを管理
- プロジェクトごとに同じラベルを定義するのが手間だったので、プロジェクトラベルと同じ名前のグループラベルを設定
という流れで最近になってようやくグループラベルが設定されました。各プロジェクトは個別にラベルを定義する必要がなくなってめでたしめでたしかと思いきや、そうではありません。さきほどのフローに妙なところがありませんでしたか?
プロジェクトラベルと同じ名前のグループラベルを設定
ここです。同じ名前のラベルを設定しましたね。グループラベルとプロジェクトラベルは別のオブジェクトとして扱われるため、同じ名前のラベルが設定できます。設定できるのは問題ないのですが、いざ使ってみようとすると
こんな感じでまったく区別のつかないラベルがラベル付与の候補として表示されるようになってしまいました。どうしてくれるんだ。しかも厄介親切なことにこのラベル候補、グループラベルかプロジェクトラベルかという情報は表示されません。
重複したらどうする?
区別がつかなくても同じ意味合いだったら別にどっちでもいいじゃん、と思わないでもありませんが、ラベルを付与するたびに「なぜ2つあるのか?どちらを付与したらいいのか?表示のバグ?」とチラッと考える時間が無駄ですね。じゃあこの重複を解消しましょう、と迂闊に既存のプロジェクトラベルを削除してしまうと、これまで該当のラベルを付与していたIssueやMRからラベルが削除されてしまいます。
仮にreview requested
などのアクションが必要なラベルの場合、削除によって対応が漏れてしまうおそれがあります。この事象に対して、GitLab.orgのIssueで対応方法が提案されていました。
- 重複しているプロジェクトラベルかグループラベルをリネームする
- プロジェクトラベルが付与されたIssueやMRに、グループラベルを追加する
- プロジェクトラベルを削除する
たとえばin review
という名前のプロジェクトラベルとグループラベルが設定されている状態では、以下のような対応になります。
- プロジェクトラベル:
in review
をin review(project)
へリネーム in review(project)
が付与されたIssueやMRに、グループラベル:in review
を付与- プロジェクトラベル:
in review(project)
を削除
リネームと削除はグループ設定画面やプロジェクト設定画面から容易に対応できますが、活発に開発が行われているプロジェクトにおいて、Issue一覧やMR一覧で重複しているラベルが付与されているものを探してIssueやMRの編集画面でラベルを設定する、という流れを1つ1つ対応するのはそれこそ時間の無駄です。
APIを使ったラベルの修正
幸い、GitLabではプロジェクトラベルやグループラベル、MRを操作するAPIが提供されています。
これらのAPIを利用して以下のようなスクリプトを作成しました。
#!/bin/bash # require # - httpie # - jq # - GitLab Administrator Role & User Token TOKEN=************ total_pages=$(http --ignore-stdin 'https://gitlab.example.com/api/v4/groups/1/merge_requests?labels='"review requested(project)"'&per_page=100' Private-Token:$TOKEN --verbose | grep X-Total-Pages | awk {'print $2'} | sed -e 's/ //g') for page in $(seq 1 ${total_pages}) do http --body --ignore-stdin \ 'https://gitlab.example.com/api/v4/groups/1/merge_requests?labels='"review requested(project)"'&per_page=100&page='${page}'' \ Private-Token:$TOKEN | jq '.[]|{ id: .id, iid: .iid, project_id: .project_id, labels: .labels}' done > request.json jq -c '.' request.json | while read mr do id=$(echo ${mr} | jq '.id') iid=$(echo ${mr} | jq '.iid') pid=$(echo ${mr} | jq '.project_id') labels=$(echo ${mr} | jq '.labels|join(",")') replaced_labels=$(echo ${labels} | jq -r 'sub("review requested\\(project\\)";"review requested")') http --body --ignore-stdin \ PUT 'https://gitlab.example.com/api/v4/projects/'${pid}'/merge_requests/'${iid}'?labels='"${replaced_labels}"'' \ Private-Token:$TOKEN sleep 5 done
今回は重複しているラベルが少なかったため、ラベル名は引数ではなく直接定義してしまっています。大雑把には以下のようなフローです。
- 対象グループ内でリネーム後のプロジェクトラベルが付与されたMRの一覧(ラベル定義の変更に必要なメタ情報を含むJSON)を作成
- ラベル定義を修正したJSONを使って各MRの情報を更新
- スクリプト内ではラベルの置換をしていますが、前述の対応方法に記載されているようにプロジェクトラベルを削除することで更新されるため、置換ではなく単純な追加でもOKです
各プロジェクトのラベル一覧を確認して重複しているラベルがあったらスクリプトを適宜修正して実行、という対応で重複を解消しました。
すべてのグループやプロジェクトに対してラベル重複のチェックをして解消したい場合は、以下のような各言語向けのモジュールを使ったスクリプトを作成するのも良いかもしれません。
結論
- 汎用的なラベルはグループラベルに定義すること
- 既存のラベルと同名のラベルを適当に作らない
- 実際にラベルを使う場面をちゃんと考えましょう