repomatic.cloudflare module¶
Reconcile a Cloudflare Pages project against the state its repository declares.
A Direct Upload project is not reproducible from anything committed:
wrangler.toml only describes what a build would need, and these projects
are never built by Cloudflare. Everything that actually shapes the live site
(the compatibility date, Smart Placement, the build image, whether a git
source got attached) lives server-side in the project’s deployment_configs
and is invisible to anyone reading the repository. One project’s compatibility
date sat three years behind the live value with nothing noticing. This module
makes that state explicit, diffable and re-applicable, from the
[tool.repomatic] site.* keys.
Backs the cloudflare-pages command, in four modes: --check diffs live
against declared and exits non-zero on drift, --apply writes the declared
values back, --create creates the Pages project when missing (reusing an
existing one) then applies, and --dump prints the live state with secrets
redacted.
Credentials resolve in this order, so the same command works in CI and on a laptop without a token ever landing on a command line:
CLOUDFLARE_API_TOKENfrom the environment (what CI uses).The OAuth token
wrangler loginstores locally.
The account is never declared; the credential settles it. GET /accounts
answers what the token sees, and one scoped to nothing but Cloudflare Pages:
Edit still enumerates the account it belongs to, so CI carries the token
alone. A credential seeing several accounts resolves the ambiguity by asking
which one owns the project being reconciled, and fails rather than guesses
when that question has no single answer. No identifier is ever hardcoded
either: repositories using this are public, and account IDs do not belong in
them.
Caution
Never gate anything on GET /user/tokens/verify: that endpoint is
user-scoped, so an account-owned token (the recommended kind, cfat_ prefix)
answers 401 there while every project call succeeds. Proving the credential
against the project it is meant to touch is the only verification that means
anything, which is what every mode here does implicitly.
- repomatic.cloudflare.API_ROOT = 'https://api.cloudflare.com/client/v4'¶
Cloudflare v4 API root every call below is relative to.
- repomatic.cloudflare.API_TIMEOUT = 30¶
Socket timeout in seconds, wider than repomatic’s JSON default: a PATCH that stalls mid-write is worth waiting out rather than retrying blind.
- repomatic.cloudflare.EXPIRY_WARNING_DAYS = 30¶
How close a token’s expiry gets before
--checkstarts warning.Cloudflare notifies about neither an approaching expiry nor a passed one, so the monthly Docs run carrying this check is the only calendar the token has. A month of warnings is enough to rotate without ever reaching the red run.
- repomatic.cloudflare.SECRET_KEYS = frozenset({'api_token', 'oauth_token', 'refresh_token', 'secret'})¶
Response keys whose values must never be printed.
- repomatic.cloudflare.WRANGLER_CONFIG_PATHS: Final = (PosixPath('/home/runner/Library/Preferences/.wrangler/config/default.toml'), PosixPath('/home/runner/.config/.wrangler/config/default.toml'))¶
Where
wrangler loginstores its OAuth token, macOS first then XDG.
- exception repomatic.cloudflare.CloudflareError[source]¶
Bases:
RuntimeErrorRaised when the Cloudflare API refuses or a credential cannot be found.
- exception repomatic.cloudflare.CloudflareHTTPError(message, status)[source]¶
Bases:
CloudflareErrorAn HTTP-level refusal from the Cloudflare API, carrying its status.
Lets a caller act on which refusal arrived (a
404meaning “create it” is a different instruction than a403meaning “stop”) without parsing the message string it was raised with.
- class repomatic.cloudflare.Setting(path, desired, default, why, verified=False, managed=True)[source]¶
Bases:
objectOne server-side setting, with enough context to justify its value.
defaultis what a stock Cloudflare Pages project reports for this key. Where that value is quoted from Cloudflare’s documentation,verifiedis True. Where it is inferred from how the product behaves, it is False and the diff labels it as such: an unverified default is a reasonable guess, not a fact, and this module should not launder one into the other.
- repomatic.cloudflare.desired_settings(compatibility_date='', placement='')[source]¶
The settings to enforce, from the repository’s
site.*declarations.Only what the repository declares is managed, plus one documented floor: the build image major version, which Cloudflare auto-migrates old projects onto (v1 on 2026-09-15, v2 on 2027-02-23) and then freezes, so asserting
3requests nothing from a current project and names the stragglers.Pages supports exactly
productionandpreview, so the two environments are enumerated rather than globbed, and every managed setting applies identically to both.
- repomatic.cloudflare.MISSING¶
Sentinel a
_digwalk answers when the path names nothing.A named alias of the class it always was, so the three-way tests below read as the absence checks they are rather than as exception handling.
- repomatic.cloudflare.run_cloudflare_pages(project, *, check=False, apply=False, dump=False, create=False, attach_domain='', compatibility_date='', placement='')[source]¶
Drive one mode of the Pages reconciliation against project project.
Exactly one of check, apply, dump or create must be set; the CLI enforces that before calling.
- Parameters:
project (
str) – Cloudflare Pages project name to reconcile.check (
bool) – Diff live against declared, exit 1 on any drift.apply (
bool) – PATCH every managed drifted setting back to its declared value. Read-only drift is reported and keeps the exit code non-zero.dump (
bool) – Print the live project state as JSON, secrets redacted.create (
bool) – Create the Pages project (Direct Upload,mainas its production branch) when it does not exist yet, then apply the declared settings to it. An existing project is reused, so a re-run converges on the declared state instead of failing on the API’s409.attach_domain (
str) – Serve the project at this hostname, creating the DNS record it needs when the credential can. See_attach_domain().compatibility_date (
str) – Declared Workers runtime date, empty for unmanaged.placement (
str) – Declared Smart Placement mode, empty for unmanaged.
- Return type:
- Returns:
Exit code:
0clean,1drift found (or left, for the read-only settings--applycannot write).