A pipeline for a voidbase project

The common page connects the repository and explains the two tokens. This one is what to do after that: what a check should prove, what a branch should get, and which keys a build is allowed to read.

A check that changes nothing

Sync will tell you what it would do and stop.

voidbase sync --dry-run

It prints the Worker it would write to, the resources it would create, the configuration it would set and the exact trigger commands, and exits without touching the account. The deploy plugins run as a dry run too, so the printed plan is the whole plan. The failure it catches most often is a secret declared in the repository that has no value anywhere, which would otherwise fail the deploy of a new Worker.

A preview instance per branch

Instances are cheap and completely separate, so a branch can have its own rather than sharing a staging one. The second trigger a Worker may have is for exactly that.

VOIDBASE_GH_TOKEN=... voidbase sync --previews
created trigger "blog-api (previews)": a push to any other branch runs `true`, then `VOIDBASE_PREVIEW=$WORKERS_CI_BRANCH bun run deploy`; the production build prunes the previews whose pull request is over (VOIDBASE_PREVIEW_PRUNE=1)

From then on a push to any branch but the production one deploys a second Worker beside production, blog-api-pr-<slug>, the slug being the branch lowercased and cut short with a short hash of the whole branch name on the end, so feature/login and feature-login are two Workers and the same branch is always the same one. It gets its own database, bucket and queue, stays on workers.dev whatever custom domains production has, and knows nothing about production.

It is seeded from production after the upload, through the backups API: the collections' definitions by default, VOIDBASE_PREVIEW_SEED=data for the rows and files too, none to skip it. The seed signs in on both sides with the superuser, which is why sync puts VOIDBASE_SUPERUSER_EMAIL and VOIDBASE_SUPERUSER_PASSWORD on the previews trigger as build secrets when your environment has them. A seed that fails leaves the preview up and unseeded, and says so.

With VOIDBASE_GH_TOKEN, a fine-grained GitHub token with pull requests: write on the repository, the branch's open pull request gets one comment with the address, the REST API and the panel under it, what it was seeded with and the version, updated by every later deploy of the branch rather than repeated. And because sync sets VOIDBASE_PREVIEW_PRUNE=1 on the production trigger, every production deploy removes the previews whose pull request is merged or closed. That is how a preview disappears on merge without anything listening for GitHub events.

The same from a laptop:

voidbase deploy --preview feature/login            # a preview of this project's Worker for the branch
voidbase previews                                  # the previews on the account: branch, address, created
voidbase previews remove feature/login             # the preview goes with its database, bucket and queue
voidbase previews prune --merged                   # every preview whose pull request is merged or closed goes

Two things to know before you turn this on. A preview Worker is new and holds no secrets, so a secret() declared without a value in the build's environment fails its deploy the way it would for any new Worker: declare it optional, or set it as a build secret on the previews trigger. And each preview has a D1 database of its own, which counts against the free plan's 10 per account. What a preview costs past that is on the pricing page.

Removing a preview deletes everything it owns, unlike a production --remove, because a preview is disposable by definition. voidbase sync --no-previews removes the trigger.

What the build may read

Every key is declared with a tier, and the tier is what decides whether the pipeline can see it.

// pb_secrets/main.ts
import { defineSecrets, secret, server, browser, local, string } from "@voidbase-cloud/voidbase/secrets";

export default defineSecrets({
  STRIPE_SECRET_KEY: secret(string(), "the worker holds it; no build ever sees it"),
  SENTRY_DSN: server(string().optional(), "the worker, and readable back"),
  PB_PUBLIC_URL: browser(string().optional(), "inlined into what the browser downloads"),
  VOIDBASE_DEPLOY_CF_API_KEY: local(string(), "your machine and the pipeline, never the worker"),
});

secret() is written to the Worker and never readable again, so a build cannot print it even by accident. local() is the opposite: it belongs to your machine and to CI, and never reaches the Worker, which is where the deploy token lives. pb_secrets has the whole model, and what git tracks covers the account's Secrets Store and feature flags.

voidbase secrets                     # declared, valued, and by whom

That prints what is declared and where each value is, and it is the fastest answer to "why did the deploy work on my machine".

A domain, and taking it down

Both are on the common page, because they are the same for a stack app: a custom domain is --domain on sync with the canonical hostname first, and voidbase deploy --remove takes the Worker down while keeping its data.

Upgrading voidbase in a tracked way

The dependency is in package.json, so an upgrade is a commit and goes through the same pipeline as anything else. voidbase update bumps it on your machine; voidbase update --check changes nothing and exits 1 when a newer release is out, which is what to run wherever you run scheduled checks. voidbase update also names the installed plugins a new voidbase would leave behind before it changes anything.

Found something wrong on this page?

Fix it yourself. The link below opens this file in GitHub's editor and forks the repository for you if you need one, and your change becomes a pull request without leaving the browser.