Reference
A short reference for the seed files, the command and the Go library. The full versions are in the repository: the seed format, the merge rules, and the command, the API and the ledger.
Seed Files
A directory of .toml files, one per table:
table = "roles"
key = "code" # a column, or a list for a composite key
on_remove = { retire = "retired_at" } # "keep" (default), "delete", or { retire = "column" }
on_conflict = "fail" # "fail" (default), "seed" or "local"
on_local_delete = "respect" # "respect" (default) or "restore"
[columns]
system = "seed" # the seed always wins
notes = "local" # set on insert, then the admin's
name = { own = "merge", on_conflict = "local" }
[refs]
parent_id = "roles.code" # store the id of the roles row with that code
[[row]]
code = "editor"
name = "Editor"
system = true
parent_id = "admin"
_renamed_from = "author" # keeps the row's id when its key changes
Columns a row leaves out are NULL. Every other column is merge. Values are compared the way SQLite stores them in that column, so a seed’s 1 in a TEXT column matches the text “1”.
The Merge
For every value, with the new seed, the table and the last-applied seed from the ledger:
| Situation | Result |
|---|---|
| table matches the seed | nothing to do |
local column |
the table’s value stays |
seed column |
the seed’s value is written |
| changed in the seed only | the seed’s value is written |
| changed in the table only | the table’s value stays |
| changed on both sides, differently | conflict |
| row was already there, never applied, different value | conflict |
For rows: new seed rows are inserted, rows deleted locally stay deleted (or come back with on_local_delete = "restore"), rows dropped from the seed follow on_remove, and a dropped row that was edited locally is a conflict before it’s deleted. A row due for deletion that something still points at is skipped until nothing does, and a row whose reference names a row that doesn’t exist is skipped and tried again next time. Rows the seed never named are never touched.
Commands
bst <command> [-db FILE] [-seeds DIR] [-json]
-db defaults to $BOOTSTRAPPING_DB, -seeds to $BOOTSTRAPPING_SEEDS or seeds. bootstrapping and bst are the same command. Flags can go before or after a command’s other arguments.
| Command | Does | Exit codes |
|---|---|---|
plan [-v] |
lists what an apply would do | 0 in step, 2 pending, 3 blocked |
apply [-take ...] |
does it, in one transaction | 0 done, 3 blocked with nothing written |
status |
one summary line | as plan |
explain TABLE KEY |
one row, value by value, with its history | 0 |
history [-n N] |
the applies, newest first | 0 |
init |
creates the ledger tables | 0 |
version |
the version | 0 |
Any error exits with 1. -take settles conflicts and can be repeated: seed or local for every conflict, or TABLE, TABLE/KEY or TABLE/KEY/COLUMN followed by =seed or =local.
Go
set, err := bootstrapping.LoadDir("seeds") // or LoadFS(embedFS, "seeds")
plan, err := bootstrapping.Preview(ctx, db, set, nil) // plans, writes nothing
plan, err = bootstrapping.Apply(ctx, db, set, &bootstrapping.Options{
Take: []bootstrapping.Resolution{{Table: "roles", Column: "name", Take: bootstrapping.TakeLocal}},
}) // ErrBlocked if a conflict stops it
rep, err := bootstrapping.Explain(ctx, db, set, "roles", "editor", nil)
runs, err := bootstrapping.History(ctx, db, 10)
db is a *sql.DB from any SQLite driver. A Plan lists its Changes, each with the table, the key, the action, the column, and the last-applied, seed and live values.
The Ledger
Four tables in your database, created on the first apply and written in the same transaction as the data: _bootstrapping_rows (the values last applied, per row), _bootstrapping_runs (one row per apply), _bootstrapping_changes (every write, for explain and history) and _bootstrapping_refs (the references last applied, so a column the seed stops naming is still read by natural key).
Limits in Version 0.1
- SQLite only. PostgreSQL is planned next.
- A reference names a row by one column.
- Built for reference data: thousands of rows per table, not millions.