GeekFactory

int128.hatenablog.com

GitHub Actions で Issue を Project に追加する

カンバンをうまく運用するには自動化の仕組みが不可欠です。Issue や Pull Request を作ったのにカンバンに入れていなくて忘れ去っていたということ、ありますよね。本稿では GitHub に Issue や Pull Request が作成された契機で Project に自動的に追加する仕組みを考えます。

ここでは Organization で Project を使っている場合を想定します。

GraphQL Mutation で Issue を Project に追加する

まずは API で Project を操作する方法を説明します。

Issue や Pull Request を Project に追加するには addProjectCard mutation が用意されています。これを使うと Project ボードの指定したカラムに追加できます。

さっそく GraphQL Explorer で試してみましょう。

まずは適当な Issue と Project を選び、以下のクエリで ID を取得します。数字は Issue number と Project number に対応します。

query { 
  repository(owner: "ORG", name: "REPO") {
    issue(number: 27193) {
      id
    }
  }
  organization(login: "ORG") {
    project(number: 26) {
      columns(first: 1) {
        nodes {
          name
          id
        }
      }
    }
  }
}

以下のような ID が返ってくるはずです。

{
  "data": {
    "repository": {
      "issue": {
        "id": "MDSOMEISSUEID"
      }
    },
    "organization": {
      "project": {
        "columns": {
          "nodes": [
            {
              "name": "To Do",
              "id": "MDSOMEPROJECTCOLUMNID"
            }
          ]
        }
      }
    }
  }
}

先ほど取得した ID を引数にして以下の mutation を実行します。

mutation($projectColumnId: ID!, $issueID: ID!) {
  addProjectCard(input: {projectColumnId: $projectColumnId, contentId: $issueID}) {
    projectColumn {
      name
    }
  }
}
{
  "projectColumnId": "MDSOMEPROJECTCOLUMNID",
  "issueID": "MDSOMEISSUEID"
}

成功した場合は Project のカラム名が返ってくるはずです。すでに Project に Issue が追加済みの場合は以下のエラーが返されます。

{
  "data": {
    "addProjectCard": null
  },
  "errors": [
    {
      "type": "UNPROCESSABLE",
      "path": [
        "addProjectCard"
      ],
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "message": "Project already has the associated issue"
    }
  ]
}

GitHub CLI で GraphQL mutation を実行する

GitHub CLI には gh api コマンドが用意されており、REST や GraphQL のリクエストを直接実行できます。先ほどの mutation は以下のコマンドで実行できます。

gh api graphql -F 'projectColumnId=MDSOMEPROJECTCOLUMNID' -F 'issueID=MDSOMEISSUEID' -f query='
  mutation($projectColumnId: ID!, $issueID: ID!) {
    addProjectCard(input: {projectColumnId: $projectColumnId, contentId: $issueID}) {
      projectColumn {
        name
      }
    }
  }'

GraphQL Explorer で試した内容をそのままコマンドで実行できるのでめっちゃ便利です。

GitHub Actions で Issue を Project に追加する

GitHub Actions の issues event type を利用すると、Issue が変更された契機でワークフローを実行できます。さらに、ワークフローの if で発火契機を細かく指定できます。

例として、Issue に特定のラベルが付いている場合に自動的に Project に追加するワークフローを書いてみましょう。

on:
  issues:
    types:
      # Issue の新規作成や変更、ラベル追加を契機にする(ラベル削除は別に考える必要あり)
      - opened
      - edited
      - labeled

env:
  GITHUB_TOKEN: ${{ secrets.YOUR_TOKEN }}
  MUTATION: |
    mutation($projectColumnId: ID!, $issueID: ID!) {
      addProjectCard(input: {projectColumnId: $projectColumnId, contentId: $issueID}) {
        projectColumn {
          name
        }
      }
    }

jobs:
  my-kanban:
    if: contains(github.event.issue.labels.*.name, 'Kubernetes') # 特定のラベルが付いている場合のみ
    runs-on: ubuntu-latest
    env:
      PROJECT_COLUMN_ID: MDSOMEPROJECTCOLUMNID
    steps:
      - run: gh api graphql -F "projectColumnId=$PROJECT_COLUMN_ID" -F 'issueID=${{ github.event.issue.node_id }}' -f query="$MUTATION"

このワークフローを配置して、Issue にラベルを付けてみましょう。以下の動作になるはずです。

  • Issue に Kubernetes ラベルが付いている場合: ジョブが実行されて Project に Issue が追加される
  • Issue に Kubernetes ラベルが付いていない場合: ワークフローは実行されるがジョブは実行されない

GitHub Actions の課金体系を見る限り、ジョブが実行された場合のみ課金されるようですが、実際は未確認です。

まとめ

  • GraphQL のクエリをそのまま gh api コマンドに貼り付けて実行できるのが便利
  • GitHub Actions の event trigger と条件分岐で細かく制御できる