make-cloudflare
Cloudflare Pages direct upload for mkrun, in
Python: publish a built directory with no Node on the machine and no wrangler
in the pipeline.
# Makefile.py in a consuming repo
# /// script
# requires-python = ">=3.11"
# dependencies = ["mkrun>=0.4", "make-cloudflare>=0.1"]
# ///
from make_cloudflare import cloudflare # importing is what registers the group
$ mk cloudflare.deploy site/dist --project mkrun # production (branch main)
$ mk cloudflare.deploy site/dist --branch try # a preview, on its own URL
$ mk cloudflare.deploy dist --functions functions # with Pages Functions
$ mk cloudflare.projects # what the account has
$ mk cloudflare.deployments --limit 5 # one project's recent ones
$ mk cloudflare.prune --keep 10 # delete all but the newest ten
$ mk -n cloudflare.deploy site/dist # the plan; nothing is sent
Credentials are the two variables wrangler already reads, so a repository that
deploys from CI needs no new secret: CLOUDFLARE_ACCOUNT_ID and
CLOUDFLARE_API_TOKEN (a token with Cloudflare Pages: Edit). The token's
name ends in TOKEN, so mkrun treats it as a credential — it is withheld from
the environment until a task asks for it, redacted in everything printed, and
readable from the encrypted store. The project can come from --project or from
CLOUDFLARE_PAGES_PROJECT in the repo's env layer.
Every deploy prunes the project to its newest 10 deployments (--keep N
to change it, --keep 0 to keep them all; pages.deploy(..., keep=) from
Python). Pages never deletes one by itself, and each stays reachable on its own
<hash>.<project>.pages.dev URL forever. The live production deployment is
never deleted, however old; a pruned preview that was still its branch's alias
takes that branch URL with it.
Pages Functions go up with the deployment. --functions functions (or
pages.deploy(..., functions=)) names the tree. It sits beside the built
directory, as wrangler expects, and /api/... answers from the same project and
origin. Bundling it is a build step, not an upload, so that one step is
wrangler pages functions build --outfile (from PATH, or through npx). It
writes exactly the _worker.bundle the deployment takes, plus
functions-filepath-routing-config.json and a generated _routes.json; the
site's own _routes.json wins. Everything else is still the four calls below.
Bindings (D1, KV) and secrets are settings on the Pages project, never part of
the upload.
Why this exists
wrangler pages deploy is four HTTPS calls and a hash. Reaching them through
npx costs a Node toolchain in repositories that otherwise have none — a Rust
one, a frontage one — and a
node_modules in the deploy image of every one of them.
POST /accounts/<acct>/pages/projects/<p>/upload-token -> a short-lived JWT
POST /pages/assets/check-missing -> what is not stored yet
POST /pages/assets/upload -> the files, batched
POST /pages/assets/upsert-hashes
POST /accounts/<acct>/pages/projects/<p>/deployments -> the manifest
Three things make a naive port wrong, and all three fail silently — every call returns 200 and the site serves the old bytes, or none:
- The hash is BLAKE3 of a strange input: the base64 text of the contents
with the extension (no dot) appended, first 32 hex characters. Not of the
bytes, and not SHA-256.
tests/test_pages.pylocks it with a golden value. _headers,_redirectsand_routes.jsonare not assets. They are fields on the deployment. Walk them in as ordinary files and the deploy reports success while the site's CSP stops applying.check-missing,uploadandupsert-hashestake the JWT, not the account token, and carry no/accounts/<id>prefix — the JWT already names the account.
What it does not do
Advanced-mode _worker.js. A _worker.js (or a functions/) inside
the built directory is refused by name: it would otherwise be served as a
static file. Pages Functions go beside it, through --functions.
Steps 2 and 3 are undocumented. They exist because wrangler uses them, and
Cloudflare can change them without a changelog; the documented deployments
endpoint alone cannot upload a file. That is the real cost of dropping
wrangler, and it is the reason this is a small module with a golden test rather
than a wrapper nobody reads.
Nothing here is specific to any owner or account. The group name is
cloudflare, and it merges with a repo's own cloudflare.* tasks — groups are
namespaces — so only a same-named task collides; this package claims deploy,
projects, deployments and prune, nothing else. blake3 is the one dependency, and
it lives here rather than in the runner so mkrun itself stays dependency-free.
Metadata
Release files for make-cloudflare 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| make_cloudflare-0.3.0.tar.gz | 16.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| make_cloudflare-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 29.7 kB
Release files / make_cloudflare-0.3.0.tar.gz
| Download URL | make_cloudflare-0.3.0.tar.gz |
|---|---|
| Size | 16.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
aca61b7004c7b0ddd097dbb7f8b7ea78551d7efafa3e6156427511792e8b2258
|
|
BLAKE2b-256 checksum How to use checksums |
211fb0c0775c864953a8fe07fbb65146bbe0351fc5d4daf275bfc76b815f6a44
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / make_cloudflare-0.3.0-py3-none-any.whl
| Download URL | make_cloudflare-0.3.0-py3-none-any.whl |
|---|---|
| Size | 13.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ebdb7acc2c6e005eddf1144d41e544af020f3a999ce8aa76a631737a8789ee01
|
|
BLAKE2b-256 checksum How to use checksums |
6f06625d6f8d32b05b77127dffa5562c6ad92b7d80460a2d053ca60123f65f68
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|