Tutorial: Deploying archerships.com

Full-site and single-post publishes, the pre-deploy gate, and the publish guard

— tutorial · 80% AI

Post 2026-A-0167

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

Step 1: Preview what a deploy would change

  1. Run:

    ~/av/bin/site status
  2. Read 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)
  3. 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

  1. Deploy the post by its slug:

    ~/av/bin/site post 2026-A-0166-2026-10-06-providers-of-abliterated-less-censored-inference
  2. Understand what that does, in order: build.sh SLUG renders that one post with the renderer for its frontmatter type, copies its HTML and images into dst/, 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.

  3. 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: nvim re-renders a post on every save, so a post you edited an hour ago is sitting in dst/ 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 bytes
  4. Useful 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).

  5. Expected output: deploy: site tests passed (bats + pytest + node), a deploy-guard: line, Success! Uploaded N files, a deployment URL, and a manifest line.

Step 3: Publish the full site

  1. Rebuild everything first, only if that is actually what you want:

    bash ~/av/bin/archerships/build.sh --full

    This 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 ships dst/ as it stands.

  2. Deploy the full site:

    ~/av/bin/site deploy

    ~/av/bin/site publish is the same command. It stages the whole dst/ tree except posts/ (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 rewrites prj/archerships.com/.deploy-manifest.tsv.

  3. 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)

  1. Run the dual-track publish:

    ~/av/bin/archerships/publish

    That runs the same full Pages deploy and then uploads the changed files to Arweave and flips the ARNS pointer.

  2. Options: --arweave-paid allows paid Turbo uploads (the default is free-only, deferring files over 100 KiB); --no-arweave skips the archive leg; publish path/a.html,path/b.html restricts the archive leg to named paths.

  3. 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 feeds

site 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

  1. Confirm the gate ran. Every deploy prints deploy: site tests passed (bats + pytest + node) before it uploads.

  2. 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 .html URL answers 308, so you need -L (Python’s urllib will not follow it by default). And archerships.com answers 200 with the home page for every missing path, so a status code proves nothing: check the title.

  3. 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 rewrites mailto: links at the edge while the deployment alias does not.

  4. For a full deploy, confirm nothing unexpected moved: run ~/av/bin/site status again and check that no modified-other entries remain.

  5. 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.