Lint and plan on pull requests only
Optional: hand this page to your coding agentThe steps work by hand too.Show the whole prompt
Set up SQL Yodeler's pull request checks without its apply pipeline, following https://intentius.io/sql-yodeler/lint-and-plan/: set `ci: { apply: false }` in yodel.config.ts, run `npm run ci`, delete the apply files it no longer writes, and check that `npx yodel ci --check` passes.
List the readers' secrets the forge needs in the pull request description, and add no writer's secret.
Open a pull request with the result.
Never run `yodel apply` against a shared environment, never run `chant approve`, `yodel approve` or `yodel override`, never edit the `chant/lifecycle` branch or `.chant/allowed_signers`, never merge; approvals and applies belong to people.This page is for a team that wants SQL Yodeler to review migrations in pull requests before it lets SQL Yodeler apply anything. Every pull request gets yodel lint, the replay check and a plan comment per environment, and the migrations are applied some other way for now: from a terminal with yodel apply, or with the process the team already has. CI holds read-only credentials only, and no job in it can write to a database.
Setting it up
Section titled “Setting it up”-
Make the project. Starting a project covers both ways in: a new project from a starter template, or
yodel init --from <env>for a database that already exists. -
Turn the apply pipeline off in
yodel.config.ts:yodel.config.ts export default defineConfig({// ...ci: { forges: ["github", "gitlab", "forgejo"], apply: false },});apply: falsecannot be combined with a wave whoseapprovalispr-reviewor withci.resume, since both run in the apply pipeline.yodel cirefuses either combination and names the setting to remove. -
Render the pipelines with
npm run ci(which runsyodel ci). Withapply: falseit writesyodel-prand awatch-<env>per environment on each forge, and noyodel-waves.json, noyodel-applyoryodel-apply-plansworkflow, and no.gitlab/yodel-apply.gitlab-ci.yml;.gitlab-ci.ymlhas no apply stages and does not include that file. A project made from a template already has those files:yodel cidoes not delete them, so delete them yourself and commit the result.yodel ci --checkthen passes, because it no longer expects them. -
Add only the readers’ secrets on the forge: for each environment, its URL and its reader (
DEV_CLICKHOUSE_URL,DEV_CLICKHOUSE_READER_USER,DEV_CLICKHOUSE_READER_PASSWORDfordevon ClickHouse,DEV_POSTGRES_...on Postgres), and on GitLabGITLAB_TOKEN, a project access token with theapiscope (Reporter) for the plan comment and the drift watch’s issue. Where each one goes on each forge is in Setting up each forge. No writer’s secret is needed. -
Open a pull request with a migration and read the plan comment: the pending migrations for each environment, what they would run, and the digest an approval would be bound to. The pull request comment shows one.
What runs
Section titled “What runs”On each pull request, every job with readers or with no database credentials at all:
lint:yodel ci --check,yodel lint, and the replay check, which replays every migration into a throwaway server the job starts. It holds no database credentials.plan-<env>:yodel config check <env> --write-probe, which tries a write and fails the job if the server allows it, thenyodel plan <env> --comment. It holds the environment’s reader, and the reader of the environment before it in the waves (waves[].requires), so the plan can show where each migration has run.
On its schedule, watch-<env> compares the server with the declared schema and keeps a tracking issue open while it finds drift (Drift). It holds the environment’s reader.
Applying is left to you. YODEL_CREDENTIALS=writer yodel apply <env> from a machine that holds the writer applies behind the same approval as the pipeline would (Approving), and records each migration in the history the plan comment and the watch read.
Turning apply on later
Section titled “Turning apply on later”- Remove
apply: falsefromciinyodel.config.ts. - Run
npm run ciand commit what it writes:yodel-waves.jsonand the apply pipeline on each forge. - Add the writers’ secrets where Setting up each forge says, so that only each environment’s wave job can read them.
The next push to main runs the apply waves, each waiting for its approval. Approval covers the gates and the waves.
Proven by
Section titled “Proven by”The scenario claims below run what this page describes against a real server, once plain (it passes) and once with the behaviour broken (the claim catches it). Claims status lists every claim.
| Claim | What it says | Plain, broken | Last run |
|---|---|---|---|
template |
a project from the starter template, on Forgejo: apply only after approval, lint with replay and the plan comment on a pull request, and the approved change applied on merge; a sealed wave applies only on an approval sealed by a signer listed at the base, and a pr-review wave on the review of a writer other than the author; a pull request job cannot write, a forked migration fails lint and is annotated, a stale or hand-edited pipeline fails yodel ci –check, the CI image pinned by digest runs a pull request’s jobs, a command token source mints the reader’s password, and the drift watch keeps one tracking issue | ClickHouse: pass, caught; Postgres: pass, caught | 868ff97, 2026-10-10 |
template-github |
a project from the starter template, on GitHub Actions (act and a mock GitHub): apply only after approval, lint with replay and the plan comment on a pull request, and the approved change applied on merge; a sealed wave applies only on an approval sealed by a signer listed at the base, and a pr-review wave on the review of a writer other than the author; a pull request job cannot write, a forked migration fails lint and is annotated, a stale or hand-edited pipeline fails yodel ci –check, the CI image pinned by digest runs a pull request’s jobs, a command token source mints the reader’s password, and the drift watch keeps one tracking issue | ClickHouse: pass, caught; Postgres: pass, caught | 868ff97, 2026-10-10 |
template-gitlab |
not recorded |
