Skip to content

Commit 328c06f

Browse files
authored
feat: Add only_up config field to prevent reversing migrations (#205)
1 parent d5c90bf commit 328c06f

File tree

6 files changed

+34
-5
lines changed

6 files changed

+34
-5
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ migrations_dir = "migrations"
6565
#
6666
# Default: (unset) (use the embedded default migration templates)
6767
templates_dir = ".squill/templates"
68+
69+
# Whether only up migrations should be allowed. This can be used to avoid
70+
# accidental data loss in shared environments.
71+
#
72+
# Default: false (allow down migrations)
73+
only_up = true
6874
```
6975

7076
Then, generate the first migration that sets up Squill's requirements:

squill-cli/src/main.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,13 @@ fn extract(fig: Figment) -> anyhow::Result<Config> {
135135
None
136136
};
137137

138+
let only_up: bool = extract_inner_or_default(&fig, "only_up")?;
139+
138140
Ok(Config {
139141
database_connect_options,
140142
migrations_dir: migrations_dir.relative(),
141143
templates_dir: templates_dir.map(|dir| dir.relative()),
144+
only_up,
142145
})
143146
}
144147

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

412415
println!("Running down migration: {}", migration);
413-
migration.down(&mut conn).await?;
416+
migration.down(&mut conn, config.only_up).await?;
414417

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

436439
println!("Running down migration: {}", migration);
437-
migration.down(&mut conn).await?;
440+
migration.down(&mut conn, config.only_up).await?;
438441

439442
println!("Running up migration: {}", migration);
440443
migration.up(&mut conn).await?;

squill/src/config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ pub struct Config {
88

99
pub migrations_dir: PathBuf,
1010
pub templates_dir: Option<PathBuf>,
11+
12+
/// Only allow up migrations to run.
13+
pub only_up: bool,
1114
}
1215

1316
impl Config {

squill/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,8 +336,18 @@ mod tests {
336336
let last = status.applied.last().unwrap();
337337
let last = status.available.get(last.id).unwrap();
338338

339+
// Pretend that only_up was set by default.
339340
let mut conn = config.connect().await.unwrap();
340-
last.down(&mut conn).await.unwrap();
341+
match last.down(&mut conn, true).await {
342+
Err(MigrateError::OnlyUp) => (),
343+
344+
Err(err) => panic!("Unexpected error: {:?}", err),
345+
Ok(_) => panic!("Unexpected success"),
346+
}
347+
348+
// Now unset only_up to allow the reversal.
349+
let mut conn = config.connect().await.unwrap();
350+
last.down(&mut conn, false).await.unwrap();
341351

342352
// Make sure the right tables exist
343353
let mut conn = config.connect().await.unwrap();

squill/src/migrate.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,11 @@ impl MigrationDirectory {
185185
Ok(())
186186
}
187187

188-
// TODO: Add some sort of "forward-only" flag that prevents down migrations.
189-
pub async fn down(&self, conn: &mut PgConnection) -> Result<(), MigrateError> {
188+
pub async fn down(&self, conn: &mut PgConnection, only_up: bool) -> Result<(), MigrateError> {
189+
if only_up {
190+
return Err(MigrateError::OnlyUp);
191+
}
192+
190193
let sql = std::fs::read_to_string(&self.down_path).map_err(|err| MigrateError::Read {
191194
path: self.down_path.to_path_buf(),
192195
err,
@@ -218,6 +221,9 @@ pub enum MigrateError {
218221

219222
#[error("failed to execute migration: {0}")]
220223
Execute(sqlx::Error),
224+
225+
#[error("cannot execute down migration: not allowed with only_up")]
226+
OnlyUp,
221227
}
222228

223229
#[cfg(test)]

squill/src/testing.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ impl TestEnv {
4747
database_connect_options: Some(self.database.connect_options.clone()),
4848
migrations_dir: self.migrations_dir.path().into(),
4949
templates_dir: Some(self.templates_dir.path().into()),
50+
only_up: true,
5051
}
5152
}
5253
}

0 commit comments

Comments
 (0)