notebook

都内でWEB系エンジニアやってます。

Cloudflare Pagesのデプロイ時に前回デプロイから特定のディレクトリに差分があったか確認し処理を分ける

本記事はCloudflareのカレンダー 16日目の記事です

Gatsby製のブログをNetlifyからCloudflarePagesに移行したが考慮不足でしばらく気付かず問題があったのでメモ

直前のデプロイ時から、特定ディレクトリ以下に差分があるかチェックし、処理を分ける

前提

  • Gatsby製のブログ
  • 記事の全文検索にAlgoliaを使っている
  • gatsby-plugin-algoliaを使ってインデックスの更新を行っている

ビルド時に前回デプロイからの差分を確認し、記事ファイルにのみ差分がある場合はAlgoliaのインデックスを更新する

常に更新にすると、RenovateなどのPRでインデックスの更新が走ってしまい以前のAlgoliaの無料枠で収まらなくなってしまうため(だったはず)

上記前提の元、Cloudflareへ移行後からAlgoliaのIndex更新が途絶えていた

移行時の記事

Netlifyの場合

Netlifyではビルド時の環境変数に$CACHED_COMMIT_REF,$COMMIT_REFが用意されている

Build environment variables | Netlify Docs

docs.netlify.com

  • CACHED_COMMIT_REF
    • 現在のビルドの直前のビルドで使用したコミットハッシュ
  • COMMIT_REF
    • 現在のビルドで使用するコミットハッシュ

この2つを用いて前回のデプロイ時のコミットから記事のディレクトリ以下に差分があった場合はAlgoliaのインデックスを更新する処理を走らせる

ということをしていた

Netlifyでしか扱えない環境変数を前提としてデプロイスクリプトを書いていたため、CloudFlareに移行後は当然正常に動かなくなっていた

Cloudflare Pagesの場合

Build configuration · Cloudflare Pages docs

developers.cloudflare.com

環境変数を見ると現在のコミットREFしかなさそう

前回デプロイビルド時のCOMMIT SHAを何かしらの方法で取得する必要がある

CloudflareのAPIを使って直前のデプロイ時のコミットハッシュを取得する

デプロイに関するAPIが用意されているのでそれを使って直前のデプロイ時のコミットハッシュを取得できる

Cloudflare API Documentation

developers.cloudflare.com

CloudflareのアカウントIDはWorkers & PagesのOverviewで右側メニューのAccount IDというところで確認できる

Pagesのデプロイリストを取得

事前に下記を環境変数に設定しておく

  • CF_ACCOUNT_ID: アカウントID
  • CF_API_KEY: CloudflareのAPIキー
  • CF_PROJECT: Pagesのプロジェクト名
curl -X GET "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/${CF_PROJECT}/deployments" \
-H "Authorization: Bearer ${CF_API_KEY}" \
-H "Content-Type:application/json"

下記はドキュメントのExampleから持ってきたレスポンスのサンプル

{
  "errors": [],
  "messages": [],
  "result": [
    {
      "aliases": [
        "https://branchname.projectname.pages.dev"
      ],
      "build_config": null,
      "created_on": "2021-03-09T00:55:03.923456Z",
      "deployment_trigger": {
        "metadata": {
          "branch": "main",
          "commit_hash": "ad9ccd918a81025731e10e40267e11273a263421",
          "commit_message": "Update index.html"
        },
        "type": "ad_hoc"
      },
      "env_vars": {
        "BUILD_VERSION": {
          "value": "3.3"
        },
        "ENV": {
          "value": "STAGING"
        }
      },
      "environment": "preview",
      "id": "f64788e9-fccd-4d4a-a28a-cb84f88f6",
      "is_skipped": true,
      "latest_stage": null,
      "modified_on": "2021-03-09T00:58:59.045655",
      "project_id": "7b162ea7-7367-4d67-bcde-1160995d5",
      "project_name": "ninjakittens",
      "short_id": "f64788e9",
      "source": null,
      "stages": [
        {
          "ended_on": "2021-06-03T15:39:03.134378Z",
          "name": "queued",
          "started_on": "2021-06-03T15:38:15.608194Z",
          "status": "active"
        },
        {
          "ended_on": null,
          "name": "initialize",
          "started_on": null,
          "status": "idle"
        },
        {
          "ended_on": null,
          "name": "clone_repo",
          "started_on": null,
          "status": "idle"
        },
        {
          "ended_on": null,
          "name": "build",
          "started_on": null,
          "status": "idle"
        },
        {
          "ended_on": null,
          "name": "deploy",
          "started_on": null,
          "status": "idle"
        }
      ],
      "url": "https://f64788e9.ninjakittens.pages.dev"
    }
  ],
  "success": true,
  "result_info": {
    "count": 1,
    "page": 1,
    "per_page": 100,
    "total_count": 1
  }
}

deployment_trigger.metadataにコミットハッシュやブランチの情報が入っている

また、日時で降順になっている、実際に試してみたら現在実行中のデプロイの情報もこのリストに含まれるようだった

デプロイスクリプトでAPIの情報を利用する場合、1つ目は今実行中のデプロイということになるので次のデプロイを指定する必要がある

あとは対象ブランチをdefaultブランチ(productionビルドを行うブランチ)でのデプロイに絞ってあげればOK

ということでちょっと微妙だが

jq '.result[]|select(.deployment_trigger.metadata.branch=="master")' | jq -scr '.[1]|.deployment_trigger.metadata.commit_hash'
  • 0: 現在のデプロイ
  • 1: 1つ前のproductionデプロイ

という感じで直前のデプロイのコミットハッシュを取得できる

デプロイ時の環境

見直したらもう少し改善余地がありそうだが次のようなデプロイスクリプトとなった

  • deploy.sh
#!/bin/bash

# install jq
curl -o $HOME/.local/bin/jq -L https://github.com/jqlang/jq/releases/download/jq-1.7/jq-linux64 && chmod +x $HOME/.local/bin/jq

git fetch --depth 100

if [ "$BUILD" = "production" ]; then
  res=$(curl -X GET "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/pages/projects/til/deployments" \
    -H "Authorization: Bearer ${CF_API_KEY}" \
    -H "Content-Type:application/json")
  LATEST_DEPLOY_COMMIT_SHA=$(echo ${res} | jq '.result[]|select(.deployment_trigger.metadata.branch=="master")' | jq -scr '.[1]|.deployment_trigger.metadata.commit_hash')

  # 差分があると終了コード1
  git diff --quiet $LATEST_DEPLOY_COMMIT_SHA $CF_PAGES_COMMIT_SHA content/blog/entries/

  rc=$?

  if [ "$rc" = "1" ]; then
    echo "content changed."
    CONTENT_CHANGED=true yarn build
  else
    echo "content not changed."
    CONTENT_CHANGED=false yarn build
  fi
else
  echo "this build is preview build."
  CONTENT_CHANGED=false yarn build
fi

これをデプロイ時に実行するようにCloudflare Pagesで設定しておく

CONTENT_CHANGEDの値によってAlgoliaのプラグイン側で、インデックスの更新可否を判断している

以下デプロイ環境で必要だったので処理を追加している

  • Build V2環境を利用しているためjqが入っていなかったのでインストール
  • デプロイ時のチェックアウトは--depth 0でソースコードを取得しているようだったので、デプロイスクリプトでgit fetch --fetch 100を追加
    • さすがに100あればコミットハッシュを取得できるだろうという理由

これで無事直前のデプロイと差分があるか確認できるようになり、差分がある場合はAlgoliaのインデックスを更新するというような分岐ができるようになった

まとめ

  • CloudFlare Pagesのビルドプロセス上でAPIをたたいてデプロイ情報を取得した
  • デプロイ情報からコミットハッシュを取得して現在のデプロイのコミットハッシュと比較して特定ディレクトリ以下に差分があるか判断できるようにした
  • 差分がある場合はAlgoliaのインデックス更新処理を走らせるようにした

NetlifyでやっていたことをCloudfFlare Pagesのデプロイプロセスでも実現できるようにした

CloudflareのAPIキーの発行は権限の設定を細かく設定できてある程度リスク管理できるよう

勉強にはなりました