• Cloudflare Pagesのデプロイ履歴が溜まっていく
  • ダッシュボードから1件ずつ消すのが面倒
  • 自動で消したいが、間違って本番を消すのが怖い

Cloudflare Pagesは、pushするたびにデプロイが1件ずつ増えていきます。

プレビュー環境も含めるので、運用しているとすぐ数十件、数百件になります。

これを自動で片付けるNode.jsスクリプトを作りました。

ただし、単純に「作成日時が新しい順に1件だけ残す」という実装にはしていません。

そのやり方だと、条件によっては本番のデプロイを消してしまうことがあるからです。

この記事では、そのスクリプトの中身と、日付だけで判定すると危険な理由を解説します。

目次

デプロイ履歴が溜まる理由

Cloudflare Pagesは、Gitリポジトリと連携すると、pushのたびに新しいデプロイを作ります。

mainブランチへのpushだけでなく、プルリクエストごとのプレビューデプロイも別カウントです。

個人開発でも、ちょっとした修正を何度もpushしていると、履歴はあっという間に積み上がります。

ダッシュボードから消すこともできますが、1件ずつクリックして確認するのは現実的ではありません。

プロジェクトが複数あるとなおさらです。

「最新の日付を残す」だけでは足りない

古いデプロイを消すスクリプトを書くとき、最初に思いつくのは「作成日時でソートして、一番新しい1件だけ残す」というロジックです。

一見するとこれで十分に見えます。

ですが、Cloudflare Pagesではプレビューデプロイが本番デプロイより後に作られることがあります。

たとえば、mainにマージしたあとでプルリクエストのブランチにpushすると、そちらの方が作成日時は新しくなります。

このとき単純な日付ソートで「最新の1件」を残すと、残るのはプレビューデプロイの方です。

本番で実際に配信されているデプロイの方が、削除対象に回ってしまいます。

この事故を避けるために、Cloudflare Pages APIが返すcanonical_deploymentを使いました。

これはプロジェクトごとに「本番として扱われているデプロイ」を指すフィールドです。

作成日時ではなく、このcanonical_deploymentを優先して「残す1件」を決めれば、プレビューの方が新しくても本番側を誤って消すことはありません。

スクリプトの全体像

処理の流れは次の通りです。

  1. .envからCF_API_TOKENCF_ACCOUNT_IDを読み込む
  2. Cloudflare APIから対象プロジェクトの一覧を取得する
  3. プロジェクトごとに、デプロイ履歴をページネーションも含めて全件取得する
  4. canonical_deployment(なければlatest_deployment、それもなければ作成日時が最新のもの)を「残す1件」として判定する
  5. それ以外のデプロイをDELETEリクエストで削除する

--dry-runを付ければ削除は実行せず、対象一覧の表示だけで止まります。

--project=プロジェクト名を付ければ、特定のプロジェクトだけに絞り込めます。

コードの中身

.envの読み込み

function loadEnvFile(envPath) {
  if (!fs.existsSync(envPath)) return;
  const content = fs.readFileSync(envPath, "utf8");
  for (const line of content.split("\n")) {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith("#")) continue;
    const eq = trimmed.indexOf("=");
    if (eq === -1) continue;
    const key = trimmed.slice(0, eq).trim();
    let value = trimmed.slice(eq + 1).trim();
    if (
      (value.startsWith('"') && value.endsWith('"')) ||
      (value.startsWith("'") && value.endsWith("'"))
    ) {
      value = value.slice(1, -1);
    }
    if (!(key in process.env)) process.env[key] = value;
  }
}

外部ライブラリを使わず、.envを自前でパースしています。

KEY=VALUE形式の行を1行ずつ読み、クォートで囲まれていれば剥がしてprocess.envに入れるだけの単純な処理です。

すでに環境変数としてCF_API_TOKENなどが設定済みの場合は上書きしないようにしています。

ページネーション込みのAPI取得

async function listAllPages(path) {
  const results = [];
  let page = 1;
  while (true) {
    const sep = path.includes("?") ? "&" : "?";
    const url = page === 1 ? path : `${path}${sep}page=${page}`;
    const body = await cfFetch(url);
    results.push(...body.result);
    const info = body.result_info;
    if (!info || !info.total_pages || page >= info.total_pages) break;
    page++;
  }
  return results;
}

Cloudflare APIはデフォルトでページ単位のレスポンスを返すので、result_info.total_pagesを見ながら最後のページまで回収しています。

プロジェクト一覧の取得にもデプロイ一覧の取得にも、この共通関数を使い回しています。

残す1件の判定ロジック

const keepId =
  project.canonical_deployment?.id ??
  project.latest_deployment?.id ??
  [...deployments].sort((a, b) => new Date(b.created_on) - new Date(a.created_on))[0].id;

先に触れた通り、ここがこのスクリプトで一番気を使った部分です。

優先順位はcanonical_deploymentlatest_deployment → 作成日時ソートの順です。

canonical_deploymentが取れる限りは、日付に関係なく本番デプロイを残す判定になります。

使い方

まず.envにトークンとアカウントIDを用意します。

CF_API_TOKEN=xxxxxxxx
CF_ACCOUNT_ID=xxxxxxxx

APIトークンには、Cloudflare Pagesの編集権限が必要です。

いきなり本番実行はせず、最初は--dry-runで対象を確認します。

node cleanup-cloudflare-pages-deployments.js --dry-run

表示された削除対象に問題がなければ、--dry-runを外して実行します。

node cleanup-cloudflare-pages-deployments.js

特定のプロジェクトだけ試したい場合は、--projectで絞り込めます。

node cleanup-cloudflare-pages-deployments.js --project=my-site --dry-run

安全に使うための設計ポイント

このスクリプトは、削除という取り消せない操作を扱います。

そのため、いくつか安全側に倒す作りにしています。

  • --dry-runで先に対象を確認できるようにする
  • 日付ではなくcanonical_deploymentで「残す1件」を判定する
  • デプロイが1件以下のプロジェクトはスキップする
  • 削除に失敗しても、そのデプロイだけログを出して処理を続ける

特に最後の「1件失敗しても止めない」は、プロジェクト数が多いときに効きます。

途中で1件エラーが出るたびに全体が止まると、結局手動でやり直す羽目になります。

よくある質問

作成日時でソートして最新を残すだけではダメですか?

プレビューデプロイが本番デプロイより後に作られることがあるため、単純な日付ソートだと本番デプロイの方を削除対象にしてしまうことがあります。

Cloudflare Pages APIが返す`canonical_deployment`を使えば、日付に関係なく本番側を残せます。

誤って必要なデプロイを消してしまうことはありませんか?

`--dry-run`オプションを付ければ、削除は実行せず対象一覧だけを表示できます。

本番実行の前に、必ず`--dry-run`で対象を確認してから消すようにしています。

複数のCloudflare Pagesプロジェクトをまとめて掃除できますか?

オプションなしで実行すると、アカウント内の全プロジェクトが対象になります。

`--project=プロジェクト名`を付ければ、特定の1プロジェクトだけに絞り込めます。

まとめ:日付より「本番かどうか」で判定する

Cloudflare Pagesのデプロイ履歴は、運用しているだけで自然に溜まっていきます。

自動で片付けるスクリプト自体はそれほど複雑ではありませんが、「残す1件」の決め方だけは注意が必要です。

作成日時ではなくcanonical_deploymentで本番デプロイを判定すれば、プレビューの方が新しく見える状況でも誤削除を避けられます。

削除のような取り消せない処理を書くときは、まず--dry-runで対象を確認できる作りにしておくと、あとで安心して使えます。

まずは--dry-runで自分のプロジェクトの削除対象を確認してみてください。

Cloudflare Pagesのビルド回数やファイル数の上限が気になる場合は、こちらの記事にまとめています。

関連記事Cloudflare Pagesの無料枠で個人ブログは運用できる?制限と注意点を解説Cloudflare Pagesの無料枠で個人ブログをどこまで運用できるのか。月500回のビルド、静的リクエスト無制限、帯域幅無制限、Functions利用時の注意点です。