Import from another tool
Convert a golang-migrate, Goose, Flyway, Liquibase, or dbmate migration directory into Ptah's native format.
Your project already has migration history in another tool, and you want Ptah
to own it from here — without hand-rewriting years of files. This page shows
how ptah migrations import converts that history, what the converted
directory looks like, and how to pick up the lifecycle afterward.
Prerequisites: a built ptah binary and the source tool’s migration
directory. The example imports a golang-migrate directory; Goose, Flyway,
Liquibase, and dbmate work the same way.
Starting state
Section titled “Starting state”A golang-migrate directory in which the second migration has no down file:
db/migrations/ 000001_create_users.up.sql 000001_create_users.down.sql 000002_add_posts.up.sqlPreview the conversion
Section titled “Preview the conversion”--dry-run lists what would be written without writing it:
ptah migrations import \ --source-dir ./db/migrations \ --migrations-dir ./migrations \ --dry-runExpected output includes:
Dry run: would write 4 migration file(s) to ./migrations 0000000001_create_users.up.sql 0000000001_create_users.down.sql 0000000002_add_posts.up.sql 0000000002_add_posts.down.sqlRun the import
Section titled “Run the import”ptah migrations import \ --source-dir ./db/migrations \ --migrations-dir ./migrationsExpected output includes:
Wrote 4 migration file(s) to ./migrationsWrote ./migrations/ptah.sum 0000000001_create_users.up.sql 0000000001_create_users.down.sql 0000000002_add_posts.up.sql 0000000002_add_posts.down.sqlImport converts the source files into Ptah’s native
NNNNNNNNNN_name.up.sql / .down.sql layout, preserving version order, and
rewrites ptah.sum itself — validation passes immediately:
ptah migrations validate --dir ./migrationsOK: migrations directory matches ptah.sumA source migration with no rollback gets a placeholder down file, so the directory satisfies the down-file contract:
-- No rollback was provided by the source migration.Review placeholder downs before relying on rollback through those versions.
Supported source tools
Section titled “Supported source tools”The source tool is auto-detected from the directory layout; set --from to
assert it explicitly (golang-migrate, goose, flyway, liquibase,
dbmate).
| Tool | Notes |
|---|---|
| golang-migrate | NNN_name.up.sql / .down.sql pairs. |
| Goose | Annotated single files (-- +goose Up / -- +goose Down); the exact whole-file line -- +goose NO TRANSACTION becomes -- +ptah no_transaction on both imported directions. |
| Flyway | Including dotted versions, undo U__ scripts, and repeatable R__ scripts. |
| Liquibase | Formatted-SQL changelogs (--changeset / --rollback), and XML, YAML or JSON changesets that carry SQL; a changeset that carries a typed change or a selector is refused by name. |
| dbmate | Annotated single files (-- migrate:up / -- migrate:down); a directive keeps its options out of the SQL, and transaction:false on one direction becomes -- +ptah no_transaction on that direction alone. |
This is native Ptah-format import, distinct from the Atlas-compatible
migrate import verb of the ptah-compat binary, which writes an Atlas-format directory with
atlas.sum.
A repeatable becomes a one-time migration
Section titled “A repeatable becomes a one-time migration”Flyway’s R__name.sql re-runs whenever its body changes. Import converts it to
an ordinary one-time migration ordered after every versioned file, because the
destination format has no reapply semantics to convert it into.
That changes what editing the file means. The converted file is checksummed
like every other migration: ptah migrations validate refuses the directory
once you edit it, and re-hashing only seals the files to each other. The
database separately recorded the checksum of what ran, so ptah migrations up
refuses with a checksum mismatch until the file is back, and the edited body
never runs.
The recovery has an order. Put the file back as it was first, because the
refusal precedes every migration in the directory, and then carry the change in
a new versioned migration. Re-importing the source directory is the other way
back, where that directory is still the source of truth. ptah migrations repair is not the route — it edits recorded state, and nothing has gone wrong.
Migrate from Flyway runs the whole sequence.
The Atlas-compatible migrate import verb of the ptah-compat binary places a
converted repeatable on a reserved version slot above every versioned migration
instead, because an Atlas directory takes each migration’s version from the
file name.
After the import
Section titled “After the import”The converted directory is ordinary Ptah history; what comes next depends on the database:
- The database already has the schema (it ran the source tool’s
migrations): record the history as applied without executing it —
ptah migrations baseline, covered step by step in Adopt an existing database. - A fresh database: apply the directory from version zero.
Either way, future changes follow the regular loop starting at Generate migrations.
Failure modes
Section titled “Failure modes”Import never overwrites. Running it into a directory that already
contains a converted file fails (exit 2) and writes nothing:
error: refusing to overwrite existing migration file "0000000001_create_users.up.sql" in ./migrationsPoint --migrations-dir at an empty directory, or remove the partial result
and rerun.
A Liquibase changeset with nothing to convert is rejected by name. A typed
change such as <createTable> carries no SQL, and context, contexts,
labels and preConditions decide at run time whether a changeset applies,
which a migration directory cannot express. The message names the changeset,
the file and the construct.
Migrate from Liquibase works through both.
Next steps
Section titled “Next steps”- Database already migrated by the old tool? Adopt an existing database.
- Fresh database to bring up? Apply migrations.
- Long imported history slowing fresh setups? Checkpoints.