【GitHubActions】PR作成時にレビュワーを自動設定するワークフローを作ってみた


こちらはディップ Advent Calendar 2024の記事になります。　ぜひ他の記事も見てください!!!
https://qiita.com/advent-calendar/2024/dip-dev

# はじめに

2024年4月からバックエンドのエンジニアとしてディップ株式会社に新卒で入社しました
[@oknyoyoyo](https://qiita.com/oknyoyoyo)と申します。
新卒で配属したチームではGo、Laravelを用いたバックエンドの開発を行なっていました。
現在は所属チームが変わり、業務で主にGoを使ったバックエンドの開発とNext.jsを用いたフロントエンドの開発をしています。

今回はGitHubActionsで、PR作成時に自動でレビュワーを設定するActionsを作った経験を
アドベントカレンダーの機会に備忘録として記事に残してみようと思います。


# 自動レビュアー設定ワークフローを作った背景

元々、役割の異なる複数のチームでバックエンドの開発を進めていたのですが、それらを
１つのチームにまとめて開発を行うという体制の変更がありました。
<br>

新体制としてチームを進めていく中で開発ルールにも見直しが入り、コードレビューのルールもその対象でした。

既存のルールとしては「別チームの人を一人以上含めて3人以上のレビュワーにapproveを貰う必要がある」というルールがありました。

これは、「コードレビューをチーム毎に実施すると、役割に応じた実装ノウハウが各チームの中でのみ蓄積されてしまう。そのため、複数チーム混合でコードレビューを行うことで、実装ノウハウを全チームの共通認識として属人性の排除に繋げる」といった目的があります。

一つのチームにまとまった後もこのルールを踏襲してPRの作成者が自分でレビュアーを3人設定する形で進めておりました。

<br>
しかし、このルールだといくつかの問題点が出てきました。

## 既存ルールでの問題点

**1. 単純に毎回3名選んで設定するのが面倒**

- 単純に毎回3人手動で選ぶことが面倒ということ、またPR作成時の選び忘れによりレビュー漏れが発生し、マージまでの時間が延びることで開発生産性が下がるリスクもあります

 **2. レビュアーが偏る**

- PR作成者が手動でレビュアーを設定する際に、レビュアーがある特定の人に偏る傾向があることです
    - これは人間が設定している以上避けられないものだと考えています。誰だって優しく速く正確にレビューしてくれる人にレビューを頼みたいものです
    - 複数のチームが統一してできたチームであるという背景からチームメンバー同士の関係性が既に構築されていたことも偏りを助長していたと思います
    
**3. 歴の浅いメンバーのレビュー機会が減ってしまう**

- 2の理由からどうしても技術や仕様に詳しく、メンバー間の関係性が構築されているベテランメンバーにレビュー設定が集中し、我々のような新卒メンバーのレビュー機会が少なくなってしまいました
- これでは私のような新卒メンバーのレビューへの苦手意識が無くならないし、チームとしてみても属人化が進みよくない方向に進んでしまいます

これらの問題を解消するためPR作成時にランダムでレビュアーを3人設定するワーフクローを作成する形になりました

# ワークフローのコードの解説

ここからは自動レビュアー設定ワークフローのコードを実際に見ながらactionsを構成している要素についても説明します

**name**
- GitHub上で実行時に表示される文言
- Actionsの説明を簡単に書く

**envs**
- このワークフローで使用される定数を環境変数として定義

**on**
- このActionsが実行されるトリガーとなる条件を記載している
    -  今回だとプルリクエストが開かれた時、もしくはドラフト状態から開かれた時
- actionsが実行されるトリガー条件としてマージ先ブランチも指定している

**jobs**
- 次からに説明する一連の`steps`をまとめた範囲
- また、ここでランナーの設定も行っている
- 作動するマージ元のブランチも指定している

```shell
name: レビュアーをランダムに設定する

env:
  MAX_REVIEWERS: 3

on:
  pull_request:
    types: [opened, ready_for_review]
    branches: ['develop', 'release/*']
jobs:
  assign_reviewers:
    if: startsWith(github.event.pull_request.head.ref, 'feature/')
    runs-on: self-hosted
```

**今回のactionsの作動条件としては下記となる**

- `feature/`という文字列から始まるブランチを、`develop`ブランチ、または`release/`
- から始まるブランチにマージしようとするPRを作成、レビュー可能にしたタイミング


**steps**
- `jobs`を構成する各処理の単位
- 今回は各stepsごとに簡単に処理内容を解説していきます

## 1. ドラフト状態か確認

- actionsは実行完了までにそこそこ時間がかかるため不要な場合には処理を実行させないようにすることが好ましいと考えている
- 今回はドラフト状態をレビューの準備ができていない状態とチームルールで定義しており、処理をスキップしたいためドラフト状態かをこのstepsで取得する
- 別の`steps`で取得した値を参照するために環境変数として情報を渡す

```shell
steps:
    - name: ドラフト状態か確認する
      // 環境変数として出力して他のstepsでも参照できるように
      run: echo 'IS_DRAFT=${{ github.event.pull_request.draft }}' >> $GITHUB_ENV
```

## 2. 既存のレビュアー情報を取得する
- すべて自動設定ではなく、「今回のPRはAさんには必ず見てもらいたい」といったケースが運用上存在する
- 今回のactionsはそのようなケースにも対応できるように、手動でレビュアーを設定している場合には3人に足りない分だけactionsが補充してくれるような機能になっている
- また、手動ですでに3人設定されている場合にはこれ以降の処理をスキップする
- これらの機能のためにこのstepsで既存レビュアーの情報を取得する

**uses**
- stepsで使用するアクションを指定
- 今回は`actions/github-script`というGitHubが提供する公式アクションで、Node.js スクリプトを簡単に実行するためのものを指定

**with**
- `uses`で使う変数、スクリプトを定義している
- `github-token`を環境変数から参照することで既存のレビュアー情報にアクセスできる

```shell
      - name: 既存のレビュアーの情報を取得する
        if: env.IS_DRAFT == 'false'
        uses: actions/github-script@v6
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
          // 対象のPRのレビュアー情報を取得する
            const { data: reviewers } = await github.rest.pulls.listRequestedReviewers({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.payload.pull_request.number,
            });
            
            //レビュアーの人数を取得し、環境変数として出力して他のstepsでも参照できるように
            const reviewersCount = reviewers.users.length;
            core.exportVariable('EXISTING_REVIEWERS_COUNT', reviewersCount);

            //既存レビュアーを一人ずつ環境変数として出力
            reviewers.users.forEach((reviewer, index) => {
              core.exportVariable(`REVIEWER_${index + 1}`, reviewer.login);
            });
            
```

## 3. PR作成者の情報を取得

- PR作成者がレビュアーに設定されることを防ぐためにPR作成者の情報を取得する

```shell
      - name: PR作成者の情報を取得する
        if: env.EXISTING_REVIEWERS_COUNT < env.MAX_REVIEWERS && env.IS_DRAFT == 'false'
        // 環境変数として出力して他のstepsでも参照できるように
        run: echo 'PR_AUTHOR=${{ github.event.pull_request.user.login }}' >> $GITHUB_ENV

```

## 4. レビュアーをランダムに選択する

- このstepsが処理の主となり長いため、コード全体を載せた後に、各セクションごとに解説する

コード全体はこれ

```shell
- name: レビュアーをランダムに選択する
        if: env.EXISTING_REVIEWERS_COUNT < env.MAX_REVIEWERS && env.IS_DRAFT == 'false'
        run: |
          REVIEWER_LIST1=('example_name1' 'example_name2' 'example_name3' 'example_name4' 'example_name5')

          // レビュアーを排除する関数
          remove_reviewer() {
            local removed_reviewer=$1
            shift
            local reviewers=("$@")
            local filtered_reviewers=()
            for reviewer in "${reviewers[@]}"; do
              if [[ "$reviewer" != "$removed_reviewer" ]]; then
                filtered_reviewers+=("$reviewer")
              fi
            done
            echo "${filtered_reviewers[@]}"
          }
          
          # 既存レビュアーを配列に格納する
          EXISTING_REVIEWERS=()
          for i in $(seq 1 $EXISTING_REVIEWERS_COUNT); do
            REVIEWER_VAR="REVIEWER_$i"
            REVIEWER_NAME="${!REVIEWER_VAR}"
            EXISTING_REVIEWERS+=("$REVIEWER_NAME")
          done
          
          # PR作成者をレビュアー候補から削除する
          REVIEWER_LIST=($(remove_reviewer "$PR_AUTHOR" "${REVIEWER_LIST[@]}"))
          
          # 既存レビュアーをレビュアー候補から削除し、該当の配列のカウントを増加させる
          COUNT_REVIEWERS_LIST=0
          
          for existing_reviewer in "${EXISTING_REVIEWERS[@]}"; do
            initial_length=${#REVIEWER_LIST[@]}
            REVIEWER_LIST=($(remove_reviewer "$existing_reviewer" "${REVIEWER_LIST[@]}"))
            if [[ ${#REVIEWER_LIST[@]} -lt $initial_length ]]; then
              COUNT_REVIEWERS_LIST=$((COUNT_REVIEWERS_LIST + 1))
            fi
          done

         # レビュアー候補の配列から選ぶ人数を設定する
         NUMBER_LIST_TO_SELECT=$((3 - COUNT_REVIEWERS_LIST))

         # レビュアー候補リストから上記で設定した人数分だけレビュアーを選択
         SELECTED_REVIEWERS=$(shuf -e "${REVIEWER_LIST[@]}" -n "$NUMBER_LIST_TO_SELECT" | tr '\n' ',' | sed 's/,$//')

         # 環境変数として出力して他のstepsでも参照できるように
         echo "SELECTED_REVIEWERS=${SELECTED_REVIEWERS}" >> $GITHUB_ENV
```

### チームメンバーのアカウント名を格納する配列を定義する
- GitHub上のTeamという機能を用いればコード上でアカウント名を管理しなくて良くなり保守が必要なくなるため、そっちの方が望ましい
- 今回はTeam内に他チームの方や非プログラマー(コードを書かない人)の方がいたので、
コード上でメンバーを管理している
```shell
MEMBER_LIST1=('example_name1' 'example_name2' 'example_name3' 'example_name4' 'example_name5')
```
### レビュアー候補から対象の人物を取り除く関数を定義
- 第一引数に取り除きたい人物、第二引数にレビュアー候補リストを受け取る
- レビュアーリストに取り除きたい人物がいないかどうかを順番に判定を行い、存在した場合にはリストから取り除く
- 上記の処理を行った後のレビュアー候補リストを返却している

```shell
          remove_reviewer() {
            local removed_reviewer=$1
            shift
            local reviewers=("$@")
            local filtered_reviewers=()
            for reviewer in "${reviewers[@]}"; do
              if [[ "$reviewer" != "$removed_reviewer" ]]; then
                filtered_reviewers+=("$reviewer")
              fi
            done
            echo "${filtered_reviewers[@]}"
          }
``` 

### 既存レビュアーを配列に格納する
- [2](https://qiita.com/oknyoyoyo/private/61d458a38d6de49c3713#2-%E6%97%A2%E5%AD%98%E3%81%AE%E3%83%AC%E3%83%93%E3%83%A5%E3%82%A2%E3%83%BC%E6%83%85%E5%A0%B1%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B)のstepsで取得した既存レビュアーを配列に格納する
- [2](https://qiita.com/oknyoyoyo/private/61d458a38d6de49c3713#2-%E6%97%A2%E5%AD%98%E3%81%AE%E3%83%AC%E3%83%93%E3%83%A5%E3%82%A2%E3%83%BC%E6%83%85%E5%A0%B1%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B)で行わなかった理由としては3人以上設定されていた場合やドラフト状態のPR作成の場合にはなるべく早く処理をスキップさせるために、2で行う処理を少なくするため
 
```shell
          EXISTING_REVIEWERS=()
          for i in $(seq 1 $EXISTING_REVIEWERS_COUNT); do
            REVIEWER_VAR="REVIEWER_$i"
            REVIEWER_NAME="${!REVIEWER_VAR}"
            EXISTING_REVIEWERS+=("$REVIEWER_NAME")
          done
```

### PR作成者と既存レビュアーを候補リストから削除する

- PR作成者と既存レビュアーを上記で定義した関数を用いて候補リストから取り除く
- 既存レビュアーを取り除いた数に関しては次の処理で使うために記録しておく
```shell
          # PR作成者をレビュアー候補から削除する
          REVIEWER_LIST1=($(remove_reviewer "$PR_AUTHOR" "${REVIEWER_LIST1[@]}"))
         
          # 既存レビュアーをレビュアー候補から削除し、該当の配列のカウントを増加させる
          COUNT_REVIEWERS_LIST=0
          
          for existing_reviewer in "${EXISTING_REVIEWERS[@]}"; do
            initial_length=${#REVIEWER_LIST[@]}
            REVIEWER_LIST=($(remove_reviewer "$existing_reviewer" "${REVIEWER_LIST[@]}"))
            if [[ ${#REVIEWER_LIST[@]} -lt $initial_length ]]; then
              COUNT_REVIEWERS_LIST=$((COUNT_REVIEWERS_LIST + 1))
            fi
          done
```

### レビュアーを選択する
- `shuf`コマンドを用いてレビュアー候補リストからランダムにレビュアーを選択する
- `-n`オプションの後に数字を渡すことでその数字分だけ要素を `-e`オプションで渡した配列から選択する
```shell
# レビュアー候補の配列から選ぶ人数を設定する
NUMBER_LIST_TO_SELECT=$((3 - COUNT_REVIEWERS_LIST))

# レビュアー候補リストから上記で設定した人数分だけレビュアーを選択
SELECTED_REVIEWERS=$(shuf -e "${REVIEWER_LIST[@]}" -n "$NUMBER_LIST_TO_SELECT" | tr '\n' ',' | sed 's/,$//')

# 環境変数として出力して他のstepsでも参照できるように
echo "SELECTED_REVIEWERS=${SELECTED_REVIEWERS}" >> $GITHUB_ENV
```

## 5. 選択されたレビュアーを設定する

- `github-token`を環境変数から取得することでレビュアーにアクセスできる
- 先ほどのstepsで出力した環境変数`SELECTED_REVIEWERS`をレビュアーに設定する
```shell
- name: 選択されたレビュアーを設定する
        if: env.EXISTING_REVIEWERS_COUNT < env.MAX_REVIEWERS && env.IS_DRAFT == 'false'
        uses: actions/github-script@v6
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const reviewers = '${{ env.SELECTED_REVIEWERS }}'.split(',');
            await github.rest.pulls.requestReviewers({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.payload.pull_request.number,
              reviewers: reviewers 
            });
```

# appendix

今回の例では1つの配列にすべてのメンバーを格納しているが、配列の数を増やすことで各グループから一人ずつ選ばれる様にするといった事も可能である。
これによって自分たちのチームでは以下の二つを実現することができたと思います。
<br>

- 私たち新卒に高い頻度でレビューの機会を与えつつ、ベテランの人も確実に選ばれる様にすることでコードの品質を担保しつつ属人化の解消を進めていくこと

<br>

- 任意のグループを作成し、そのメンバーが必ず選ばれる様にすること
    - 今回、私たちのチームではPHP,Laravelのバージョンアップを進めているチームと通常の施策を進めるグループの二つに別れて開発を進めておりました
    - その様な体制の中で施策のために追加、編集したコードがバージョンアップに準拠しているかどうかをバージョンアップチームにレビューしてもらうためにバージョンアップチームが必ずレビュアーに設定される様な仕組みづくりをワークフローを用いる事で行いました



##  レビュアーが3人以上設定されている際のみに行うstepsを追加した。

- レビュアーが既存で3人以上設定されている際には自動レビュアーのワークフローは作動しない設定にしておりました。
    - そのため既存の3人にバージョンアップチームが設定されていなかった場合,
    バージョンアップに準拠したコードかレビューする人物がいないままapproveされてしまうリスクがあります
- レアケースであるが既存で3人以上設定されていて特定のリストのメンバーが存在しなかった場合には4人目として追加するように下記のコードを既存レビュアー情報取得後に追加しました
    

```shell
#レビュアーが3人以上設定されている場合に実行(PHPバージョンアップが終わったらこのstepは削除する)
      - name: 既存のレビュアーにREVIEWER_LIST1のメンバーが含まれているか確認する
        if: env.EXISTING_REVIEWERS_COUNT >= env.MAX_REVIEWERS && env.IS_DRAFT == 'false'
        run: |
          REVIEWER_LIST1=('version_up_member_1' 'version_up_member_2' 'version_up_member_1')
     
          # 既存レビュアーを配列に格納する
          EXISTING_REVIEWERS=()
          for i in $(seq 1 $EXISTING_REVIEWERS_COUNT); do
            REVIEWER_VAR="REVIEWER_$i"
            REVIEWER_NAME="${!REVIEWER_VAR}"
            EXISTING_REVIEWERS+=("$REVIEWER_NAME")
          done

          # REVIEWER_LIST1のメンバーが既存のレビュアーに含まれているかをチェック
          ANY_REVIEWER_IN_LIST1=false
          for reviewer in "${REVIEWER_LIST1[@]}"; do
            if [[ " ${EXISTING_REVIEWERS[@]} " =~ " ${reviewer} " ]]; then
              ANY_REVIEWER_IN_LIST1=true
              break
            fi
          done

          if [ "$ANY_REVIEWER_IN_LIST1" = false ]; then
            SELECTED_REVIEWERS=$(shuf -e "${REVIEWER_LIST1[@]}" -n 1 | tr '\n' ',' | sed 's/,$//')
            echo "SELECTED_REVIEWERS=${SELECTED_REVIEWERS}" >> $GITHUB_ENV
          fi

      #レビュアーが3人以上設定されている場合に実行(PHPバージョンアップが終わったらこのstepは削除する)
      - name: 選択されたレビュアーを設定する
        if: env.EXISTING_REVIEWERS_COUNT >= env.MAX_REVIEWERS && env.IS_DRAFT == 'false'
        uses: actions/github-script@v6
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const reviewers = '${{ env.SELECTED_REVIEWERS }}'.split(',');
            await github.rest.pulls.requestReviewers({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.payload.pull_request.number,
              reviewers: reviewers 
            });
```

## レビュアー候補のグループを3つに増やしたstepsの例
- 今回は先ほど述べたようにバージョンアップ対応のために`REVIEWER_LIST1`のメンバーが必ず選択されるように条件文を追加しました
```shell
    # 各レビュアー候補の配列から選ぶ人数を設定する
          NUMBER_LIST1_TO_SELECT=$((1 - COUNT_REVIEWERS_LIST1))
          NUMBER_LIST2_TO_SELECT=$((1 - COUNT_REVIEWERS_LIST2)) 
          NUMBER_LIST3_TO_SELECT=$((1 - COUNT_REVIEWERS_LIST3)) 

          if [[ $NUMBER_LIST1_TO_SELECT -lt 0 ]]; then
            NUMBER_LIST1_TO_SELECT=0
            NUMBER_LIST2_TO_SELECT=1
            NUMBER_LIST3_TO_SELECT=0
          fi

          if [[ $NUMBER_LIST2_TO_SELECT -lt 0 ]]; then
            NUMBER_LIST1_TO_SELECT=1
            NUMBER_LIST2_TO_SELECT=0
            NUMBER_LIST3_TO_SELECT=0
          fi

          if [[ $NUMBER_LIST3_TO_SELECT -lt 0 ]]; then
            NUMBER_LIST1_TO_SELECT=1
            NUMBER_LIST2_TO_SELECT=0
            NUMBER_LIST3_TO_SELECT=0
          fi

```

## 各LISTから選出したメンバーを最終的に一つにまとめる処理を追加
 ```shell
SELECTED_REVIEWERS=$(echo "${SELECTED_REVIEWER_LIST1},${SELECTED_REVIEWER_LIST2},${SELECTED_REVIEWER_LIST3}")
```

## 変更したstepsのコード全体はこちら
```shell
- name: レビュアーをランダムに選択する
        if: env.EXISTING_REVIEWERS_COUNT < env.MAX_REVIEWERS && env.IS_DRAFT == 'false'
        run: |
          # メンバー配列(メンバーの増減に応じて適宜変更必要)
          REVIEWER_LIST1=('version_up_member_1' 'version_up_member_2' 'version_up_member_1')
          REVIEWER_LIST2=('example_member1' 'example_member2' 'example_member3' 'example_member4')
          REVIEWER_LIST3=('example_member5' 'example_member6' 'example_member7' 'example_member8')
          
          remove_reviewer() {
            local removed_reviewer=$1
            shift
            local reviewers=("$@")
            local filtered_reviewers=()
            for reviewer in "${reviewers[@]}"; do
              if [[ "$reviewer" != "$removed_reviewer" ]]; then
                filtered_reviewers+=("$reviewer")
              fi
            done
            echo "${filtered_reviewers[@]}"
          }
          
          # 既存レビュアーを配列に格納する
          EXISTING_REVIEWERS=()
          for i in $(seq 1 $EXISTING_REVIEWERS_COUNT); do
            REVIEWER_VAR="REVIEWER_$i"
            REVIEWER_NAME="${!REVIEWER_VAR}"
            EXISTING_REVIEWERS+=("$REVIEWER_NAME")
          done

           # PR作成者をレビュアー候補から削除する
          REVIEWER_LIST1=($(remove_reviewer "$PR_AUTHOR" "${REVIEWER_LIST1[@]}"))
          REVIEWER_LIST2=($(remove_reviewer "$PR_AUTHOR" "${REVIEWER_LIST2[@]}"))
          REVIEWER_LIST3=($(remove_reviewer "$PR_AUTHOR" "${REVIEWER_LIST3[@]}"))

          COUNT_REVIEWERS_LIST1=0
          COUNT_REVIEWERS_LIST2=0
          COUNT_REVIEWERS_LIST3=0

          # 既存レビュアーをレビュアー候補から削除し、該当の配列のカウントを増加させる
          for existing_reviewer in "${EXISTING_REVIEWERS[@]}"; do
            initial_length=${#REVIEWER_LIST1[@]}
            REVIEWER_LIST1=($(remove_reviewer "$existing_reviewer" "${REVIEWER_LIST1[@]}"))
            if [[ ${#REVIEWER_LIST1[@]} -lt $initial_length ]]; then
              COUNT_REVIEWERS_LIST1=$((COUNT_REVIEWERS_LIST1 + 1))
            fi
  
            initial_length=${#REVIEWER_LIST2[@]}
            REVIEWER_LIST2=($(remove_reviewer "$existing_reviewer" "${REVIEWER_LIST2[@]}"))
            if [[ ${#REVIEWER_LIST2[@]} -lt $initial_length ]]; then
              COUNT_REVIEWERS_LIST2=$((COUNT_REVIEWERS_LIST2 + 1))
            fi

            initial_length=${#REVIEWER_LIST3[@]}
            REVIEWER_LIST3=($(remove_reviewer "$existing_reviewer" "${REVIEWER_LIST3[@]}"))
            if [[ ${#REVIEWER_LIST3[@]} -lt $initial_length ]]; then
              COUNT_REVIEWERS_LIST3=$((COUNT_REVIEWERS_LIST3 + 1))
            fi
          done

          # 各レビュアー候補の配列から選ぶ人数を設定する
          NUMBER_LIST1_TO_SELECT=$((1 - COUNT_REVIEWERS_LIST1))
          NUMBER_LIST2_TO_SELECT=$((1 - COUNT_REVIEWERS_LIST2)) 
          NUMBER_LIST3_TO_SELECT=$((1 - COUNT_REVIEWERS_LIST3)) 

          if [[ $NUMBER_LIST1_TO_SELECT -lt 0 ]]; then
            NUMBER_LIST1_TO_SELECT=0
            NUMBER_LIST2_TO_SELECT=1
            NUMBER_LIST3_TO_SELECT=0
          fi

          if [[ $NUMBER_LIST2_TO_SELECT -lt 0 ]]; then
            NUMBER_LIST1_TO_SELECT=1
            NUMBER_LIST2_TO_SELECT=0
            NUMBER_LIST3_TO_SELECT=0
          fi

          if [[ $NUMBER_LIST3_TO_SELECT -lt 0 ]]; then
            NUMBER_LIST1_TO_SELECT=1
            NUMBER_LIST2_TO_SELECT=0
            NUMBER_LIST3_TO_SELECT=0
          fi
          
          # レビュアーを選択する
          SELECTED_REVIEWER_LIST1=$(shuf -e "${REVIEWER_LIST1[@]}" -n "$NUMBER_LIST1_TO_SELECT" | tr '\n' ',' | sed 's/,$//')
          SELECTED_REVIEWER_LIST2=$(shuf -e "${REVIEWER_LIST2[@]}" -n "$NUMBER_LIST2_TO_SELECT" | tr '\n' ',' | sed 's/,$//')
          SELECTED_REVIEWER_LIST3=$(shuf -e "${REVIEWER_LIST3[@]}" -n "$NUMBER_LIST3_TO_SELECT" | tr '\n' ',' | sed 's/,$//')

          SELECTED_REVIEWERS=$(echo "${SELECTED_REVIEWER_LIST1},${SELECTED_REVIEWER_LIST2},${SELECTED_REVIEWER_LIST3}")
          echo "SELECTED_REVIEWERS=${SELECTED_REVIEWERS}" >> $GITHUB_ENV
```

# 実際に運用してみて

実際に数ヶ月このワークフローをチームで運用してみて良かった点、改善点をチームリーダーとメンバーに聞いてみたのでまとめてみます

## 良かった点
-  経験年数が多いメンバーにレビュアーが偏らなくなった点
-  若手メンバーのレビュー参加の機会が増えた点
-  レビュアー設定忘れがなくなるためレビューされずに放置されることがなくなった点
-  PHPバージョンアップなど特定のメンバーを必ずレビュアーとして設定できるようになった点

## 改善点
- コード上でメンバーを管理しているために人が入れ替わるたびに更新が必要
- ワークフローの実行完了までが遅い(待っている間に手動で選択したほうが早い)
- 自動と手動が切り替えられると良い
    - labelを作成して特定のlabelが設定されている時には実行しない様に条件を追加すればできそう
- DraftPRで3人からapprove貰って、マージしようと思いPRオープンしたら3人中2人が再レビューになり、さらにもう1人追加された(
    - Draft中にレビューをもらう想定で作成していなかったので検証が不十分、どこかしらに考慮漏れがあると考えられる

# おわりに
初めてGitHubActionsを用いてワークフローを実際に作成してみることでActionsの書き方を理解することができたので非常に良い経験となりました。

また、ただ無駄な作業を短縮するだけでなくチームの開発体験をより良いものにしていく事に少しでも貢献できた事が嬉しく感じました。これはPR作成というチームメンバーが全員行う作業に影響を与えることのできるGitHubActionsの魅力だと思います。

次はAPIの開発時にドキュメントの生成、更新が自動で出来たらすごく嬉しいので調べてみようと思います🫡