🤝

GitHubActionsでProjectsにfield設定しながら紐付け

2024/10/21に公開

はじめに

初めましての方は初めまして!PREVENTで働いているバックエンド新米エンジニアのとぴ(@topi_log)と申します!
今回は業務で、作成したissueやPRを自動でGitHubProjectsに紐付けるためにGitHubActionsの作成を行いました。
その作成方法についてまとめさせていただきます。

目的

今回やりたいのは下記の要件を満たすワークフローです。

  • issueやPRを作成したら特定のProjectに紐づけてほしい
  • Projectの紐づけ時にはStatusとProductを設定してほしい(いずれもシングルセレクト)
  • PRは紐づけたあとレビュワーをアサインした時でStatusを変更してほしい
  • リポジトリ毎に設定したいProductが異なるが全部が異なるProductというわけではない
  • 関係のある全てのリポジトリでできるようにしてほしい

列挙するだけだとイメージがつきにくいと思いますので、図で表します。

各リポジトリで作成されたissueやPRは、それぞれ用意されたProjectに紐づくようになっています。
それぞれのリポジトリとは別に、全てのissueやPRの状態を把握できるProjectALLがあります。
このProjectALLに各リポジトリで作成したissueやPRを、作成時に自動で紐づけるようにしたい、というのが今回の目的です。
リポジトリにProjectを紐づけたり、Projectのワークフローを使えばGitHubActionsを使う必要はないといえばないのですが、今回は「一度に複数の設定を行いたい」要件があったためActionsを作ることにしました。

また、リポジトリごとに設定するフィールド値は異なったり同じだったりするものの、作成するActionsのワークフロー自体は同じです。
全てのリポジトリにそれを用意するのかと考えていましたが、先輩から「ワークフローの再利用というものがあるよ」と教えて頂き、今回はそちらを活用することにしました。
https://docs.github.com/ja/actions/sharing-automations/reusing-workflows

実装

それでは実装フェーズに入ります。
今回は各リポジトリの状況に左右されないよう、再利用可能なワークフローだけが置かれているリポジトリを用意しました。
まずはそのリポジトリに作成していきます。
なお、別のリポジトリから再利用可能なワークフローを呼び出すには、再利用可能なワークフローを作成したリポジトリに権限設定をする必要があります。
リポジトリのSettings⇨左カラムのActions⇨General⇨一番下のAccessの項目で「Accessible from repositories in the '組織名' organization」を選択すると、他のリポジトリから呼び出すことができるようになります。
※個人だとこのやり方ではありません。詳細は下記を御覧ください。
https://docs.github.com/ja/actions/sharing-automations/reusing-workflows#access-to-reusable-workflows

前提

アクションには下記3種類があります

  • 複合アクション
  • JavaScriptアクション
  • Dockerコンテナーのアクション

https://docs.github.com/ja/actions/sharing-automations/creating-actions/about-custom-actions

今回はGitHub CLIを使いたかったので複合アクションでの実装方法です。
https://docs.github.com/ja/github-cli/github-cli/about-github-cli
GitHub CLIを使った理由は、GitHubActionsでは標準で導入されており、ワークフロー内でインポートの必要がないことから選びました。
また、GitHubCLIで今回やりたいことが実現でき、複数のサードパーティなactionsやパッケージのインストールが必要ないのも魅力の一つです。
https://cli.github.com/

issueを作成したらProjectに紐づける再利用可能なワークフロー

まずざっくりとフローを考えます。

  1. issueが作成されたらProjectに紐づける
  2. Projectに紐づけた後にStatusやProductの項目を設定する

やりたいことはこの2つです。
GitHubCLIでこれを実現できるのが下記のコマンドです。

  1. issueが作成されたらProjectに紐づける
gh project item-add Projectの番号 --owner Projectやリポジトリの持ち主 --url issueのURL

Projectの番号:ProjectのURLにある番号です
(例:https://github.com/orgs/組織名/projects/14なら14

  1. Projectに紐づけた後にStatusやProductの項目を設定する
gh project item-edit --id Project内でのItemID --field-id 設定したいフィールドのグローバルID --project-id ProjectのグローバルID --single-select-option-id フィールドに設定したいオプションのグローバルID

Project内でのItemID
Projectに紐づいたissueやPRは他のものと衝突しないよう個別にIDが割り当てられます。

設定したいフィールドのグローバルID
今回でいうとStatusやProductといったシングルセレクト事に設定されているグローバルIDです

ProjectのグローバルID
こちらは先程のProjectの番号とは別のグローバルIDです

フィールドに設定したいオプションのグローバルID
今回でいうとStatusであればBacklogやIn Progress、ProductであればProductXやProductYといったオプションのグローバルIDです。

全体のフロー

上記の2つをクリアするために、必要な値を取ってこなければなりません。
ですので流れとしては

  1. issueをProjectに紐づけ
  2. ProjectのグローバルID、フィールドやそのオプションのグローバルIDを取得
  3. プロジェクトに紐づけられたissueのProject内でのItemID取得
  4. 取得した各種IDを使ってStatusなどを設定

となります。(2,3はどちらが先でもOK)
このフローをもとにステップを分け、それぞれ実装を行っていきます。

実装

実装した内容の後に、各項目を解説していきます。
ということでこちらが実装内容です。

name: Issue assignment to Project
on:
  workflow_call:
    inputs:
      ProjectNumber:
        description: "紐づけたいProjectの番号"
        required: true
        type: number
      HTML_URL:
        description: "IssueのHTML URL"
        required: true
        type: string
      FieldKeyValues:
        description: "設定したいSelect FieldのKeyとValueの組み合わせ"
        required: true
        type: string
      RepositoryName:
        description: "リポジトリ名"
        required: true
        type: string
      IssueNumber:
        description: "紐づけたいissueの番号"
        required: true
        type: number
    secrets:
      GH_TOKEN:
        description: "GitHub Token"
        required: true

jobs:
  assign_to_project:
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: bash
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Add Item Project
        run: gh project item-add ${{ inputs.ProjectNumber }} --owner ${{ github.repository_owner }} --url ${{ inputs.HTML_URL }}
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}

      - name: Get Global ID
        run: |
          QUERY="
            query {
              organization(login: \"${{ github.repository_owner }}\") {
                projectV2(number: ${{ inputs.ProjectNumber }}) {
                  id
                  fields(first: 100) {
                    nodes {
                      ... on ProjectV2SingleSelectField {
                        id
                        name
                        options {
                          id
                          name
                        }
                      }
                    }
                  }
                }
              }
            }
          "

          gh api graphql -f query="$QUERY" > project_data.json
          echo "PROJECT_ID=$(jq -r '.data.organization.projectV2.id' project_data.json)" >> $GITHUB_ENV

          field_values=$(echo '${{ inputs.FieldKeyValues }}' | jq -c '.[]')
          enc_data=""
          while IFS= read -r key_value; do
            field_name=$(echo "$key_value" | jq -r '.key')
            value_name=$(echo "$key_value" | jq -r '.value')

            field_id=$(jq -r --arg field_name "$field_name" '.data.organization.projectV2.fields.nodes[] | select(.name == $field_name) | .id' project_data.json)
            value_id=$(jq -r --arg field_name "$field_name" --arg option_name "$value_name" '.data.organization.projectV2.fields.nodes[] | select(.name == $field_name).options[] | select(.name == $option_name) | .id' project_data.json)

            enc_data+="$field_id=$value_id,"
          done <<< "$field_values"

          clean_enc_data=$(echo "$enc_data" | sed 's/,$//')
          echo $clean_enc_data
          echo "FIELD_ID_VALUES=$clean_enc_data" >> $GITHUB_ENV
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}

      - name: Get Item ID
        run: |
          QUERY="
            query {
              repository(owner: \"${{ github.repository_owner }}\", name: \"${{ inputs.RepositoryName }}\") {
                issue(number: ${{ inputs.IssueNumber }}) {
                  projectItems(first: 1) {
                    nodes {
                      id
                    }
                  }
                }
              }
            }
          "

          ITEM_ID=$(gh api graphql -f query="$QUERY" --jq '.data.repository.issue.projectItems.nodes[0].id')
          echo "ITEM_ID=$ITEM_ID" >> $GITHUB_ENV
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}

      - name: Set Fields
        run: |
          for field_id_value in $(echo "${{ env.FIELD_ID_VALUES }}" | tr ',' '\n'); do
            field_id=$(echo $field_id_value | cut -d '=' -f1)
            value_id=$(echo $field_id_value | cut -d '=' -f2)

            gh project item-edit --id "${{ env.ITEM_ID }}" --field-id "$field_id" --project-id "${{ env.PROJECT_ID }}" --single-select-option-id "$value_id"
          done
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}

各項目を解説していきます。

値設定

on:
  workflow_call:
    inputs:
      ProjectNumber:
        description: "紐づけたいProjectの番号"
        required: true
        type: number
      HTML_URL:
        description: "IssueのHTML URL"
        required: true
        type: string
      FieldKeyValues:
        description: "設定したいSelect FieldのKeyとValueの組み合わせ"
        required: true
        type: string
      RepositoryName:
        description: "リポジトリ名"
        required: true
        type: string
      IssueNumber:
        description: "紐づけたいissueの番号"
        required: true
        type: number
    secrets:
      GH_TOKEN:
        description: "GitHub Token"
        required: true

inputsやsecretsでは、このワークフローを利用するワークフローから値を受け取ります。
説明は上記に書いてあるとおりですが、2点ほど補足します。

FieldKeyValues
こちらはフィールド名とオプション名を配列形式で受け取るようにします。
'[{"key":"Status","value":"Backlog"},{"key":"Product","value":"ProductX"}]'と言ったものを受け取ります。

GH_TOKEN
GitHubのトークンを取得するのですが、ここで設定するのはPersonarl Access Token(以降PAT)です。
PATは個人で発行でき、権限を色々付与できます。
今回必要なPATの権限はrepo, admin:org, projectの3つです。
個人のリポジトリやProjectであってもadmin:orgが必要なので注意が必要です。

1. issueをProjectに追加

- name: Add Item Project
  run: gh project item-add ${{ inputs.ProjectNumber }} --owner ${{ github.repository_owner }} --url ${{ inputs.HTML_URL }}
  env:
    GH_TOKEN: ${{ secrets.GH_TOKEN }}

GitHub CLIを使ってProjectにissueを紐づけます。
https://cli.github.com/manual/gh_project_item-add

2. 各種グローバルIDを取得

- name: Get Global ID
  run: |
    QUERY="
      query {
        organization(login: \"${{ github.repository_owner }}\") {
          projectV2(number: ${{ inputs.ProjectNumber }}) {
            id
            fields(first: 100) {
              nodes {
                ... on ProjectV2SingleSelectField {
                  id
                  name
                  options {
                    id
                    name
                  }
                }
              }
            }
          }
        }
      }
    "

    gh api graphql -f query="$QUERY" > project_data.json
    echo "PROJECT_ID=$(jq -r '.data.organization.projectV2.id' project_data.json)" >> $GITHUB_ENV

    field_values=$(echo '${{ inputs.FieldKeyValues }}' | jq -c '.[]')
    enc_data=""
    while IFS= read -r key_value; do
      field_name=$(echo "$key_value" | jq -r '.key')
      value_name=$(echo "$key_value" | jq -r '.value')

      field_id=$(jq -r --arg field_name "$field_name" '.data.organization.projectV2.fields.nodes[] | select(.name == $field_name) | .id' project_data.json)
      value_id=$(jq -r --arg field_name "$field_name" --arg option_name "$value_name" '.data.organization.projectV2.fields.nodes[] | select(.name == $field_name).options[] | select(.name == $option_name) | .id' project_data.json)

      enc_data+="$field_id=$value_id,"
    done <<< "$field_values"

    clean_enc_data=$(echo "$enc_data" | sed 's/,$//')
    echo $clean_enc_data
    echo "FIELD_ID_VALUES=$clean_enc_data" >> $GITHUB_ENV
  env:
    GH_TOKEN: ${{ secrets.GH_TOKEN }}

記述量が多いですが、やっていることはGitHubGraphQLへアクセスし各種IDを取得しています。
Query内のorganization(login: \"${{ github.repository_owner }}\")は組織アカウントでの書き方なので、個人の場合はuser(login: \"${{ github.repository_owner }}\")になるかと思います。
fields(first: 100)でフィールドの値を100件取得していますが、設定しているフィールド数が少ないのであればもっと少なくてよいです。今回は今後増えても良いように多めに設定しています。

gh api graphql -f query="$QUERY" > project_data.json
作成したクエリでGitHubCLIを使いGitHubGraphQLへアクセスします。その結果(JSON)をproject_data.jsonというファイルに一時的に保存しておきます。
このファイルはこちらで用意しなくてもよく、GitHubActions内で作成されます。

echo "PROJECT_ID=$(jq -r '.data.organization.projectV2.id' project_data.json)" >> $GITHUB_ENV
先ほど保存したproject_data.jsonからこのプロジェクトのグローバルIDのみを抽出し、他のステップでも使えるようにPROJECT_IDというキーで環境変数に設定します。

field_values=$(echo '${{ inputs.FieldKeyValues }}' | jq -c '.[]')
enc_data=""
while IFS= read -r key_value; do
  field_name=$(echo "$key_value" | jq -r '.key')
  value_name=$(echo "$key_value" | jq -r '.value')

  field_id=$(jq -r --arg field_name "$field_name" '.data.organization.projectV2.fields.nodes[] | select(.name == $field_name) | .id' project_data.json)
  value_id=$(jq -r --arg field_name "$field_name" --arg option_name "$value_name" '.data.organization.projectV2.fields.nodes[] | select(.name == $field_name).options[] | select(.name == $option_name) | .id' project_data.json)

  enc_data+="$field_id=$value_id,"
done <<< "$field_values"

受け取ったFieldKeyValuesから配列の記号を削除し、key:valueでループ文を回します。
このループ文の中で各keyとvalueのグローバルIDを取得します。
たとえば{"key":"Status","value":"Backlog"}であれば、StatusというフィールドのグローバルIDとBacklogというオプションのグローバルIDを取得する形です。
先ほどのproject_data.jsonから必要なもののみ抽出し、enc_datafiled_id=value_idの形式でカンマ区切りで追加していきます。

clean_enc_data=$(echo "$enc_data" | sed 's/,$//')
echo $clean_enc_data
echo "FIELD_ID_VALUES=$clean_enc_data" >> $GITHUB_ENV

先ほどのenc_dataでは末尾にカンマがついてしまうので、末尾のカンマだけ削除します。
そしてその値をFILED_ID_VALUESというキーで環境変数に設定します。

3. Project内におけるissueのItemIDを取得

- name: Get Item ID
  run: |
    QUERY="
      query {
        repository(owner: \"${{ github.repository_owner }}\", name: \"${{ inputs.RepositoryName }}\") {
          issue(number: ${{ inputs.IssueNumber }}) {
            projectItems(first: 1) {
              nodes {
                id
              }
            }
          }
        }
      }
    "

    ITEM_ID=$(gh api graphql -f query="$QUERY" --jq '.data.repository.issue.projectItems.nodes[0].id')
    echo "ITEM_ID=$ITEM_ID" >> $GITHUB_ENV
  env:
    GH_TOKEN: ${{ secrets.GH_TOKEN }}

こちらもGitHubCLIを使ってGitHubGraphQLにアクセスしItemIDを取得しています。
取得した値をITEM_IDとして環境変数に設定します。

4. issueのフィールド値を設定

- name: Set Fields
  run: |
    for field_id_value in $(echo "${{ env.FIELD_ID_VALUES }}" | tr ',' '\n'); do
      field_id=$(echo $field_id_value | cut -d '=' -f1)
      value_id=$(echo $field_id_value | cut -d '=' -f2)

      gh project item-edit --id "${{ env.ITEM_ID }}" --field-id "$field_id" --project-id "${{ env.PROJECT_ID }}" --single-select-option-id "$value_id"
    done
  env:
    GH_TOKEN: ${{ secrets.GH_TOKEN }}

2で設定したFIELD_ID_VALUESをカンマ区切りで分割しループ文を回します。
カンマで区切ったらキーバリューが=で分けられているので、=で分割し1つ目をフィールドのグローバルID、2つ目をオプションのグローバルIDとして定義します。
gh project item-edit --id "${{ env.ITEM_ID }}" --field-id "$field_id" --project-id "${{ env.PROJECT_ID }}" --single-select-option-id "$value_id"でissueのフィールド値の設定をしています。
https://cli.github.com/manual/gh_project_item-edit

以上でissue作成時のProjectへの紐づけと複数のシングルセレクトの設定が終わりです!
続いて、この再利用可能なワークフローを呼び出す方法です。

呼び出す側のワークフロー

ワークフローの前に…PATの取得と設定をします。
作成方法は下記をご覧ください。
https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#personal-access-token-classic-の作成

設定は、個人の場合は各リポジトリに「GH_TOKEN」として設定します。
組織の場合は、組織全体に環境変数を設定できるので、そこで「GH_TOKEN」として設定します。

本来なら上記のワークフローを各リポジトリに書かなければなりませんが、再利用可能なワークフローなのでとっても短くなります。それがこちら!

name: Issue Assign Project
on:
  issues:
    types:
      - opened
jobs:
  assign_projects:
    uses: 組織(個人)名/リポジトリ名/.github/workflows/ファイル名.yml@main
    with:
      ProjectNumber: Projectの番号
      HTML_URL: ${{ github.event.issue.html_url }}
      RepositoryName: リポジトリ名
      FieldKeyValues: '[{ "key": "Status", "value": "Backlog" }, { "key": "Product", "value": "ProductX" }]'
      IssueNumber: ${{ github.event.issue.number }}
    secrets:
      GH_TOKEN: ${{ secrets.GH_TOKEN }}

こちらを実際にワークフローを発火させたいリポジトリに用意します。
これでissue作成時にProjectへ自動でアサインしつつ色々設定してくれます。YATTANE!!

PRを作成したらProjectに紐づける再利用可能なワークフロー

こちらは概ねissue作成時と実装内容は同じです。
違うのはクエリ発行時にissueを取得していたところをPRに置き換えるだけです。
解説はissueと同じなので割愛します。実装内容はこちら。

name: PullRequest assignment to Project
on:
  workflow_call:
    inputs:
      ProjectNumber:
        description: "紐づけたいProjectの番号"
        required: true
        type: number
      HTML_URL:
        description: "PRのHTML URL"
        required: true
        type: string
      FieldKeyValues:
        description: "設定したいSelect FieldのKeyとValueの組み合わせ"
        required: true
        type: string
      RepositoryName:
        description: "リポジトリ名"
        required: true
        type: string
      PRNumber:
        description: "紐づけたいPRの番号"
        required: true
        type: number
    secrets:
      GH_TOKEN:
        description: "GitHub Token"
        required: true

jobs:
  assign_to_project:
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: bash
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Add Item Project
        run: gh project item-add ${{ inputs.ProjectNumber }} --owner ${{ github.repository_owner }} --url ${{ inputs.HTML_URL }}
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}

      - name: Get Global ID
        run: |
          QUERY="
            query {
              organization(login: \"${{ github.repository_owner }}\") {
                projectV2(number: ${{ inputs.ProjectNumber }}) {
                  id
                  fields(first: 100) {
                    nodes {
                      ... on ProjectV2SingleSelectField {
                        id
                        name
                        options {
                          id
                          name
                        }
                      }
                    }
                  }
                }
              }
            }
          "

          gh api graphql -f query="$QUERY" > project_data.json
          echo "PROJECT_ID=$(jq -r '.data.organization.projectV2.id' project_data.json)" >> $GITHUB_ENV

          field_values=$(echo '${{ inputs.FieldKeyValues }}' | jq -c '.[]')
          enc_data=""
          while IFS= read -r key_value; do
            field_name=$(echo "$key_value" | jq -r '.key')
            value_name=$(echo "$key_value" | jq -r '.value')

            field_id=$(jq -r --arg field_name "$field_name" '.data.organization.projectV2.fields.nodes[] | select(.name == $field_name) | .id' project_data.json)
            value_id=$(jq -r --arg field_name "$field_name" --arg option_name "$value_name" '.data.organization.projectV2.fields.nodes[] | select(.name == $field_name).options[] | select(.name == $option_name) | .id' project_data.json)

            enc_data+="$field_id=$value_id,"
          done <<< "$field_values"

          clean_enc_data=$(echo "$enc_data" | sed 's/,$//')
          echo $clean_enc_data
          echo "FIELD_ID_VALUES=$clean_enc_data" >> $GITHUB_ENV
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}

      - name: Get Item ID
        run: |
          QUERY="
            query {
              repository(owner: \"${{ github.repository_owner }}\", name: \"${{ inputs.RepositoryName }}\") {
                pullRequest(number: ${{ inputs.PRNumber }}) {
                  projectItems(first: 1) {
                    nodes {
                      id
                    }
                  }
                }
              }
            }
          "

          ITEM_ID=$(gh api graphql -f query="$QUERY" --jq '.data.repository.pullRequest.projectItems.nodes[0].id')
          echo "ITEM_ID=$ITEM_ID" >> $GITHUB_ENV
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}

      - name: Set Fields
        run: |
          for field_id_value in $(echo "${{ env.FIELD_ID_VALUES }}" | tr ',' '\n'); do
            field_id=$(echo $field_id_value | cut -d '=' -f1)
            value_id=$(echo $field_id_value | cut -d '=' -f2)

            gh project item-edit --id "${{ env.ITEM_ID }}" --field-id "$field_id" --project-id "${{ env.PROJECT_ID }}" --single-select-option-id "$value_id"
          done
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}

呼び出す側

呼び出す側もissueと概ね同じなので解説は割愛します。

name: PR Assign Project
on:
  pull_request:
    types:
      - opened
jobs:
  assign_projects:
    uses: 組織(個人)名/リポジトリ名/.github/workflows/ファイル名.yml@main
    with:
      ProjectNumber: 紐づけたいProjectの番号
      HTML_URL: ${{ github.event.pull_request.html_url }}
      RepositoryName: リポジトリ名
      FieldKeyValues: '[{ "key": "Status", "value": "In Progress" }, { "key": "Product", "value": "ProductY" }]'
      PRNumber: ${{ github.event.pull_request.number }}
    secrets:
      GH_TOKEN: ${{ secrets.GH_TOKEN }}

レビュワーをアサインしたらStatusを更新

PRについては、作成時はIn ProgressでレビュワーをアサインしたらReviewに更新してほしいという要件があります。
そちらのワークフローはこちらです。

name: Review Request with Status Update
on:
  workflow_call:
    inputs:
      ProjectNumber:
        description: "紐づけたいProjectの番号"
        required: true
        type: number
      RepositoryName:
        description: "リポジトリ名"
        required: true
        type: string
      PRNumber:
        description: "ステータスを変更したいPRの番号"
        required: true
        type: number
    secrets:
      GH_TOKEN:
        description: "GitHub Token"
        required: true

jobs:
  update_pr_status:
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: bash
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Get Global ID
        run: |
          QUERY="
            query {
              organization(login: \"${{ github.repository_owner }}\") {
                projectV2(number: ${{ inputs.ProjectNumber }}) {
                  id
                  fields(first: 100) {
                    nodes {
                      ... on ProjectV2SingleSelectField {
                        id
                        name
                        options {
                          id
                          name
                        }
                      }
                    }
                  }
                }
              }
            }
          "

          gh api graphql -f query="$QUERY" > project_data.json
          echo "PROJECT_ID=$(jq -r '.data.organization.projectV2.id' project_data.json)" >> $GITHUB_ENV

          enc_data=""
          field_name="Status"
          option_name="Review"

          field_id=$(jq -r --arg field_name "$field_name" '.data.organization.projectV2.fields.nodes[] | select(.name == $field_name) | .id' project_data.json)
          option_id=$(jq -r --arg field_name "$field_name" --arg option_name "$option_name" '.data.organization.projectV2.fields.nodes[] | select(.name == $field_name).options[] | select(.name == $option_name) | .id' project_data.json)

          echo "FIELD_ID=$field_id" >> $GITHUB_ENV
          echo "OPTION_ID=$option_id" >> $GITHUB_ENV
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}

      - name: Get Item ID
        run: |
          QUERY="
            query {
              repository(owner: \"${{ github.repository_owner }}\", name: \"${{ inputs.RepositoryName }}\") {
                pullRequest(number: ${{ inputs.PRNumber }}) {
                  projectItems(first: 1) {
                    nodes {
                      id
                    }
                  }
                }
              }
            }
          "

          ITEM_ID=$(gh api graphql -f query="$QUERY" --jq '.data.repository.pullRequest.projectItems.nodes[0].id')
          echo "ITEM_ID=$ITEM_ID" >> $GITHUB_ENV
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}

      - name: Set Fields
        run: |
          gh project item-edit --id "${{ env.ITEM_ID }}" --field-id "${{ env.FIELD_ID}}" --project-id "${{ env.PROJECT_ID }}" --single-select-option-id "${{ env.OPTION_ID }}"
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN }}

issueやPR作成時のフィールド設定については、あとから修正・追加・削除ができるような実装をしていましたが、今回はそれをせずStatusとReviewオプションを使うようにしています。
作成時の方も指定方式でいいと思うのですが、このProject管理については最近始めた新しい試みの一つなので、色々変更する可能性を考えて幅を利かせられるようにしています。
呼び出すときは下記のように呼び出します。

name: Review Request with Status Update
on:
  pull_request:
    types:
      - review_requested
jobs:
  assign_projects:
    uses: 組織名/リポジトリ名/.github/workflows/ファイル名.yml@main
    with:
      ProjectNumber: Projectの番号
      RepositoryName: リポジトリ名
      PRNumber: ${{ github.event.pull_request.number }}
    secrets:
      GH_TOKEN: ${{ secrets.GH_TOKEN }}

終わりに

環境構築というほどではないですが開発環境を整える部分に入社まもなく携われたのはとても良い機会でした。
挑戦させて頂き無事終えることが出来たので、エンジニアとして幸先よくスタートダッシュを決められたと思います。
今回作成したワークフローを誰でも使えるようカスタムActionとして用意もしてあるので、近々個人的にマーケットプレイスに載せたいところ。
それでは、ご拝読ありがとうございました!

Discussion