Built with Chinaski

build.pl Reference

builder/build.pl is the static site builder Chinaski runs after content and settings changes. It reads content from the database, renders HTML into a staging directory, checks it, and swaps it live only when the build is healthy. There are no command-line flags: the builder is controlled entirely by environment variables, the settings you edit in the admin area, and the build controls on the dashboard.

This page covers the full build lifecycle and the switches you can use when running the builder by hand. (Recommended invocation for manual runs: set CHINASKI_DB and CHINASKI_OUTPUT first, matching how the CMS runs it.)

Guardrails

  • The builder refuses to run as root: output files would then be unwritable by the chinaski service user. Run it as the chinaski user instead (e.g. sudo -u chinaski perl builder/build.pl).
  • The builder refuses to run at all if the database file does not exist.

Where things live

  • Database: CHINASKI_DB if set; otherwise chinaski.db in the app root.
  • Output: CHINASKI_OUTPUT if set; otherwise output in the repo root.
  • Block templates: CHINASKI_BLOCK_TEMPLATES if set; otherwise builder/block_templates in the repo.
  • A completed build keeps the last 100 rows of build history (visible under Builds in the admin area).

Full vs incremental builds

The default build is incremental: it reuses what’s already live.

An incremental build is only attempted when:

  • incremental mode is actually requested,
  • a previous successful build is recorded (and not stamped in the future),
  • the live output directory still exists and contains an index.html,
  • the stored build fingerprint is unchanged (a fingerprint mismatch means a global change, such as a theme switch; that triggers a full build),
  • and seeding the staging copy of the live tree succeeds; if the seed fails the whole build silently falls back to full.

Anything else causes a full build, and the reason is recorded in the build history.

To force a clean full rebuild: use the Full rebuild checkbox on the dashboard, set CHINASKI_FORCE_FULL=1, or run bin/force-full-build.pl, which clears the build cache and then runs a full build.

Priorities and parallelism

CHINASKI_JOBS sets how many parallel workers render pages. It must be a plain number; it beats the build_jobs setting in the admin area, and both fall back to your CPU count. The value is clamped to the range 1–16. A value of 1 builds sequentially with no extra processes.

The timeout

Every build has a hard wall-clock limit of 30 minutes (1800 seconds). When the limit hits, in-flight render workers are killed and the build dies with a timeout message; nothing is promoted.

How a build runs

  1. Eligibility check: incremental or full is decided before anything is written, so a failed check can never damage the live site.
  2. Render to staging: pages, posts, and everything else are rendered into <output>.next, a staging directory alongside the live output. Incremental builds skip rewriting unchanged pages by reusing files from the previous build.
  3. Check quality: the promotion guard compares this build against the last healthy promote (see below). Render errors are collected as they happen.
  4. Write the signed manifest: a signed attestation of the output is produced only after all checks pass, and only if any errors would not already have blocked the build. If signing fails, the build aborts and the previous manifest is restored, unless the manifest is marked optional.
  5. Promote: the staging directory is swapped to live. The swap is atomic (a single rename), so there is no window where the site is half-updated.

When a build refuses to promote

The build never promotes over a broken result. Three conditions stop it:

  • Render errors. Any template or render failure blocks promotion. The live output is left untouched, staging is discarded, and the failing templates are reported.
  • The promotion guard (enforce mode). The guard compares this build against the last clean promote: page-count drop (default threshold 80%), increase in broken links (default 25), and increase in accessibility issues (default 10), plus an optional absolute limit on unresolved blocks. In enforce mode the promote is blocked exactly like a render failure. In warn mode (the default) the problem is logged but the build still promotes. Guard settings live under Settings → Workflow.
  • Manifest failure. A build cannot promote without a usable signed manifest.

Overriding a guard block is possible for one build at a time: the Promote anyway control on the dashboard, or CHINASKI_FORCE_PROMOTE=1. Forcing never updates the quality baselines; the next ordinary build still compares against the last clean promote, so a forced bad build can be caught later.

Exit codes

The builder exits 0 on a promotion that completed cleanly with zero render errors, and 1 on anything else: a guard block, render errors, a timeout, a manifest failure, or a failed promote. Deploy tooling relies on this: a nonzero exit aborts a standard deploy rather than restarting the service on stale output.

Running builds from the admin area

You normally don’t run build.pl by hand:

  • Saving settings that affect output, or changing the post URL prefix, dispatches a build automatically.
  • The dashboard (via Build) offers a normal (incremental) build, a Full rebuild, and Promote anyway, each with its own confirmation message.
  • Only one build runs at a time: starting a second is refused with “a build is already in progress”. The lock also expires on its own if a build never reports back, so it can’t wedge builds permanently.
  • While a build runs, /admin/build/status reports progress, and the build history shows the last 100 completed builds, including blocked and errored ones.
  • If a build notification email is configured under Settings → Email, a message is sent when a build succeeds or fails (delivered through the SMTP relay, so it needs SMTP set up and is skipped in air-gap mode).

Air-gap mode

With CHINASKI_AIRGAP=1 set, the builder strips all external references from generated pages: GA4/GTM analytics are removed even if configured, and snippet code that references external URLs is stripped. Built pages then load nothing from a CDN.

Environment quick reference

Variable Effect
CHINASKI_DB Database to read from (overrides the default)
CHINASKI_OUTPUT Where to publish
CHINASKI_INCREMENTAL=1 Request incremental (may still fall back to full)
CHINASKI_FORCE_FULL=1 Force a clean full rebuild
CHINASKI_FORCE_PROMOTE=1 Bypass the promotion guard for this build
CHINASKI_JOBS Parallel worker count (1–16)
CHINASKI_BLOCK_TEMPLATES Block template directory
CHINASKI_MANIFEST_OPTIONAL=1 Downgrade signed-manifest failure to a warning
CHINASKI_AIRGAP Strip analytics and external snippet references
CHINASKI_TEST Test-mode escape hatch (permits running as root)

See the environment variables reference for background on the shared variables.

Read this site in