Abstract
Every route to the live site goes through one command, ~/av/bin/site, which drives one deploy path: stage the built tree, run a publish guard against a manifest of what is already live, upload the snapshot to Cloudflare Pages, and record a new manifest. This tutorial covers the reader’s side of that: how to preview what a deploy would change, how to publish a single post, how to publish the whole site, how to add the permanent Arweave copy, and how to verify the change actually landed.
Prerequisites
- A working
~/avcheckout. The commands below live in~/av/binand~/av/bin/archerships. python3on PATH: the renderers, the guard, and the sitemap generator all use it.make: every deploy runs themake test-sitegate before uploading.- Node plus
npxwith the wrangler OAuth login for thearchershipsPages project. The deploy runsenv -u CLOUDFLARE_API_TOKEN, so wrangler uses the interactive OAuth session rather than a DNS-only API token. - The post you intend to publish already exists as
~/av/doc/posts/SLUG/SLUG.md, saved to disk. - For the archive step only: the Arweave wallet and ARNS pointer for this project already configured.
Step 1: Preview what a deploy would change
Run:
~/av/bin/site statusRead the report. It stages a throwaway copy of
dst/and runs the guard in dry-run, so nothing is published and no manifest is written. It prints one summary line and up to three lists:- drafts: post pages that are not deployed and not live, which a deploy would withhold
- modified-other: post pages that changed locally since the last deploy, which would abort a scoped publish
- everything else: allowed (indexes, feeds, sitemap, pages, assets)
Decide from that output what to publish, and with which flags. This is the cheapest way to answer “why did my publish stop?” before running it.
Step 2: Publish one post
Deploy the post by its slug:
~/av/bin/site post 2026-A-0166-2026-10-06-providers-of-abliterated-less-censored-inferenceUnderstand what that does, in order:
build.sh SLUGrenders that one post with the renderer for its frontmatter type, copies its HTML and images intodst/, renames images to webp and patches the HTML; then the home page, essays index, feeds and a changelog entry are regenerated; then the post-render inject passes run (contact block, nav, feed notice, head metadata, sitemap); then the deploy runs with the guard restricted to that post.If the guard stops the publish, it lists the post pages that would be republished along with yours. That is a safety check, not a bug:
nvimre-renders a post on every save, so a post you edited an hour ago is sitting indst/changed. Choose one:~/av/bin/site post SLUG --allow-extra # publish those other pages too ~/av/bin/site post SLUG --keep-others-live # put them back to their deployed bytesUseful flags:
--no-deploy(render and build only),--dry-run(report the plan, change nothing),--arweave(also run the free-only archive leg),--no-gate(skip the test gate; lightweight paths only).Expected output:
deploy: site tests passed (bats + pytest + node), adeploy-guard:line,Success! Uploaded N files, a deployment URL, and a manifest line.
Step 3: Publish the full site
Rebuild everything first, only if that is actually what you want:
bash ~/av/bin/archerships/build.sh --fullThis wipes
dst/, runs every generator, re-renders every post, copies everything and rebuilds Pagefind. It takes 15-30 minutes. A full deploy does NOT do this for you; it shipsdst/as it stands.Deploy the full site:
~/av/bin/site deploy~/av/bin/site publishis the same command. It stages the wholedst/tree exceptposts/(the Arweave-only archive;pagefind/is included, or live search breaks), runs the guard in class mode (existing post pages may be refreshed, a new post page is still withheld as a draft), uploads, and rewritesprj/archerships.com/.deploy-manifest.tsv.Do not stage a subtree by hand. A Cloudflare Pages deployment is a complete snapshot that serves only the staged files, so a partial stage deletes every other page from the live site. That regression has already happened once.
Step 4: Add the permanent Arweave copy (optional)
Run the dual-track publish:
~/av/bin/archerships/publishThat runs the same full Pages deploy and then uploads the changed files to Arweave and flips the ARNS pointer.
Options:
--arweave-paidallows paid Turbo uploads (the default is free-only, deferring files over 100 KiB);--no-arweaveskips the archive leg;publish path/a.html,path/b.htmlrestricts the archive leg to named paths.The ARNS flip is non-blocking. If it fails, the content is still on Arweave; retry the pointer with:
~/av/bin/archerships/publish --set-arns <manifestTxId>
Step 5: Other scopes (optional)
Each of these rebuilds only its own scope but still stages and deploys the whole tree:
~/av/bin/site essays # all posts plus recipes and pages
~/av/bin/site recipes # recipes and the recipe index
~/av/bin/site pages # static pages from doc/pages
~/av/bin/site home # the home page only
~/av/bin/site pebbles # the Shiny Pebbles wiki and its feedssite pebbles publishes a wiki entry and, by default, keeps any other changed post page at its already-deployed bytes, so adding one entry cannot drag a half-finished post live. Pass --allow-extra to republish those pages instead.
Verification
Confirm the gate ran. Every deploy prints
deploy: site tests passed (bats + pytest + node)before it uploads.Fetch the page you published and check its title:
curl -sL "https://archerships.com/essays/SLUG.html" | grep -o "<title>[^<]*</title>"Two traps here. The
.htmlURL answers308, so you need-L(Python’s urllib will not follow it by default). And archerships.com answers200with the home page for every missing path, so a status code proves nothing: check the title.Check the deployed bytes rather than the served ones if the content matters. The deploy prints its deployment URL (
https://<id>.archerships.pages.dev); fetching the same path there returns the raw deployed file, because archerships.com rewritesmailto:links at the edge while the deployment alias does not.For a full deploy, confirm nothing unexpected moved: run
~/av/bin/site statusagain and check that nomodified-otherentries remain.For a single post, confirm the indexes followed: the home page and
/essays/should list the post, and its feeds entry should be present.
Want to stay in touch? You can reach me in a variety of ways from my Contact page.