スプレッドシートの内容を GitHub のリポジトリに自動的に同期する仕組みを作った
概要
ひょんなことから2,3ヶ月に渡る期間開催されるスプラトゥーン3の大会のポータルサイトの開発をすることになりました。
チーム情報や大会の対戦情報はスプレッドシートで管理されており、その内容をポータルサイトに反映する仕組みを作ったので紹介します。
具体的にはこのような仕組みを作りました。
雑に説明するとスプレッドシートを CMS として扱い、CMS とリポジトリの同期を GitHub Actions で実現しました。
仕組み
スプレッドシートの差分検知
スプレッドシートの内容を JSON ファイルに変換し、その JSON ファイルをコミットする方針にしました。これにより git diff
の結果があれば差分があると検知することができます。
スプレッドシートの内容を JSON に変換
まずはスプレッドシートの内容を JSON に変換することを考えます。
例えば、以下のようなデータがあったときに
チーム名 | メンバー1 | メンバー2 | メンバー3 | メンバー4 |
---|---|---|---|---|
チームA | Aさん | Bさん | Cさん | Dさん |
次のような形式の JSON に変換しています。
[{ "teamName": "チームA", "players": ["Aさん", "Bさん", "Cさん", "Dさん"] }]
スプレッドシートの内容を Node.js で取得
スプレッドシートの内容を Node.js で取得するために @googleapis/sheets を使用しました。
認証情報は Application Default Credentials (以下 ADC) が設定されていることを前提にしました。事前に認証情報に紐付いているメールアドレスに対して、スプレッドシートを閲覧できる権限を与えておく必要があります。
これにより以下のコードでスプレッドシートの内容を取得することができます。
const sheetId = "";
const auth = new GoogleAuth({
scopes: [
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/spreadsheets.readonly",
],
});
const sheets = getSheets({
version: "v4",
auth,
});
const res = await sheets.spreadsheets.values.get({
spreadsheetId: sheetId,
range: "チーム!A1:B",
});
console.log(res.data.value);
差分検知の仕組みを GitHub Actions で動かす
差分検知は定期実行されてほしいので、GitHub Actions の schedule イベントの cron を使用しました。
Actions での認証情報は、元々あったポータルサイトを Firebase Hosting に自動デプロイする仕組みで使用しているサービスアカウントを利用しました。
シークレットに保存されているサービスアカウントを利用するには google-github-actions/auth が便利です。
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v1
with:
credentials_json: "${{ secrets.FIREBASE_SERVICE_ACCOUNT_ROOKIE_LEAGUE_PORTAL }}"
認証情報を環境変数に設定してくれるため ADC を前提にしているプログラムならコードを書き換えずに動作してくれます。
検知した差分をコミットするプルリクを作成
ここまでで、GitHub Actions 上でスプレッドシートとの差分を検知して JSON に変換し、差分がある場合は git diff
が発生するところまで完成しました。
次にこの差分を持つプルリクを作成する仕組みを作ります。
GitHub Actions でプルリクを作成する場合は peter-evans/create-pull-request が便利です。
差分があるときのみにプルリクを作成、すでにプルリクが存在する場合は force-push で上書きなどの機能を持ちます。
GitHub Actions の仕様で、通常の GitHub Actions で作成したプルリクでは pull_request イベントが発火しません。これは無限ループの発生により無限に GitHub Actions が呼び出されることを防止するためです。この仕様は peter-evans/create-pull-request で作成されるプルリクであっても適用されます。
今回のポータルサイトではプルリクが作成されるたびに、プレビュー環境が作成され動作確認ができる仕組みが用意されていました。そのためスプレッドシートの差分をプレビュー環境で確認するにはもうひと手間必要でした。
この問題の対応策は peter-evans/create-pull-request のドキュメント で言及されています。色々方針がありますが今回は Authenticating with GitHub App generated tokens
を選択しました。
全体像
最後に GitHub Actions の設定ファイルを紹介して終わります。
name: Synchronize Sheets
on:
workflow_dispatch:
schedule:
- cron: "0 * * * *"
env:
TZ: "Asia/Tokyo"
jobs:
sync-sheets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18.12.1
cache: "npm"
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v1
with:
credentials_json: "${{ secrets.FIREBASE_SERVICE_ACCOUNT_ROOKIE_LEAGUE_PORTAL }}"
- uses: tibdex/github-app-token@v1
id: generate-token
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- run: npm ci
- run: npm run sync-sheets
working-directory: packages/sync-sheets
- run: git status
- name: Create Pull Request
uses: peter-evans/create-pull-request@v4
with:
add-paths: |
packages/app/src/data
branch: sync-sheets
title: Sync sheets
body: |
マスターシートのデータを json ファイルに同期するプルリクです
diff とプレビュー環境で意図した変更か確認して良さそうならマージお願いします
commit-message: Sync sheets
labels: |
sync-sheets
reviewers: |
odanado
delete-branch: true
token: ${{ steps.generate-token.outputs.token }}
Discussion