似たような話が先日ありましたね。
こちらはGraphQLではなくてOpenAPI、かつサーバーサイド側のリポジトリからクライアントサイド側のリポジトリにプッシュする方式でした。
今関わっているプロダクトでは、1つのGraphQLスキーマが複数のリポジトリから必要とされていたり、それぞれで同期タイミングを制御できたらいいかなということもあったりで、クライアントサイドからGitHub Actionsの定期実行でプルする方式で更新をしています。
イメージは次のとおり:
name: Update GraphQL Schema
on:
schedule:
- cron: "0 0 * * 1-5"
workflow_dispatch:
jobs:
UpdateGraphQLSchema:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: make update-graphql-schema
- uses: peter-evans/create-pull-request@v4
with:
commit-message: Update schema.graphqls
title: Update schema.graphqls
body: ""
branch: update-graphql-schema
token: ${{ secrets.GITHUB_PAT }}
トリガーとしては、scheduleでの定期実行 OR workflow_dispatchでの手動実行になっています。
実際の更新部分は例として make update-graphql-schema
としていますが、中身としてはApollo Clientのコマンドを実行してスキーマをダウンロードする(実態としてはIntrospectionを実行してスキーマを得る)ようにしています。ここについては、サーバーサイドのリポジトリをチェックアウトしてきて、元のスキーマファイルをコピーするようにしてきてもよいでしょう。
更新したスキーマをゲットできたら、あとは peter-evans/create-pull-request actionを使ってプルリクエスト(以下、PR)を作成しています。
ここで扱っているのはApollo Clientを使ったネイティブアプリで、Apollo iOSもApollo Kotlinもアプリのビルド時にオンデマンドでコード生成を行うので、スキーマ更新と同時にコード生成をしてPRに含める、ということはありません。
さて、これはこれで単純でいいのですが、スキーマの変更内容がPRで確認できたとしても、その詳細(変更の意図や経緯)がこれだけでは分かりません。ということで、サーバーサイドのリポジトリの関連するPRを拾い上げて、スキーマ更新のPRに変更履歴という体で貼り付けていました。
しかしさすがに人力で関連PRを探しに行くのは面倒なので自動化したい。ということで、チームメンバーであるところの
id:mangano-ito さんがやってくれました。
name: Update GraphQL Schema
on:
schedule:
- cron: "0 0 * * 1-5"
workflow_dispatch:
jobs:
UpdateGraphQLSchema:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- id: generate-associated-pr-list
uses: ./.github/actions/generate-associated-pr-list
with:
our-path: path/to/client/schema.graphqls
their-path: path/to/server/schema.graphql
their-owner: ...
their-repo: ...
github-token: ${{ secrets.GITHUB_PAT }}
- run: make update-graphql-schema
- uses: peter-evans/create-pull-request@v4
with:
commit-message: Update schema.graphqls
title: Update schema.graphqls
body: |
${{ steps.generate-associated-pr-list.outputs.result }}
branch: update-graphql-schema
token: ${{ secrets.GITHUB_PAT }}
generate-associated-pr-list
というのがお手製のComposite Actionです。自分方のリポジトリのファイルと、相手方のリポジトリのファイルの変更日時から、相手方のファイルのその間のコミット・それに紐付くPRを拾い上げて、Markdownのリストとして出力してくれるものです。パラメーター名も、gitの衝突解消時のours, theirsという用語を基にしていてオシャレですね。
その実装の大事な部分だけ抜粋してみます:
runs:
using: composite
steps:
- id: get-last-updated-date
shell: bash
env:
TZ: UTC0
PATH_TO_FILE: ${{ inputs.our-path }}
run: |
value=$(\
git log -1 \
--date="format-local:%Y-%m-%dT%H:%M:%SZ" \
--pretty="format:%cd" \
"${PATH_TO_FILE}" \
)
echo "::set-output name=value::$value"
- id: find-prs
uses: octokit/graphql-action@v2.x
env:
GITHUB_TOKEN: ${{ inputs.github-token }}
with:
query: |
query FindAssociatedPullRequests(
$since: GitTimestamp!,
$path: String!,
$owner: String!,
$repo: String!,
) {
repository(owner: $owner, name: $repo) {
defaultBranchRef {
target {
... on Commit {
history(since: $since, path: $path) {
nodes {
associatedPullRequests(first: 1) {
nodes {
number
url
}
}
}
}
}
}
}
}
}
since: ${{ steps.get-last-updated-date.outputs.value }}
path: ${{ inputs.their-path }}
owner: ${{ inputs.their-owner }}
repo: ${{ inputs.their-repo }}
GitHubのGraphQL API (v4) を上手く使って、特定のファイルの特定日時以降のコミット・それに紐付くPRをリストアップしています。これで人力で関連PRを探しに行かなくてよくなりました。めでたしめでたし。
このようなワークフロー・改善により、自分達のGraphQL APIとこれからも仲良く付き合っていきたい。