刪除 CF Pages 部署

一個處理部署過多的 Python 腳本

當你嘗試刪除一個擁有超過約 100 次部署的 Pages 專案時(每次構建都算作一次部署,例如你更新了網誌中的一篇文章),你可能會遇到以下錯誤:Your project has too many deployments to be deleted, follow this guide to delete them: https://cfl.re/3CXesln

Screenshot

於是我叫大肥魚(Deepseek)幫我寫一個 Python 腳本,自動刪除部署,這樣我之後就可以刪除該專案。cloudflare-bulk-delete 這個 repo 已經壞了,因為 Pages API 每頁查詢的記錄上限是 25 條,而該工具每次發送 100 條記錄的查詢,導致 HTTP 400 錯誤。

python
 1#!/usr/bin/env python3
 2"""Bulk delete all Cloudflare Pages deployments in a project."""
 3
 4import os
 5import sys
 6import requests
 7
 8# Config — set these via environment variables or edit inline
 9ACCOUNT_ID = os.environ.get("CLOUDFLARE_ACCOUNT_ID", "your_account_id")
10TOKEN = os.environ.get("CLOUDFLARE_API_TOKEN", "cfut_kfcCRAZYthursday")
11PROJECT = os.environ.get("CLOUDFLARE_PROJECT", "hugo")
12
13BASE = f"https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/pages/projects/{PROJECT}/deployments"
14HEADERS = {"Authorization": f"Bearer {TOKEN}"}
15
16
17def fetch_all_deployment_ids():
18    """Fetch all deployment IDs across all pages."""
19    ids = []
20    page = 1
21    while True:
22        resp = requests.get(f"{BASE}?page={page}", headers=HEADERS).json()
23        if not resp["success"]:
24            print(f"Error fetching page {page}: {resp['errors']}", file=sys.stderr)
25            sys.exit(1)
26
27        ids.extend(d["id"] for d in resp["result"])
28        total_pages = resp["result_info"]["total_pages"]
29        print(f"  Page {page}/{total_pages} — collected {len(ids)} total")
30
31        if page >= total_pages:
32            break
33        page += 1
34
35    return ids
36
37
38def delete_deployment(dep_id):
39    """Delete a single deployment. Returns (ok, message)."""
40    r = requests.delete(f"{BASE}/{dep_id}", headers=HEADERS)
41    data = r.json()
42    if data.get("success"):
43        return True, "OK"
44    else:
45        err = data.get("errors", [{}])[0].get("message", "unknown error")
46        return False, err
47
48
49def main():
50    print(f"=== Fetching all deployments for project '{PROJECT}' ===")
51    ids = fetch_all_deployment_ids()
52    print(f"\n=== Deleting {len(ids)} deployments ===")
53
54    ok = 0
55    skip = 0
56    for i, dep_id in enumerate(ids, 1):
57        success, msg = delete_deployment(dep_id)
58        if success:
59            ok += 1
60            print(f"  [{i}/{len(ids)}] OK  {dep_id}")
61        else:
62            skip += 1
63            print(f"  [{i}/{len(ids)}] SKIP ({msg})  {dep_id}")
64
65    print(f"\n=== Done! Deleted {ok}, skipped {skip}, total {len(ids)} ===")
66
67
68if __name__ == "__main__":
69    main()