adapt is a simple, non-magical general purpose migration library that gets embedded into your application. |
- 🟢 Simplicity: Migration lifecycle and schema versioning is completely abstracted and managed by adapt
- 🟢 Concurrency controlled (protected from race-conditions): Mutexes and other techniques are used to prevent multiple adapt instances conflicting each other during concurrent boot-ups
- 🟢 Extensible: Simple
Driver
and migrationSource
interfaces - 🟢 Migrations: can be provided in a variety of ways and get merged and sorted into a single migration-collection, which is applied against the version-controlled schema in your storage
- Go Code for all non-database migrations or situations where SQL doesn't do the job
- Embed migration folders containing your SQL scripts
- Hardcoded SQL statements
- 🟢 Branching and merging compatible: adapt automatically catches up with missing migrations ("holes")
- 🟢 Zero external dependencies
- 🟢 Customizable Logging: adapt uses
slog
included with Go 1.21+ so that you can provide your own logging backend.
- File - Basic driver that stores migration meta-data in a local JSON file (demonstrates how a
Driver
without any reliance or dependency ondatabase/sql
can be written.) - MySQL / MariaDB
- SQLite
- PostgreSQL
- Add driver ?
Any other storage backend by providing your own Driver
, DatabaseDriver
or SqlStatementsDriver
. Unlike most other migration tools, with adapt there is no reliance on database/sql
(such a case can be seen with the included FileDriver
)
- Go Code
- Filesystem
- In-memory
- Embedded Filesystem - Using Go 1.16+ go:embed
Note
Please support this project and provide additional sources that could be useful for other people
$ go get github.com/harwoeck/adapt
var db *sql.DB = initDB()
err := adapt.Migrate(
"backend@v0.1.17", // <name> of executor
adapt.NewMySQLDriver(db), // Database driver
adapt.SourceCollection{
adapt.NewFilesystemSource("./sql"), // SQL-Migration-Scripts from filesystem
})
Note
Next example: Due to compliance rules you decide to encrypt your users email addresses inside your database. Since this requires actual Go code (and not just SQL statements), you could implement one of the adapt.Hook
functions and during your next deployment adapt will notice this new unapplied migration and execute your hook. When no error is returned adapt will commit the transaction and update the schema table with the relevant meta information.
err := adapt.Migrate(
"backend@v0.1.17", // <name> of executor
adapt.NewMySQLDriver(db), // Database driver
adapt.SourceCollection{ // adapt will automatically merge and sort all provided sources for you
adapt.NewFilesystemSource("./sql"), // SQL-Migration-Scripts from filesystem
adapt.NewCodeSource("2020-04-17_1104_encrypt-user-email", adapt.Hook{
MigrateUpTx: func(tx *sql.Tx) error {
// For this encrypt-migration you could use `MigrateUpTx` to load
// the rows, encrypt the content in Go and update the columns again
// within a single managed transaction.
return nil
},
},
})
This project was heavily inspired by the features and ideas of these great projects: