3 releases (breaking)
Uses new Rust 2024
| 0.3.0 | Jun 25, 2026 |
|---|---|
| 0.2.0 | Jun 20, 2026 |
| 0.1.0 | Jun 14, 2026 |
#1814 in Database interfaces
350KB
7K
SLoC
stig
A SQLite migration and schema CLI.
Forward-only migrations, filesystem snapshots for local rollback, and schema-aware codegen — all in a single binary.
cargo install stig
Quickstart
Initialize a project, create a migration, apply it, and generate types:
# Bootstrap stig in the current directory
stig init
# Create a new migration (opens $EDITOR)
stig new create_users
# Apply all pending migrations
stig migrate
# Generate TypeScript types from the live schema
stig generate
After init you'll have a stig.toml config file, a db/migrations/
directory, and a schema_migrations tracking table in your database.
Commands
init
stig init
Bootstraps a new stig project in the current directory. Creates the config
file (stig.toml), migrations directory, backups directory (with
snapshots/ and resets/ subdirs), and ensures schema_migrations exists
in the database.
new
stig new [OPTIONS] <DESCRIPTION>
Scaffolds a timestamped migration file (db/migrations/<timestamp>_<slug>.sql)
and opens it in $EDITOR. The description is slugified: lowercased,
non-alphanumeric characters collapsed to _, capped at 60 chars.
stig new create_users # → 20260524103000_create_users.sql
stig new "Add Widget Types" # → 20260524103000_add_widget_types.sql
stig new fix-bug-#123 # → 20260524103000_fix_bug_123.sql
Options:
--no-edit— Skip opening$EDITORafter creating the file.
migrate
stig migrate [OPTIONS]
Applies all pending migrations in lexicographic order. Before each
migration, a filesystem snapshot of the database is taken (unless
auto_snapshot = false). Checksums are verified against
schema_migrations to detect drift.
Options:
--dry-run— Preview what would be applied without mutating state.
$ stig migrate
apply 20260524103000_add_widgets.sql (snapshot: pre-20260524103000.db)
skip 20260323081155_initial_schema.sql
✓ 1 applied, 1 already up to date
status
stig status
Reports migration state without changing anything. Shows a table of applied, pending, and drifted migrations along with snapshot availability.
$ stig status
database: app.db
migrations dir: db/migrations
checksum check: on
applied drifted snapshot version file
------- ------- -------- -------------------------------- -----------------------------------------
yes no pruned 20260323081155 20260323081155_initial_schema.sql
yes no yes 20260524103000 20260524103000_add_widgets.sql
no — — 20260525090000 20260525090000_widget_indexes.sql
summary: 2 applied, 1 pending, 0 drifted
redo
stig redo [OPTIONS] [VERSION]
Restores the snapshot taken before the named version and replays all migrations from that point forward. With no argument, defaults to the most recent applied migration.
Options:
--yes— Skip confirmation prompt.
Use this to pick up edits to an already-applied migration whose snapshot still exists.
$ stig redo
restoring pre-20260524103000.db
re-applying 20260524103000_add_widgets.sql
✓ redo complete
reset
stig reset [OPTIONS]
Destructive. Renames the live database into the resets backup directory and
re-migrates from empty. Prompts for confirmation unless --yes is passed.
If re-applying migrations fails partway through, the original database is
automatically restored from the reset backup. If that restore also fails, the
backup remains in resets/ for manual recovery.
Use this when a migration has been edited but its snapshot has already been pruned, or when you want a clean slate.
# Chain with your project's seed command
stig reset --yes && my-project seed
restore
stig restore [TIMESTAMP] [OPTIONS]
Restores the database from a reset backup created by stig reset. With no
TIMESTAMP, restores the most recent reset backup. With a timestamp, restores
the matching reset-<TIMESTAMP>.db file.
Prompts for confirmation unless --yes is passed.
Use this to return to a previous database state after testing against a fresh reset.
$ stig reset --yes
$ # ... test with empty database ...
$ stig restore --yes
✓ restored database from reset-20260520T101500Z.db
generate
stig generate [TARGET_NAME]
Runs configured codegen targets against the live schema. With no argument,
runs all targets. With a name (matched against kind or an optional name
field), runs only that target.
$ stig generate
✓ typescript → lib/database/types.ts
backups list
stig backups list
Lists snapshots and reset backups with sizes and ages.
$ stig backups list
snapshots (5 of max 5):
pre-20260525090000.db 48 KiB 2 minutes ago
pre-20260524103000.db 44 KiB 1 day ago
...
resets (1 of max 3):
reset-20260520T101500Z.db 42 KiB 5 days ago
backups prune
stig backups prune [OPTIONS]
Removes old backups according to the snapshot_keep and reset_keep
policies. Prompts for confirmation unless --yes is passed.
Configuration
Config lives in stig.toml at the project root. All keys have defaults;
the minimal config is an empty file.
# Path to the live SQLite database, relative to the project root or absolute.
database_path = "app.db"
# Where migration files live.
migrations_dir = "db/migrations"
# Where snapshots and reset backups live. Created by `init` with a .gitignore.
backups_dir = "db"
# How many pre-migration snapshots to retain.
snapshot_keep = 5
# How many reset backups to retain.
reset_keep = 3
# Whether to snapshot automatically before applying migrations.
auto_snapshot = true
# Whether to verify migration checksums on migrate/status.
checksum_check = true
# SQLite PRAGMAs applied on every connection open.
[pragmas]
journal_mode = "WAL"
foreign_keys = "ON"
# Codegen targets.
[[generate]]
kind = "typescript"
path = "lib/database/types.ts"
# Additional tables to exclude from codegen (glob patterns).
# sqlite_% and schema_migrations are always excluded internally.
# Values here are additive to those defaults.
exclude = ["posts"]
Environment variables
| Env var | Overrides | Notes |
|---|---|---|
STIG_CONFIG |
config file path | Skips upward search |
STIG_DATABASE_PATH |
database_path |
|
DATABASE_PATH |
database_path |
Fallback for legacy setups |
STIG_MIGRATIONS_DIR |
migrations_dir |
|
STIG_BACKUPS_DIR |
backups_dir |
|
STIG_NO_SNAPSHOT |
sets auto_snapshot = false |
Any non-empty value |
STIG_NO_CHECKSUM |
sets checksum_check = false |
Any non-empty value |
.env is loaded automatically at the start of every command.
CLI flags
Global flags accepted by every subcommand:
| Flag | Overrides | Notes |
|---|---|---|
--config <path> |
config file path | Beats STIG_CONFIG |
--database-path <path> |
database_path |
|
--migrations-dir <path> |
migrations_dir |
|
--backups-dir <path> |
backups_dir |
|
--schema-path <path> |
schema_path |
|
--no-snapshot |
sets auto_snapshot = false |
|
--no-checksum |
sets checksum_check = false |
When passed to stig init, the values of these flags are written into the generated stig.toml. For all other commands they apply only to that invocation.
Precedence
For any setting, highest wins:
- CLI flag
- Environment variable
stig.tomlvalue- Built-in default
Troubleshooting
Drift errors
If stig migrate or stig status reports drift, a migration file has been
edited after it was applied. The fix depends on whether the snapshot still
exists:
$ stig migrate
✗ migration 20260524103000_add_widgets has been edited since it was applied
snapshot pre-20260524103000.db is available
→ run: stig redo 20260524103000
- Snapshot available: Run
stig redo <version>to restore and re-apply. - Snapshot pruned: Either revert the edit, or run
stig resetto start fresh (the old database is backed up to the resets directory). - Intentional edits: Set
checksum_check = falseinstig.tomlorSTIG_NO_CHECKSUM=1to skip verification entirely (e.g. in production deploys from immutable images).
Missing snapshots
Snapshots are pruned automatically based on snapshot_keep (default 5). If
you need to redo a migration whose snapshot is gone, use stig reset
instead. Adjust snapshot_keep in stig.toml to keep more history.
:memory: databases
Setting database_path = ":memory:" opens an in-memory database. In this
mode:
PRAGMA journal_mode = WALis skipped (WAL is incompatible with in-memory databases).- Snapshots and resets are disabled.
- Migrations and codegen still work.
This is useful for CI or testing, but not for development workflows that rely on snapshots.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Generic failure (SQL error, IO error, unexpected state) |
| 2 | Usage / config error (invalid config, user declined prompt) |
| 3 | Drift detected |
| 4 | Prerequisite missing (snapshot gone, target unknown) |
| 5 | Database locked or otherwise unavailable |
Development
Git hooks are managed by hk via hk.pkl:
hk install
CI pipeline:
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo test --all
Versioning
This project follows Semantic Versioning:
- MAJOR — incompatible CLI or configuration changes
- MINOR — new functionality (commands, options, codegen targets)
- PATCH — bug fixes and documentation improvements
Until v1.0.0, minor releases may contain breaking changes. These will be documented in the CHANGELOG with migration guidance.
Installing a specific version
# Via cargo-install from crates.io
cargo install stig --version 0.1.0
# Via prebuilt binary from GitHub Releases
curl -sSL https://github.com/jackboberg/stig/releases/download/v0.1.0/stig-v0.1.0-aarch64-apple-darwin.tar.gz | tar xz
License
MIT. See LICENSE.
Dependencies
~45MB
~757K SLoC