删除 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 这个仓库已经坏了,因为 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()