Skip to content

Commit

Permalink
feat: Add only_up config field to prevent reversing migrations (#205)
Browse files Browse the repository at this point in the history
  • Loading branch information
jdkaplan authored Oct 7, 2024
1 parent d5c90bf commit 328c06f
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 5 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ migrations_dir = "migrations"
#
# Default: (unset) (use the embedded default migration templates)
templates_dir = ".squill/templates"

# Whether only up migrations should be allowed. This can be used to avoid
# accidental data loss in shared environments.
#
# Default: false (allow down migrations)
only_up = true
```

Then, generate the first migration that sets up Squill's requirements:
Expand Down
7 changes: 5 additions & 2 deletions squill-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,13 @@ fn extract(fig: Figment) -> anyhow::Result<Config> {
None
};

let only_up: bool = extract_inner_or_default(&fig, "only_up")?;

Ok(Config {
database_connect_options,
migrations_dir: migrations_dir.relative(),
templates_dir: templates_dir.map(|dir| dir.relative()),
only_up,
})
}

Expand Down Expand Up @@ -410,7 +413,7 @@ async fn undo(config: &Config) -> anyhow::Result<()> {
let mut conn = config.connect().await?;

println!("Running down migration: {}", migration);
migration.down(&mut conn).await?;
migration.down(&mut conn, config.only_up).await?;

Ok(())
}
Expand All @@ -434,7 +437,7 @@ pub async fn redo(config: &Config) -> anyhow::Result<()> {
let mut conn = config.connect().await?;

println!("Running down migration: {}", migration);
migration.down(&mut conn).await?;
migration.down(&mut conn, config.only_up).await?;

println!("Running up migration: {}", migration);
migration.up(&mut conn).await?;
Expand Down
3 changes: 3 additions & 0 deletions squill/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ pub struct Config {

pub migrations_dir: PathBuf,
pub templates_dir: Option<PathBuf>,

/// Only allow up migrations to run.
pub only_up: bool,
}

impl Config {
Expand Down
12 changes: 11 additions & 1 deletion squill/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,18 @@ mod tests {
let last = status.applied.last().unwrap();
let last = status.available.get(last.id).unwrap();

// Pretend that only_up was set by default.
let mut conn = config.connect().await.unwrap();
last.down(&mut conn).await.unwrap();
match last.down(&mut conn, true).await {
Err(MigrateError::OnlyUp) => (),

Err(err) => panic!("Unexpected error: {:?}", err),
Ok(_) => panic!("Unexpected success"),
}

// Now unset only_up to allow the reversal.
let mut conn = config.connect().await.unwrap();
last.down(&mut conn, false).await.unwrap();

// Make sure the right tables exist
let mut conn = config.connect().await.unwrap();
Expand Down
10 changes: 8 additions & 2 deletions squill/src/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,11 @@ impl MigrationDirectory {
Ok(())
}

// TODO: Add some sort of "forward-only" flag that prevents down migrations.
pub async fn down(&self, conn: &mut PgConnection) -> Result<(), MigrateError> {
pub async fn down(&self, conn: &mut PgConnection, only_up: bool) -> Result<(), MigrateError> {
if only_up {
return Err(MigrateError::OnlyUp);
}

let sql = std::fs::read_to_string(&self.down_path).map_err(|err| MigrateError::Read {
path: self.down_path.to_path_buf(),
err,
Expand Down Expand Up @@ -218,6 +221,9 @@ pub enum MigrateError {

#[error("failed to execute migration: {0}")]
Execute(sqlx::Error),

#[error("cannot execute down migration: not allowed with only_up")]
OnlyUp,
}

#[cfg(test)]
Expand Down
1 change: 1 addition & 0 deletions squill/src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ impl TestEnv {
database_connect_options: Some(self.database.connect_options.clone()),
migrations_dir: self.migrations_dir.path().into(),
templates_dir: Some(self.templates_dir.path().into()),
only_up: true,
}
}
}
Expand Down

0 comments on commit 328c06f

Please sign in to comment.