Skip to content

Publishing on approval

Markdown

Approving a listing writes a row; the page exists only after the site rebuilds. providers.rebuild names what asks for that build — a Cloudflare Deploy Hook, a repository_dispatch to your own GitHub Actions, or nothing — and providers.rebuildAuto decides whether an approval asks by itself.

Pick the route that matches Deploy on git push.

  1. Create the hook: Cloudflare → Workers & Pages → your Worker → Settings → Build → Deploy hooks → Create deploy hook. The repository must be connected to Workers Builds first.

  2. Store the URL. It takes no header and no body, so it is the whole credential.

    Terminal
    pnpm cf:secrets DEPLOY_HOOK_URL

    The prompt ends with ✔ <n> characters received — setting it on <worker>… done.

  3. Switch the provider on and deploy.

    site.config.ts
    providers: { rebuild: 'deploy-hook', rebuildAuto: true },

    pnpm check:config, then push. The provider is read from the deployed build at request time.

Approve one listing in /admin/ and follow it.

What to check Where You should see
The request the publishing bar on /admin/ Last request: requested via deploy-hook on <date> — build_uuid <id>, or repository_dispatch "rebuild" accepted by <owner/repo>
The row pnpm exec wrangler d1 execute DB --remote --command "SELECT provider, status, detail FROM rebuild_requests ORDER BY created_at DESC LIMIT 1" requested, or coalesced when a build was already in flight
The build Workers & Pages → your Worker → Builds, or the Actions run a build shortly after the approval, with the [zerodirs-d1] line in its log
The page curl -sI https://<your domain>/<listingBase>/<slug>/ | head -1 HTTP/2 200 once the build finishes

Until then the submitter’s status page says “goes live with the next publish”, and the approval mail “within a few minutes”.

What an approval triggers. Approving assigns the slug, stamps approved_at, writes the audit row, queues the approval mail and calls requestRebuild() with reason approve:<id>. Editing, featuring or unfeaturing an approved listing asks too, as do the hourly expireFeatured (featured-expired) and the button (manual). Rejecting never does: nothing on the live site changed.

The request row. Every request writes one rebuild_requests row — reason, provider, status, attempts, detail — so the table is a complete log, not a sample. The status is requested (the detail is the build id), coalesced, failed (the HTTP status or error text, the hook URL scrubbed to [DEPLOY_HOOK_URL]), or skipped under none. A provider gets 10 seconds and never throws: an approve cannot fail because a build could not start.

Coalescing. A request within 60 seconds of a requested one is written as coalesced, folded into rebuild <id>, and the provider is not called: ten approvals in a minute are one build. Only requested rows open the window, so under none nothing coalesces and the skipped rows stay an honest backlog. The hook itself answers already_exists with the id of a build still queued; that is recorded as requested.

The hourly retry. retryRebuilds takes the oldest failed row with fewer than five attempts, fires the provider once, and on success folds the other failed rows into that build. A 429 or 5xx, a timeout or a transport error is retryable; a 404 (the hook is gone), any other 4xx, or a GitHub 401, 403, 404 or 422 is not — but a row is rescanned until its attempts run out either way. After five, only Publish now or pnpm deploy moves it.

Manual mode. rebuildAuto: false keeps the provider and makes every request wait: approvals accumulate and Publish now fires one build. The button ignores rebuildAuto.

The bar. RebuildBar on /admin/ shows the newest row and the count of approvals newer than the last requested row: “Up to date”, “N approved listings are waiting for the next build”, “The last publish request failed”, or under none “N approved listings are not published yet” with Record a publish — and since none never writes a requested row, that count does not reset. Beneath: Last request: <status> via <provider> on <date> — <detail>.

State Visitor sees /admin/ reports Fix
rebuild: 'none' approved listings stay off the site warning; rows skipped pnpm deploy, or a provider
'deploy-hook' without DEPLOY_HOOK_URL the same: the trigger degrades to none error: “Approvals will not trigger a build.” pnpm cf:secrets DEPLOY_HOOK_URL
'github-dispatch' missing a secret the same error: the trigger falls back to none pnpm cf:secrets GITHUB_TOKEN GITHUB_REPO
USE_GITHUB_DEPLOY unset the dispatch is accepted, the run is skipped requested, no build gh variable set USE_GITHUB_DEPLOY --body true
The hook was deleted nothing builds failed — HTTP 404: the deploy hook no longer exists create a new hook, store it
LISTINGS_SOURCE unset on the builder the build runs, the page is a 404 requested, build green set the D1 variables
Field Default What it changes
providers.rebuild 'none' 'deploy-hook' POSTs DEPLOY_HOOK_URL; 'github-dispatch' posts to GITHUB_REPO with GITHUB_TOKEN; 'none' records and skips
providers.rebuildAuto true whether an approval, an edit, a feature change or an expiry asks by itself

See providers.

  • Directorysrc/
    • Directoryserver/
      • Directoryrebuild/
        • index.ts requestRebuild(), the window, the row
        • deploy-hook.ts
        • github-dispatch.ts
        • none.ts
      • jobs.ts retryRebuilds
      • queries.ts what approve, update and feature ask for
    • components/admin/RebuildBar.astro
    • loaders/index.ts how LISTINGS_SOURCE is resolved
  • .github/workflows/deploy.yml the rebuild listener
  • .dev.vars.example the dashboard path, the token instructions
  • .env.example