GitHub Actions で Issue の内容に応じてファイル変更・プッシュ・プルリク作成までを自動化
概要
GitHub Actions ドキュメント も是非参照してください。また記事内容にはパブリックプレビューの機能を含みます。
今回、下記のフローを想定して実装していきます。
- 修正したい json ファイルの ID (ファイル名)を指定して固定フォーマットの issue を作成
- issue 作成をトリガーに対象 json ファイルを編集して自動プッシュ・自動プルリク作成
- 対象のプルリクを確認して問題なければプルリクマージ
- マージによって対象ブランチ削除、対象 issue クローズ
最終的なファイル構成は下記のとおりです。
.
├── .github
│ ├── ISSUE_TEMPLATE
│ │ └── update_data.yml
│ └── workflows
│ └── auto-create-pr.yml
└── json
└── 001.json (更新対象ファイル)
001.json
の内容は下記を想定しています。
{
"properties": {
"id": "001",
"name": "元の名前"
},
}
issue を固定フォーマットにする
GitHub のリポジトリページから issue を登録する際に、ISSUE_TEMPLATE を作ることで登録フォームに独自の項目を作って issue を固定のフォーマットにすることができます。
※現在この機能はパブリックプレビューということなので、この後に書いた issue の内容からブランチ名を設定する方法は無効になるかもしれません
.github/ISSUE_TEMPLATE
というディレクトリを作成し、その下に定義ファイルを作成します。
ファイル名はなんでもいいですが、今回は update_data.yml
とします。
name: "JSON ファイル更新"
description: "指定した ID の JSON ファイルを更新"
title: "[update] JSON ID: "
labels: ["data update"]
body:
- type: input
id: json_id
attributes:
label: "JSON_ID"
description: "修正するJSONのIDを入力してください"
placeholder: "例: 001"
validations:
required: true
- type: input
id: new_name
attributes:
label: "NAME"
description: "修正後のNAMEを入力してください"
validations:
required: true
- type: textarea
id: reason
attributes:
label: "REASON"
description: "変更理由"
validations:
required: false
こちらを作成した後、上部メニューの Issues から New issue ボタンをクリックすると、下記の様に設定したフォーマットで issue を作ることができます。

workflow の作成
ワークフローの定義は .github/workflows
ディレクトリ配下に yaml ファイルで設定します。
今回は auto-create-pr.yml
として作成します。
まずは全体を載せて詳しくは後述します。
注意点として、下記は issue の内容を検査していないので、コマンドインジェクションなどの脆弱性があるかもしれません。
実際には厳密な検査やバリデが必要です。
参考:
- Keeping your GitHub Actions and workflows secure Part 2: Untrusted input | GitHub Security Lab
- GitHub Actions のセキュリティ強化 - GitHub Docs
name: Auto Create Pull Request
on:
issues:
types: [opened]
jobs:
create-pr:
runs-on: ubuntu-latest
if: contains(github.event.issue.title, '[update]')
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Extract JSON ID
id: extract_id
run: |
ISSUE_BODY="${{ github.event.issue.body }}"
# issue の内容を取得
JSON_ID=$(echo "$ISSUE_BODY" | awk '/### JSON_ID/ { getline; getline; print $0; }')
NAME=$(echo "$ISSUE_BODY" | awk '/### NAME/ { getline; getline; print $0; }')
# TIMESTAMP を取得
TIMESTAMP=$(date +%Y%m%d%H%M%S)
# 環境変数として登録
echo "JSON_ID=$JSON_ID" >> $GITHUB_ENV
echo "NAME=$NAME" >> $GITHUB_ENV
echo "BRANCH_NAME=update/$JSON_ID/$TIMESTAMP" >> $GITHUB_ENV
- name: Modify json
run: |
jq --arg id "$JSON_ID" \
--arg name "$NAME" \
'
if .properties.id == $id then
.properties.name = $name
else .
end
' "json/$JSON_ID.json" > temp.json && mv temp.json "json/$JSON_ID.json"
- name: Commit & Push Changes
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions@users.noreply.github.com"
git checkout -b "${{ env.BRANCH_NAME }}"
git add "json/$JSON_ID.json"
git commit -m "Update json data for $JSON_ID"
git push origin "${{ env.BRANCH_NAME }}"
- name: Create Pull Request using GitHub CLI
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr create --base main --head "${{ env.BRANCH_NAME }}" \
--title "Update json data for ${{ env.JSON_ID }}" \
--body "Closes #${{ github.event.issue.number }}"
issue の内容を参照してブランチ名を取得する
まずは、issue 作成のトリガーにより、issue の内容を取得してブランチ名を決定します。
name: Auto Create Pull Request
on:
issues:
types: [opened]
jobs:
create-pr:
runs-on: ubuntu-latest
if: contains(github.event.issue.title, '[update]')
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Extract JSON ID
id: extract_id
run: |
ISSUE_BODY="${{ github.event.issue.body }}"
# issue の内容を取得
JSON_ID=$(echo "$ISSUE_BODY" | awk '/### JSON_ID/ { getline; getline; print $0; }')
NAME=$(echo "$ISSUE_BODY" | awk '/### NAME/ { getline; getline; print $0; }')
# TIMESTAMP を取得
TIMESTAMP=$(date +%Y%m%d%H%M%S)
# 環境変数として登録
echo "JSON_ID=$JSON_ID" >> $GITHUB_ENV
echo "NAME=$NAME" >> $GITHUB_ENV
echo "BRANCH_NAME=update/$JSON_ID/$TIMESTAMP" >> $GITHUB_ENV
ワークフローのトリガー にある通り、on.issues.types
に [opened]
を指定することで issue の作成をトリガーすることができます。
if: contains(github.event.issue.title, '[update]')
によって、issue のタイトルに [update]
が含まれていれば後続の処理を実行します。これは.github/ISSUE_TEMPLATE/update_data.yml
の title
にあたる部分です。
「# issue の内容を取得」のところで、awk コマンドを使って見出し ### JSON_ID
の次の次の行にあるデータを取得しています。固定フォーマットの場合、issue の中身は
### JSON_ID
001
### NAME
新しい名前
の様に記述されるためです。
その後、後続のステップで使えるよう、環境変数に登録しています。
JSON ファイルを更新する
jq コマンドを使って JSON ファイルを更新します。
- name: Modify json
run: |
jq --arg id "$JSON_ID" \
--arg name "$NAME" \
'
if .properties.id == $id then
.properties.name = $name
else .
end
' "json/$JSON_ID.json" > temp.json && mv temp.json "json/$JSON_ID.json"
取得済みの JSON_ID
変数を使って該当ファイルを探し、ファイル内の id とも一致した場合に properties.name
を NAME
変数で更新しています。
GitHub Actions と関係ないので詳しい説明は省きます。
コミットとプルリク作成
コミットは git コマンドで、プルリクは gh コマンドで実行しています。
- name: Commit & Push Changes
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions@users.noreply.github.com"
git checkout -b "${{ env.BRANCH_NAME }}"
git add "json/$JSON_ID.json"
git commit -m "Update json data for $JSON_ID"
git push origin "${{ env.BRANCH_NAME }}"
- name: Create Pull Request using GitHub CLI
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr create --base main --head "${{ env.BRANCH_NAME }}" \
--title "Update json data for ${{ env.JSON_ID }}" \
--body "Closes #${{ github.event.issue.number }}"
git コマンドでは特に特別なことはしておらず、環境変数をもとにプッシュまでを実行しています。
git のユーザー名を github-actions[bot]
としていますが、これは GitHub Actions が行うコミットを識別しやすくするための慣習だと思います。必要に応じて他の名前に変更することも可能です。
gh コマンドを使用するには GH_TOKEN という環境変数に必要なスコープを持つトークンを設定する必要があります。今回は自動的に提供される secrets.GITHUB_TOKEN
を使用しています。
--body に "Closes #${{ github.event.issue.number }}"
を設定していますが、こうすることでプルリクがマージされたときに、対象の issue も併せて閉じることができます。
補足 - プルリクのワークフローを分ける
今回はプッシュ・プルリク作成を一つの workflow にまとめていますが、push トリガーを使ってプルリクを別ワークフローに分けることもできます。
※トリガーではなく直接後続のワークフローを指定するには workflow_dispatch
と repository_dispatch
を使います
ただ、プルリク作成側の workflow ではそのまま issue 番号を取得できないため、プルリクマージ後に issue を削除したい場合、プッシュするブランチ名やコミットメッセージに issue 番号を含めるなどの調整が必要になるかと思います。
また、トリガーによる別ワークフローの実行は無限ループを防止するために、デフォルトの権限ではトリガーが機能しません。
例えば、先行 workflow のプッシュによって後続の push トリガーを機能させるには、先行 workflow で GITHUB_TOKEN の代わりに、GitHub App インストール アクセス トークンまたは personal access token を使う必要があります。
PAT (Fine-grained personal access token) を使う場合、GitHub のページで作成し後続 workflow の実行に最低限必要な Contents, Workflows の Read and write 権限を付与したものを用意し、下記の様に with.token
を設定することで後続の workflow のトリガーが機能します。
※「Fine-grained personal access token」は現在パブリックプレビューです ※「personal access tokens (classic)」を使う場合、repo, workflow の権限を付与します
jobs:
update-json:
runs-on: ubuntu-latest
if: contains(github.event.issue.title, '[update]')
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
token: ${{ secrets.PAT_TOKEN }}