pr_01m47d24b0e6n91zwymwxg0vpx/scripts/cloudflare-setup.py
Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Deploy scripts live in the repository | 1 | """Makes what Deployments needs that `wrangler login` cannot: a proxied |
| 2 | wildcard DNS record on the apps' zone, and an API token for the deployments | |
| 3 | service (Workers Scripts: Edit, Account Analytics: Read). | |
| 4 | ||
| 5 | Run by scripts/setup-deployments.sh with CLOUDFLARE_EMAIL and | |
| 6 | CLOUDFLARE_API_KEY (a Global API Key) set. Prints only ids and outcomes, | |
| 7 | never the key or the new token. Skips what exists. | |
| 8 | """ | |
| 9 | import json | |
| 10 | import os | |
| 11 | import sys | |
| 12 | import urllib.error | |
| 13 | import urllib.request | |
| 14 | ||
| 15 | ACCOUNT = os.environ.get("CLOUDFLARE_ACCOUNT_ID") or "1e6f2cffa3f445920836e8ebe446bb58" | |
| 16 | EMAIL = os.environ.get("CLOUDFLARE_EMAIL", "") | |
| 17 | KEY = os.environ.get("CLOUDFLARE_API_KEY", "") | |
| 18 | ZONE = os.environ.get("G1T_APPS_ZONE", "g1t.page") | |
| 19 | TOKEN_FILE = os.environ["TOKEN_FILE"] | |
| 20 | API = "https://api.cloudflare.com/client/v4" | |
| 21 | ||
| 22 | if not KEY or not EMAIL: | |
| 23 | sys.exit("CLOUDFLARE_EMAIL and CLOUDFLARE_API_KEY must be set") | |
| 24 | ||
| 25 | ||
| 26 | def call(method, path, body=None): | |
| 27 | request = urllib.request.Request( | |
| 28 | API + path, | |
| 29 | method=method, | |
| 30 | data=json.dumps(body).encode() if body is not None else None, | |
| 31 | headers={ | |
| 32 | "X-Auth-Email": EMAIL, | |
| 33 | "X-Auth-Key": KEY, | |
| 34 | "Content-Type": "application/json", | |
| 35 | # Cloudflare refuses Python's default user agent. | |
| 36 | "User-Agent": "g1t-setup", | |
| 37 | }, | |
| 38 | ) | |
| 39 | try: | |
| 40 | return json.load(urllib.request.urlopen(request)) | |
| 41 | except urllib.error.HTTPError as error: | |
| 42 | return json.loads(error.read() or b"{}") | {"http": error.code} | |
| 43 | ||
| 44 | ||
| 45 | zones = call("GET", f"/zones?name={ZONE}") | |
| 46 | if not zones.get("result"): | |
| 47 | sys.exit(f"zone {ZONE} not found: {zones.get('errors')}") | |
| 48 | zone = zones["result"][0]["id"] | |
| 49 | ||
| 50 | records = call("GET", f"/zones/{zone}/dns_records?name=*.{ZONE}") | |
| 51 | if records.get("result"): | |
| 52 | print(f"*.{ZONE}: record exists") | |
| 53 | else: | |
| 54 | made = call("POST", f"/zones/{zone}/dns_records", { | |
| 55 | "type": "AAAA", "name": "*", "content": "100::", "proxied": True, "ttl": 1, | |
| 56 | "comment": "g1t deployments: every app goes to the dispatch Worker", | |
| 57 | }) | |
| 58 | print(f"*.{ZONE}:", "record created" if made.get("success") else made.get("errors")) | |
| 59 | ||
| 60 | if os.path.exists(TOKEN_FILE): | |
| 61 | print("token: exists in .credentials; not making another") | |
| 62 | sys.exit(0) | |
| 63 | ||
| 64 | groups = call("GET", "/user/tokens/permission_groups") | |
| 65 | wanted = {"Workers Scripts Write", "Account Analytics Read"} | |
| 66 | ids = [g["id"] for g in groups.get("result", []) if g["name"] in wanted] | |
| 67 | if len(ids) != len(wanted): | |
| 68 | sys.exit("could not find the permission groups for the token") | |
| 69 | created = call("POST", "/user/tokens", { | |
| 70 | "name": "g1t deployments service (Workers for Platforms uploads, analytics)", | |
| 71 | "policies": [{ | |
| 72 | "effect": "allow", | |
| 73 | "resources": {f"com.cloudflare.api.account.{ACCOUNT}": "*"}, | |
| 74 | "permission_groups": [{"id": i} for i in ids], | |
| 75 | }], | |
| 76 | }) | |
| 77 | if not created.get("success"): | |
| 78 | sys.exit(f"token not created: {created.get('errors')}") | |
| 79 | os.makedirs(os.path.dirname(TOKEN_FILE), exist_ok=True) | |
| 80 | with open(TOKEN_FILE, "w", encoding="utf-8") as f: | |
| 81 | f.write(created["result"]["value"] + "\n") | |
| 82 | print("token: created and saved to .credentials") |