Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,53 @@
# Changelog

## [Unreleased]
### Added
- `apply` accepts `--step N` to apply exactly N migrations, in either direction
- `apply` and `redo` accept `--allow-unknown-tags` to permit a run when the database has an
applied tag not present in the defined migration set, and `--allow-out-of-order` to permit
applying migrations out of their defined order. Both are off by default: an unknown applied
tag or an out-of-order pending migration now aborts the run with an error

### Changed
- `apply` applies all pending migrations by default, instead of just the next one. Use
`--step 1`, or `--down` (which remains single-step by default), to move one migration at a
time
- The `__migrant_migrations` bookkeeping table is now multi-column (`id`, `tag`, `checksum`,
`applied_at`) instead of a single `tag` column. Each applied migration now records a sha256
checksum of its up-SQL (null for programmatic migrations) and an applied-at timestamp, and
applied order is tracked by `id` rather than inferred from file/tag order. `redo` and
`apply --down` now target the most-recently-applied migration by this recorded order.

This is a one-time breaking change to the on-disk schema. If you don't need to preserve
applied history, the simplest upgrade is to drop the existing `__migrant_migrations` table
and let `migrant setup` recreate it. To upgrade an existing table in place instead, add the
missing columns and backfill them (existing rows can be left with `checksum` null):

```sql
-- postgres
alter table __migrant_migrations add column id bigserial;
alter table __migrant_migrations add column checksum text;
alter table __migrant_migrations add column applied_at timestamptz not null default now();

-- sqlite
alter table __migrant_migrations add column id integer;
alter table __migrant_migrations add column checksum text;
alter table __migrant_migrations add column applied_at text not null default (datetime('now'));

-- mysql
alter table __migrant_migrations add column id bigint unsigned auto_increment unique;
alter table __migrant_migrations add column checksum text;
alter table __migrant_migrations add column applied_at timestamp not null default current_timestamp;
```

Since applied order was not previously tracked, `id` will not necessarily reflect the
original apply order after an in-place upgrade; reconstruct it manually (e.g. from your own
deployment history) if order matters, or prefer the fresh-table path above.

### Removed
- `apply --all`. Applying all pending migrations is now the default; use `--step N` if you
need to control how many migrations move

## [1.0.0-rc.2]
### Added
- `migrant status` reports every managed migration's applied/pending state with summary counts,
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ percent-encoding = "2"
postgres = "0.19"
postgres-native-tls = "0.5"
serde = { version = "1", features = ["derive"] }
sha2 = "0.10"
tempfile = "3"
thiserror = "2"
toml = "0.9"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ When run interactively (without `--no-confirm`), `setup` will be run automatical

`migrant status [--format <text|json>]` - Report every managed migration's applied/pending state with summary counts, as pretty text (default) or JSON.

`migrant apply [--down, --all, --force, --fake, --no-sync]` - Apply the next available migration[s].
`migrant apply [--down, --step N, --force, --fake, --no-sync]` - Apply all pending migrations. `--down` reverses direction and applies a single step by default; `--step N` applies exactly N steps in either direction.

`migrant redo [--all, --force, --no-sync]` - Re-apply the latest migration (down then up).

Expand Down
28 changes: 28 additions & 0 deletions migrant_lib/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
# Changelog

## [Unreleased]
### Added
- `Migratable::checksum()` returns a sha256 checksum of a migration's up-SQL (`None` for
`FnMigration`), recorded in the bookkeeping table when the migration is applied
- `Migrator::allow_unknown_tags(bool)` (default `false`) lets a run proceed when the database
has an applied tag not present in the defined migration set, instead of erroring
- `Migrator::allow_out_of_order(bool)` (default `false`) lets a run apply migrations out of
their defined order, instead of erroring

### Changed
- `migrant_lib::new` is renamed to `migrant_lib::create_migration` and now returns a
`NewMigration` (with `dir()`/`up_path()`/`down_path()` accessors) instead of `()`
- `migrant_lib::list` moved to `migrant_lib::cli::list`
- `SqliteSettingsBuilder`/`PostgresSettingsBuilder`/`MySqlSettingsBuilder` setters
`database_path`/`migration_location` are now infallible: they take and return `Self` instead
of `Result<Self>`. Validation is deferred to `build()`
- `Migratable::description` and `cli::edit` now take `Direction` by value instead of by
reference
- `MigratableClone` is now a sealed trait and can no longer be implemented outside this crate
- The `__migrant_migrations` bookkeeping table is now multi-column (`id`, `tag`, `checksum`,
`applied_at`) instead of a single `tag` column. Applied order is tracked by `id` and is
authoritative; `Down` targets the most-recently-applied migration by this recorded order,
not by scanning the defined migration list. This is a breaking change to the on-disk schema;
see the `migrant` CLI changelog for an upgrade note
- A run now errors by default if the database records an applied tag absent from the defined
migration set, or if it would apply migrations out of order. Opt out with
`Migrator::allow_unknown_tags(true)` / `Migrator::allow_out_of_order(true)`

## [1.0.0-rc.2]
Breaking pre-1.0 release, continuing the API cleanup from rc.1.

Expand Down
1 change: 1 addition & 0 deletions migrant_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ chrono = { workspace = true }
log = { workspace = true }
percent-encoding = { workspace = true }
serde = { workspace = true }
sha2 = { workspace = true }
thiserror = { workspace = true }
toml = { workspace = true }
url = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion migrant_lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ config.setup()?;
Migration management identical to the [`migrant`](https://github.com/jaemk/migrant) CLI tool can also be embedded.
This method only supports file-based migrations (so `FileMigration`s or `EmbeddedMigration`s using `include_str!`)
and those migration files names must be timestamped with the format `[0-9]{14}_[a-z0-9-]+`,
Properly named files can be generated by `migrant_lib::new` or the `migrant` CLI tool.
Properly named files can be generated by `migrant_lib::create_migration` or the `migrant` CLI tool.
This is required because migration order is implied by file names which must follow
a specific format and contain a valid timestamp.

Expand Down
6 changes: 3 additions & 3 deletions migrant_lib/examples/embedded_cli_compatible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::env;
fn run() -> Result<(), Box<dyn std::error::Error>> {
let path = env::current_dir()?;
let path = path.join("db/embedded_example.db");
let settings = Settings::configure_sqlite().database_path(&path)?.build()?;
let settings = Settings::configure_sqlite().database_path(&path).build()?;

let mut config = Config::with_settings(settings);

Expand Down Expand Up @@ -65,7 +65,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
.apply()?;

let config = config.reload()?;
migrant_lib::list(&config)?;
migrant_lib::cli::list(&config)?;

println!("\nUnapplying migrations...");
Migrator::with_config(&config)
Expand All @@ -74,7 +74,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
.apply()?;

let config = config.reload()?;
migrant_lib::list(&config)?;
migrant_lib::cli::list(&config)?;
Ok(())
}

Expand Down
6 changes: 3 additions & 3 deletions migrant_lib/examples/embedded_programmable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ mod migrations {
fn run() -> Result<(), Box<dyn std::error::Error>> {
let path = env::current_dir()?;
let path = path.join("db/embedded_example.db");
let settings = Settings::configure_sqlite().database_path(&path)?.build()?;
let settings = Settings::configure_sqlite().database_path(&path).build()?;

let mut config = Config::with_settings(settings);
config.setup()?;
Expand Down Expand Up @@ -105,7 +105,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
.apply()?;

let config = config.reload()?;
migrant_lib::list(&config)?;
migrant_lib::cli::list(&config)?;

println!("\nUnapplying migrations...");
Migrator::with_config(&config)
Expand All @@ -114,7 +114,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
.apply()?;

let config = config.reload()?;
migrant_lib::list(&config)?;
migrant_lib::cli::list(&config)?;
Ok(())
}

Expand Down
8 changes: 4 additions & 4 deletions migrant_lib/examples/migrant_cli_compatible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ fn run() -> Result<(), migrant_lib::Error> {
Config::init_in(&dir)
.with_sqlite_options(
SqliteSettingsBuilder::empty()
.database_path("db/db.db")?
.migration_location("migrations/managed")?,
.database_path("db/db.db")
.migration_location("migrations/managed"),
)
// .with_postgres_options(
// PostgresSettingsBuilder::empty()
Expand Down Expand Up @@ -65,15 +65,15 @@ fn run() -> Result<(), migrant_lib::Error> {
.all(true)
.apply()?;
let config = config.reload()?;
migrant_lib::list(&config)?;
migrant_lib::cli::list(&config)?;

println!("Unapplying all migrations...");
migrant_lib::Migrator::with_config(&config)
.direction(migrant_lib::Direction::Down)
.all(true)
.apply()?;
let config = config.reload()?;
migrant_lib::list(&config)?;
migrant_lib::cli::list(&config)?;
Ok(())
}

Expand Down
8 changes: 4 additions & 4 deletions migrant_lib/examples/settings_file_programmable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
Config::init_in(&dir)
.with_sqlite_options(
SqliteSettingsBuilder::empty()
.database_path("db/db.db")?
.migration_location("migrations/managed")?,
.database_path("db/db.db")
.migration_location("migrations/managed"),
)
.interactive(false)
.initialize()?;
Expand Down Expand Up @@ -59,7 +59,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
.apply()?;

let config = config.reload()?;
migrant_lib::list(&config)?;
migrant_lib::cli::list(&config)?;

println!("\nUnapplying migrations...");
Migrator::with_config(&config)
Expand All @@ -68,7 +68,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
.apply()?;

let config = config.reload()?;
migrant_lib::list(&config)?;
migrant_lib::cli::list(&config)?;
Ok(())
}

Expand Down
Loading