SQL Yodeler

SQL YODELER
Declarative schema migrations for ClickHouse and Postgres
Declare what the database should be, in TypeScript. yodel works out the change and applies it, or writes it as a migration, behind an approval of the exact plan.
export const events = table` CREATE TABLE ${db}.events ( id UInt64, kind LowCardinality(String), at DateTime, country LowCardinality(String) DEFAULT '' ) ENGINE = MergeTree ORDER BY (kind, at)`;20261010T2213-add-country 0 statement SQLCH201 metadata events.events ALTER TABLE `events`.`events` ADD COLUMN country LowCardinality(String) DEFAULT '' AFTER `at`export const orders = table` CREATE TABLE ${shopSchema}.orders ( id bigint GENERATED ALWAYS AS IDENTITY, customer_email text NOT NULL, status text DEFAULT 'placed'::text NOT NULL, amount numeric(12,2) NOT NULL, placed_at timestamp with time zone DEFAULT now() NOT NULL, coupon text, CONSTRAINT orders_pkey PRIMARY KEY (id), CONSTRAINT orders_amount_positive CHECK (amount > 0) )`;-- orders (shop.orders): SQLPG201 metadataALTER TABLE shop.orders ADD COLUMN coupon text;
-- orders (shop.orders): SQLPG217 metadata-- pre-check, must return 0 (rows that fail the check orders_amount_positive (amount > 0)): SELECT count(*) AS n FROM shop.orders WHERE NOT (amount > 0)ALTER TABLE shop.orders ADD CONSTRAINT orders_amount_positive CHECK (amount > 0) NOT VALID;
-- orders (shop.orders): SQLPG220 validateALTER TABLE shop.orders VALIDATE CONSTRAINT orders_amount_positive;Works
- A project with a profile, a migrate Op and a drift watch per environment, and pipelines for GitHub, GitLab and Forgejo
- The first migration written from
src/schema.ts, planned, approved and applied on chant's local emulator - Lint, the replay check and a plan comment on every pull request
- Sort-key and engine changes as rebuilds into a new table, swapped in by the migration
- Backfills as steps that resume where they stopped
- A single node, a cluster, a Replicated database or ClickHouse Cloud
- GitHub: The writer's secrets are secrets of a GitHub environment limited to
main. The plan comment and the drift issue use the job's own token.
Proven: 15 claims on ClickHouse. Claims status
First step
npx @intentius/sql-yodeler@latest create my-schema --clickhouse --database eventsYour first migrationMore in Start a new schema: what differs on each database and forge, the proof, and what to read next.
Your schema, declared in TypeScript
Declarative or versioned migrations
GitHub, GitLab or Forgejo
Approvals bound to the plan
Data steps that resume
Lint and the replay check
Drift watched on a schedule
An audit log from the history
The pipelines yodel ci writes
Section titled “The pipelines yodel ci writes”npm run ci renders them for GitHub Actions, GitLab CI and Forgejo Actions, from the environments and the Ops the project declares. The two workflows.
lintevery pull requestyodel lint, the replay check on a throwaway server, and yodel ci --check on the rendered pipelines.
plan-<env>every pull requestThe plan as one comment per environment, made with the reader, after a write probe that fails a job that could write.
yodel-applyon a push to mainOne wave per environment, in order, each behind its gate policy as main had it.
yodel approvewhen a wave waitsA person reads the plan, types the environment, and the waiting job resumes.
watch-<env>on a scheduleDrift between the declared schema and the live one, kept in one issue.
A gate per environment
Section titled “A gate per environment”Each environment is one wave of the apply pipeline, applied after the wave before it. Its gate policy decides when it waits for a person, and an approval covers exactly the plan it was shown, by its digest.
- devgate: never
jcs1-sha256:6a1f…c09e
appliedapplied on the push to main - staginggate: on-destructive
jcs1-sha256:b77d…12a4waitingnpx yodel approve staging - prodgate: always
planned when staging appliesnextwaits for its own approval
refusedA migration changed after staging's approval, so staging applied nothing and named both digests.
Approval has the policies, the digest and what happens when it moves.
The docs
Section titled “The docs”SQL Yodeler manages the schemas of ClickHouse and Postgres databases declaratively. You declare what the schema should be in TypeScript with chant’s sql lexicon (Declaring the schema), and the yodel CLI works out the change: it either applies it directly (the declarative path) or writes versioned migrations from it and applies those (the versioned path). Both paths apply behind an approval bound to a digest of the plan. Data migrations (rebuilds, backfills, expand-and-contract column changes) are steps inside a migration, recorded in the same history as the DDL, and resume where they stopped.
| Page | What it covers |
|---|---|
| Your first migration | from an empty directory to applied migrations on a local database: the template, the emulator, the variables, plan, approve and apply |
| Installing and configuring | installing yodel, the project layout, chant.config.ts profiles, yodel.config.ts, the environment variables |
| Starting a project | a new project from a starter template, yodel init --from to adopt a database that already exists, and yodel init --baseline for another environment that holds the same schema |
| Coming from another migration tool | for a database golang-migrate, goose, Flyway or dbmate manages: the concepts mapped, adopting it with yodel init --from, the old tool’s files and table, and rollback with yodel revert |
| Declaring the schema | the schema as TypeScript in src/: one export per object in the database’s own DDL, references with ${}, splitting it across files, generating objects with code, composites, what the build checks and the editor |
| The two workflows | the declarative path (yodel plan, yodel apply against the declared schema) and the versioned path (yodel new, then yodel apply) |
| Migrations | the migrations directory, the history table, the apply lock, resume, out-of-order refusal, yodel status, yodel rebase, yodel repair, yodel checkpoint, and rollback with yodel revert |
| Data migrations | ClickHouse rebuilds, backfills, Postgres PostgresMigrationOp steps, yodel cleanup of what they keep, and manual steps |
| Access control | Postgres row-level security, policies, roles and grants, and ClickHouse users, roles, row policies and grants: access per environment, what the schema owns and what the environment owns, the plan’s access section, adopting them |
| Lint | the rules, yodel:allow silences, rule levels, --env, and the replay check (--replay) |
| Lint and plan on pull requests only | ci: { apply: false }: lint, the replay check and the plan comment on every pull request with readers only, no apply pipeline, and turning apply on later |
| Setting up each forge | GitHub, GitLab and Forgejo side by side: the pipeline files, where the readers’ and the writer’s secrets go, the tokens for the plan comment, the drift watch and resuming, cloud roles over OIDC, runners and pull requests from forks |
| Approval | the migrations Op and its gate, the plan digest, the pull request comment, and what happens when the digest moves |
| Reports and the audit log | yodel report: every environment’s migrations side by side, the audit log derived from the history and the gate ledger, its gaps, audit.jsonl and the HTML run view |
| Schema from an ORM | sources in yodel.config.ts: tables an ORM defines, read from the DDL it prints, planned and applied with the rest |
| Drift | yodel drift and the scheduled WatchOp |
| Webhook events | notify in yodel.config.ts: signed events for an apply waiting for approval, a refusal, a failure and drift, and checking them in a receiver |
| Generated schema reference | yodel docs: an HTML reference and a Mermaid ERD generated from the declared schema |
| Topology | ClickHouse on a single node, a cluster, a Replicated database and ClickHouse Cloud |
| The commands | which command runs what (yodel, chant’s CLI, the template’s just targets), yodel --help, the exit codes, and the JSON Schemas of --json output |
| Set up with a coding agent | the prompt to hand a coding agent, the steps it follows to add SQL Yodeler to a repository, and what it leaves to people |
| Glossary | the chant terms yodel’s commands and output use, in yodel’s terms |
| Claims status | every scenario claim, what it says, and whether it passed plain and was caught broken on ClickHouse and on Postgres, with the pages each one proves |
Two example projects, one per dialect, show each of these on a real server. They are finished projects, and their READMEs are transcripts of the runs that made them, against chant’s emulator, with the output each command printed. To follow along step by step, start with Your first migration:
examples/clickhouse: adoption, a migration, a sort-key rebuild, a backfill, a lint silence, an approval that stops holding, drift, the replay check.examples/postgres: adoption, lint findings, a migration that fails part way and resumes, a column rename as an expand-and-contract step, a destructive change silenced, a fork resolved withyodel rebaseandyodel repair, an out-of-order migration, drift, the replay check.
The pages quote those runs.
Hand the setup to your agent
Section titled “Hand the setup to your agent”Your first migration has the same steps by hand. Or paste this into a coding agent in your repository. The agent page says what the agent does; it never applies and never approves.
Hand the setup to your coding agentOr set up by hand: Your first migration has the same steps.Show the whole prompt
Set up SQL Yodeler in this repository.
Read https://intentius.io/sql-yodeler/llms.txt first, then
https://intentius.io/sql-yodeler/agents/ and follow it.
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.