realkarych/seqwall
realkarych/seqwall has 32 GitHub stars and 2 forks. GitHot measured 0 stars in 24 hours and 0 in 7 days. Last checked 2026-11-09 22:18 UTC. Last push 2026-09-11.
🧪 Testing tool for PostgreSQL migrations — schema-snapshots based, framework‑agnostic.
Measured star growth
- in 24h
- 0
- in 7 days
- 0
- in 15 days
- Update delayed
- in 1 month
- Update delayed
Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.
README.md
Original
Seqwall is a tool for PostgreSQL migrations testing.
Ensure that every migration is reversible, idempotent, compatible with others in sequence, structurally sound and verifiable.
📦 Installation
Docker images
Package: https://github.com/realkarych/seqwall/pkgs/container/seqwall.
docker run --rm --network=host \
ghcr.io/realkarych/seqwall:latest staircase --help
Homebrew (macOS & Linux)
brew install realkarych/tap/seqwall
brew upgrade realkarych/tap/seqwall
Debian / Ubuntu (APT)
# Import the GPG key
curl -fsSL https://realkarych.github.io/seqwall-apt/public.key \
| sudo tee /etc/apt/trusted.gpg.d/seqwall.asc
# Add the repository
echo "deb [arch=$(dpkg --print-architecture)] \
https://realkarych.github.io/seqwall-apt stable main" \
| sudo tee /etc/apt/sources.list.d/seqwall.list
# Install / update
sudo apt update
sudo apt install seqwall # first install
sudo apt upgrade seqwall # later updates
Other distros / Windows
Download the pre‑built archive from the Releases page, unpack,
add the binary to your PATH.
On Windows, you may need
Unblock-File .\seqwall.exebefore first run.
Go install (Go ≥ 1.24)
go install github.com/realkarych/seqwall@latest
# make sure $GOBIN (default ~/go/bin) is on your PATH
✅ Once installed, verify it works
❯ seqwall staircase --help
Launch staircase testing
Usage:
seqwall staircase [flags]
Flags:
--postgres-url string PostgreSQL connection URL (defaults to DATABASE_URL)
--migrations-path string Directory containing lexicographically ordered migration files
--upgrade string Command that applies exactly one migration
--downgrade string Command that reverts exactly one migration
--test-snapshots Compare schema snapshots (default true)
--schema stringArray Schema to include in testing (repeatable) (default [public])
--depth int Number of migrations to test (0 means all)
--migrations-extension string Migration filename extension (default ".sql")
-h, --help help for staircase
🧬 Methodology & Core Principles
Migrations are contracts
Each migration must be reversible and must not break the schema if applied, reverted, and reapplied.
Snapshots reveal the truth
After each migration, Seqwall captures schema metadata using both SQL-standard information_schema views and
PostgreSQL-specific system catalogs and views. The snapshots are compared using structured diffs.
The project-required and tested compatibility range is PostgreSQL 13–18. PostgreSQL 13 remains Seqwall's compatibility floor even though it is outside upstream security maintenance; upstream maintenance and Seqwall's tested range are separate policies. Changing either end of the range requires a ticket, compatibility evidence, matching PostgreSQL integration and staircase matrices, and updated documentation.
Seqwall compares snapshots within one PostgreSQL major with unchanged connection and session settings. It does not
promise equality between snapshots serialized by different PostgreSQL majors. The public schema is selected by
default; repeat --schema to include more schemas. Only the properties captured below are compared.
| Object | Compared identity and properties |
|---|---|
| Tables/columns | Qualified table identity; ordered columns; column name, SQL/logical qualified type identity, type modifier/category, nullability, default, qualified collation, identity/generated state/expression, datetime precision, character length, numeric precision/scale |
| Views | Qualified identity and PostgreSQL view definition |
| Materialized views | Qualified identity, definition, and populated state |
| Indexes | Qualified identity and PostgreSQL index definition |
| Constraints | Qualified schema/table/name, full catalog-deparsed definition, type, deferrability, initial deferral, validation, inheritance, and enforcement where supported; CHECK/FK/PK/UNIQUE/exclusion plus native PostgreSQL 18 NOT NULL |
| Foreign keys | Qualified local/target relations, ordered local/target column lists, full definition, update/delete behavior; shared constraint state is represented by the constraint entry |
| Enums | Qualified identity and ordered labels |
| Triggers | Qualified owning table/name, full PostgreSQL definition, and enabled mode; user triggers including user constraint triggers, excluding internally generated triggers |
| Functions/procedures | Schema-qualified identity with argument types, routine kind, return type, and full definition; aggregates excluded |
| Sequences | Qualified identity, logical type, start/min/max/increment/cache/cycle, and nullable qualified column ownership/dependency kind; runtime counters excluded |
| Table privileges | Qualified table identity, grantee, privilege, and grantability visible through role_table_grants |
PostgreSQL 18's native NOT NULL metadata is captured as a real constraint, without regex or name rewriting. See the
PostgreSQL 18 release notes and
pg_constraint catalog.
Snapshot comparison does not establish universal database equivalence. Table persistence, row-level security,
partitioning, inheritance, relation options, and ownership are not comprehensively captured. Standalone domain,
composite, and range definitions and extension object definitions are not comprehensive. Non-table ACLs are not
comprehensive, and table grants are limited to privileges visible to the current role through role_table_grants.
Seqwall does not promise a consistent snapshot during concurrent DDL.
Staircase tests captured schema consistency
We use a 3-phase strategy:
actualize— applying all migrations and captures etalon schema snapshot for each migration.down → up → down— starting from the latest migration, step backwards:- downgrade one migration,
- upgrade it again,
- then downgrade once more (down step).
- At each step, the schema is compared with previously captured etalon snapshots — both before and after — ensuring reversibility and no drift.
re-actualize— starting from the lower point reached in step 2 (after several rollbacks):- re-apply each migration one by one
- compare each re-applied migration with etalon
This checks the captured schema metadata in both directions, including recovery from mid-chain downgrades.
Standalone by design
Seqwall is a single-purpose CLI tool — it requires no server, no daemon, no embedded framework, and no special runtime.
You can run it locally or in CI/CD (recommended), with just your migrations and a database connection string. No vendor lock-in, no config-files, no dependencies beyond PostgreSQL.
Test migrations as they really run
Seqwall runs your actual migration scripts and commands — no wrapper DSLs, no abstractions, no mocks.
You bring your own migration runner (dbmate, alembic, goose, sqlx, atlas, etc.).
Seqwall just executes shell commands.
Seqwall captures the database state before the first migration and expects the first rollback to restore that state. The database schema and migration-runner history must both begin in the state immediately before the first listed migration. Initialize the runner's metadata before starting Seqwall, without applying any listed migration. Any tables or other objects created during that initialization become part of Seqwall's initial snapshot.
Seqwall selects non-directory files by --migrations-extension and sorts their paths lexicographically. Every upgrade
or downgrade command must advance exactly one migration. Callbacks inherit Seqwall's environment and working directory.
For dbmate 2.27.0, the following command creates its schema_migrations table without applying a migration:
export DATABASE_URL='postgres://postgres@localhost:5432/postgres?sslmode=disable'
dbmate --schema-file /dev/null dump
This requires pg_dump on PATH. The schema dump is discarded through /dev/null, while the bookkeeping table
remains in the database and becomes part of Seqwall's initial snapshot.
Passing the current migration to your runner
Every upgrade and downgrade command receives the filename-safe current path through SEQWALL_CURRENT_MIGRATION,
even when the command contains no placeholder. Its value is the exact path discovered by Seqwall, without quoting or
normalization. Each command must apply or revert exactly one migration.
An unrestricted up that applies all pending migrations violates the staircase algorithm.
For a POSIX-compatible shell, pass the value as a double-quoted argument:
seqwall staircase --postgres-url "$DATABASE_URL" --migrations-path ./migrations \
--upgrade './migrate-one up "$SEQWALL_CURRENT_MIGRATION"' \
--downgrade './migrate-one down "$SEQWALL_CURRENT_MIGRATION"'
The outer single quotes defer expansion until Seqwall runs the command. Inside a shell script,
use "$SEQWALL_CURRENT_MIGRATION" in the same way. Repeat the quoted variable to pass the path twice.
Do not embed the value in source passed to eval or another sh -c.
Seqwall uses $SHELL, falling back to sh when unset or empty; a non-POSIX shell requires its own
safe variable syntax, or a helper that reads the environment directly.
On Windows, use a native helper that reads SEQWALL_CURRENT_MIGRATION, or invoke PowerShell scripts
without inserting the filename into the command string:
--upgrade "powershell.exe -NoProfile -File .\apply-one.ps1"
--downgrade "powershell.exe -NoProfile -File .\revert-one.ps1"
The scripts read $env:SEQWALL_CURRENT_MIGRATION as a string; for example,
Get-Content -LiteralPath $env:SEQWALL_CURRENT_MIGRATION reads that exact file.
A native helper can pass the value to its runner using an argument list.
Expanding %SEQWALL_CURRENT_MIGRATION% in cmd is not a universal literal-data contract:
delayed expansion, CALL, nested parsing, and command construction can reinterpret the filename.
The legacy {current_migration} placeholder retains raw source substitution for filenames containing
only ASCII letters, digits, _, -, ., and /; Windows also permits \ and :.
An empty value retains the previous empty substitution behavior. Every other filename is rejected
before the shell starts if the command contains a placeholder, including quoted, embedded, or repeated
placeholders. This deliberately restricts previously accepted templates with spaces or punctuation;
use the environment contract for those names. Legacy substitution preserves simple filename behavior
and does not promise literal arguments in arbitrary shell evaluation contexts.
Limitations & Scope
Does this mean Seqwall is the only tool you need for testing migrations?
No — databases involve a spectrum of concerns, and a complete testing strategy should include:
- Load testing — to observe performance & regressions
- Lock behavior analysis — to catch deadlocks and blocking issues
- Data state testing — to ensure data survives or transforms as expected
- Static analysis — to catch anti-patterns or unsafe operations before runtime
- Integration tests — to validate application logic against migrated schemas
- ...
Seqwall focuses on reversibility and consistency of the captured schema metadata.
🙏 Contribution
Found a bug?
- Please open an issue with a clear description, reproduction steps (if possible), and expected vs. actual behavior.
Have a question?
- Please open a discussion in QA section.
Or feel free to message me on Telegram:
@karych.
Want to suggest a feature?
- If you have a concrete and well-scoped idea — feel free to open a feature request.
- If the idea is more exploratory — start a discussion instead.
Ready to contribute code?
- Look for issues marked with
help wantedorgood first issue. In fact, you can pick any issue without Assignees 😊️️️️️️. - Fork the repo, create a branch, and open a pull request when ready (and tag
@realkarychfor review).
Your feedback and contributions are always welcome 💙.
Explore related projects
Asabeneh/30-Days-Of-Python
The 30 Days of Python programming challenge is a step-by-step guide to learn the Python programming language in 30 days. This challenge may take more than 100 days. Follow your own pace. These videos may help too: https://www.youtube.com/channel/UC7PNRuno1rzYPb1xLa4yktw
78,486 starsgrafana/grafana
The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
77,790 starsredis/redis
For developers, who are building real-time data-driven applications, Redis is the preferred, fastest, and most feature-rich cache, data structure server, and document and vector query engine.
77,332 starsapache/superset
Apache Superset is a Data Visualization and Data Exploration Platform
75,455 stars