Movine

Linux build status Crates.io

Movine is a simple database migration manager that aims to be compatible with real-world migration work. Many migration managers get confused with complicated development strategies for migrations. Oftentimes migration managers do not warn you if the SQL saved in git differs from what was actually run on the database. Movine solves this issue by keeping track of the unique hashes for the up.sql and down.sql for each migration, and provides tools for fixing issues. This allows users to easily keep track of whether their local migration history matches the one on the database.

This project is currently in early stages.

Movine does not aim to be an ORM. Consider diesel instead if you want an ORM.

Migration Concepts

Movine keeps track of four different states of migrations on the database. There are the basic ones:

Then there are the more complicated ones, which Movine was specifically designed to handle:

Short Asciinema Demo

A 3.5 minute video showcasing the various tools Movine provides.

asciicast

Getting Started

The first step to get started with Movine is to set the configuration. Configuration can be supplied either through a movile.toml file or environment variables:

Next, you can run the init command to set everything up, the generate command to create your first migration, and once those are written you can run up to apply them. ``` $ movine init $ tree migrations/ migrations/ └── 1970-01-01-000000movineinit ├── down.sql └── up.sql

1 directory, 2 files $ movine generate createnewtable $ tree migrations/ migrations/ ├── 1970-01-01-000000movineinit │   ├── down.sql │   └── up.sql └── 2019-03-17-163451createnew_table ├── down.sql └── up.sql

2 directories, 4 files $ movine up $ movine status 2019-03-17 16:34:51 UTC - Applied 2019-03-17-163451createnewtable 1970-01-01 00:00:00 UTC - Applied 1970-01-01-000000movine_init ```

Commands

There are a few commands that Movine uses, and all of them can be listed by using --help on the command line.

Init

The init command will run the initialization routine for Movine, which will create a table on the database to keep track of migrations and create a local migrations folder. ``` $ movine init $ ls migrations/ movine.toml $ tree migrations/ migrations/ └── 1970-01-01-000000movineinit ├── down.sql └── up.sql

1 directory, 2 files $ psql $PARAMS -c "\d" List of relations Schema | Name | Type | Owner --------+--------------------------+----------+-------- public | movinemigrations | table | movine public | movinemigrationsidseq | sequence | movine ```

Generate

The generate command will generate a folder with the current date and the given name in the migrations/ directory with blank up.sql and down.sql files. ``` $ movine generate createnewtable $ tree migrations/ migrations/ ├── 1970-01-01-000000movineinit │   ├── down.sql │   └── up.sql └── 2019-03-17-163451createnew_table ├── down.sql └── up.sql

2 directories, 4 files ```

Status

The status command will tell you the current state of all migrations, both local and on the database.

$ movine status 2019-03-17 16:34:51 UTC - Pending 2019-03-17-163451_create_new_table 1970-01-01 00:00:00 UTC - Applied 1970-01-01-000000_movine_init

Up

The up command will run all pending migrations. You can also run with the -p flag to show the migration plan without running it. This is true for all commands that modify the database and is useful for seeing if Movine will do what you expect.

$ movine up -p 1. Up - 2019-03-17-163451_create_new_table $ movine status 2019-03-17 16:34:51 UTC - Pending 2019-03-17-163451_create_new_table 1970-01-01 00:00:00 UTC - Applied 1970-01-01-000000_movine_init $ movine up $ movine status 2019-03-17 16:34:51 UTC - Applied 2019-03-17-163451_create_new_table 1970-01-01 00:00:00 UTC - Applied 1970-01-01-000000_movine_init

Down

The down command will rollback the most recent migration. $ movine down $ movine status 2019-03-17 16:34:51 UTC - Pending 2019-03-17-163451_create_new_table 1970-01-01 00:00:00 UTC - Applied 1970-01-01-000000_movine_init

Redo

The redo command will rollback and then re-apply the most recent applied migration or variant migration. Note: If the latest migration is divergent then redo will simply skip it. Be careful, and run fix if you want to fix divergent migrations. $ movine status 2019-03-17 16:34:51 UTC - Variant 2019-03-17-163451_create_new_table 1970-01-01 00:00:00 UTC - Applied 1970-01-01-000000_movine_init $ movine redo $ movine status 2019-03-17 16:34:51 UTC - Applied 2019-03-17-163451_create_new_table 1970-01-01 00:00:00 UTC - Applied 1970-01-01-000000_movine_init

Fix

The fix command will rollback everything until there are no divergent or variant migrations, and then apply all migrations except the migrations that were pending at the start. $ movine status 2019-03-17 16:41:07 UTC - Pending 2019-03-17-164107_create_another_table 2019-03-17 16:40:59 UTC - Divergent 2019-03-17-164059_modify_table 2019-03-17 16:34:51 UTC - Variant 2019-03-17-163451_create_new_table 1970-01-01 00:00:00 UTC - Applied 1970-01-01-000000_movine_init $ movine fix $ movine status 2019-03-17 16:41:07 UTC - Pending 2019-03-17-164107_create_another_table 2019-03-17 16:34:51 UTC - Applied 2019-03-17-163451_create_new_table 1970-01-01 00:00:00 UTC - Applied 1970-01-01-000000_movine_init

Custom

The custom command will allow you to specify your own migration strategy (in case Movine is not smart enough). Note: this is currently not implemented

Library Usage

Note: this API is in development. Please let me know your feedback!

Movine can be used as a library like so: ```rust use movine::{Movine, Config}; use movine::adaptor::SqliteAdaptor; use movine::errors::Error;

fn main() -> Result<(), Error> { let config = Config::load(&"movine.toml")?; let adaptor = SqliteAdaptor::from_params(&config.sqlite.unwrap())?; let mut movine = Movine::new(adaptor); movine.fix()?; Ok(()) } ```

Or, if you want to provide the params without going through the config file: ```rust use movine::{Movine, Config}; use movine::adaptor::SqliteAdaptor; use movine::errors::Error;

fn main() -> Result<(), Error> { let adaptor = SqliteAdaptor::new("file.db")?; let mut movine = Movine::new(adaptor); movine.fix()?; Ok(()) } ```

Why you should use Movine

Why you should not use Movine