Skip to content

Commit 6c70021

Browse files
committed
Allow specifying multiple migration sources in CLI
1 parent 45b5b61 commit 6c70021

File tree

4 files changed

+71
-18
lines changed

4 files changed

+71
-18
lines changed

sqlx-cli/src/database.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,18 @@ pub async fn drop(connect_opts: &ConnectOpts, confirm: bool, force: bool) -> any
4444
}
4545

4646
pub async fn reset(
47-
migration_source: &str,
47+
migration_sources: &[String],
4848
connect_opts: &ConnectOpts,
4949
confirm: bool,
5050
force: bool,
5151
) -> anyhow::Result<()> {
5252
drop(connect_opts, confirm, force).await?;
53-
setup(migration_source, connect_opts).await
53+
setup(migration_sources, connect_opts).await
5454
}
5555

56-
pub async fn setup(migration_source: &str, connect_opts: &ConnectOpts) -> anyhow::Result<()> {
56+
pub async fn setup(migration_sources: &[String], connect_opts: &ConnectOpts) -> anyhow::Result<()> {
5757
create(connect_opts).await?;
58-
migrate::run(migration_source, connect_opts, false, false, None).await
58+
migrate::run(migration_sources, connect_opts, false, false, None).await
5959
}
6060

6161
fn ask_to_continue_drop(db_url: &str) -> bool {

sqlx-cli/src/migrate.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,12 @@ fn short_checksum(checksum: &[u8]) -> String {
192192
s
193193
}
194194

195-
pub async fn info(migration_source: &str, connect_opts: &ConnectOpts) -> anyhow::Result<()> {
196-
let migrator = Migrator::new(Path::new(migration_source)).await?;
195+
pub async fn info(migration_sources: &[String], connect_opts: &ConnectOpts) -> anyhow::Result<()> {
196+
let paths: Vec<_> = migration_sources
197+
.iter()
198+
.map(|source| Path::new(source))
199+
.collect();
200+
let migrator = Migrator::new(paths).await?;
197201
let mut conn = crate::connect(&connect_opts).await?;
198202

199203
conn.ensure_migrations_table().await?;
@@ -272,13 +276,17 @@ fn validate_applied_migrations(
272276
}
273277

274278
pub async fn run(
275-
migration_source: &str,
279+
migration_sources: &[String],
276280
connect_opts: &ConnectOpts,
277281
dry_run: bool,
278282
ignore_missing: bool,
279283
target_version: Option<i64>,
280284
) -> anyhow::Result<()> {
281-
let migrator = Migrator::new(Path::new(migration_source)).await?;
285+
let paths: Vec<_> = migration_sources
286+
.iter()
287+
.map(|source| Path::new(source))
288+
.collect();
289+
let migrator = Migrator::new(paths).await?;
282290
if let Some(target_version) = target_version {
283291
if !migrator.version_exists(target_version) {
284292
bail!(MigrateError::VersionNotPresent(target_version));
@@ -367,13 +375,17 @@ pub async fn run(
367375
}
368376

369377
pub async fn revert(
370-
migration_source: &str,
378+
migration_sources: &[String],
371379
connect_opts: &ConnectOpts,
372380
dry_run: bool,
373381
ignore_missing: bool,
374382
target_version: Option<i64>,
375383
) -> anyhow::Result<()> {
376-
let migrator = Migrator::new(Path::new(migration_source)).await?;
384+
let paths: Vec<_> = migration_sources
385+
.iter()
386+
.map(|source| Path::new(source))
387+
.collect();
388+
let migrator = Migrator::new(paths).await?;
377389
if let Some(target_version) = target_version {
378390
if target_version != 0 && !migrator.version_exists(target_version) {
379391
bail!(MigrateError::VersionNotPresent(target_version));
@@ -461,7 +473,7 @@ pub async fn revert(
461473
Ok(())
462474
}
463475

464-
pub fn build_script(migration_source: &str, force: bool) -> anyhow::Result<()> {
476+
pub fn build_script(migration_sources: &[String], force: bool) -> anyhow::Result<()> {
465477
anyhow::ensure!(
466478
Path::new("Cargo.toml").exists(),
467479
"must be run in a Cargo project root"
@@ -472,11 +484,17 @@ pub fn build_script(migration_source: &str, force: bool) -> anyhow::Result<()> {
472484
"build.rs already exists; use --force to overwrite"
473485
);
474486

487+
let instructions = migration_sources
488+
.iter()
489+
.map(|source| format!(r#"println!("cargo:rerun-if-changed={source}");"#))
490+
.collect::<Vec<_>>()
491+
.join("\n ");
492+
475493
let contents = format!(
476494
r#"// generated by `sqlx migrate build-script`
477495
fn main() {{
478496
// trigger recompilation when a new migration is added
479-
println!("cargo:rerun-if-changed={migration_source}");
497+
{instructions}
480498
}}"#,
481499
);
482500

sqlx-cli/src/opt.rs

+22-6
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ pub enum DatabaseCommand {
8888
confirmation: Confirmation,
8989

9090
#[clap(flatten)]
91-
source: Source,
91+
source: Sources,
9292

9393
#[clap(flatten)]
9494
connect_opts: ConnectOpts,
@@ -101,7 +101,7 @@ pub enum DatabaseCommand {
101101
/// Creates the database specified in your DATABASE_URL and runs any pending migrations.
102102
Setup {
103103
#[clap(flatten)]
104-
source: Source,
104+
source: Sources,
105105

106106
#[clap(flatten)]
107107
connect_opts: ConnectOpts,
@@ -156,7 +156,7 @@ pub enum MigrateCommand {
156156
/// Run all pending migrations.
157157
Run {
158158
#[clap(flatten)]
159-
source: Source,
159+
source: Sources,
160160

161161
/// List all the migrations to be run without applying
162162
#[clap(long)]
@@ -177,7 +177,7 @@ pub enum MigrateCommand {
177177
/// Revert the latest migration with a down file.
178178
Revert {
179179
#[clap(flatten)]
180-
source: Source,
180+
source: Sources,
181181

182182
/// List the migration to be reverted without applying
183183
#[clap(long)]
@@ -199,7 +199,7 @@ pub enum MigrateCommand {
199199
/// List all available migrations.
200200
Info {
201201
#[clap(flatten)]
202-
source: Source,
202+
source: Sources,
203203

204204
#[clap(flatten)]
205205
connect_opts: ConnectOpts,
@@ -210,7 +210,7 @@ pub enum MigrateCommand {
210210
/// Must be run in a Cargo project root.
211211
BuildScript {
212212
#[clap(flatten)]
213-
source: Source,
213+
source: Sources,
214214

215215
/// Overwrite the build script if it already exists.
216216
#[clap(long)]
@@ -234,6 +234,22 @@ impl Deref for Source {
234234
}
235235
}
236236

237+
/// Argument for the migration scripts sources.
238+
#[derive(Args, Debug)]
239+
pub struct Sources {
240+
/// Paths to folders containing migrations.
241+
#[clap(long, default_values_t = vec!["migrations".to_owned()])]
242+
source: Vec<String>,
243+
}
244+
245+
impl Deref for Sources {
246+
type Target = Vec<String>;
247+
248+
fn deref(&self) -> &Self::Target {
249+
&self.source
250+
}
251+
}
252+
237253
/// Argument for the database URL.
238254
#[derive(Args, Debug)]
239255
pub struct ConnectOpts {

sqlx-core/src/migrate/source.rs

+19
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::error::BoxDynError;
22
use crate::migrate::{Migration, MigrationType};
33
use futures_core::future::BoxFuture;
44

5+
use futures_util::future;
56
use std::borrow::Cow;
67
use std::fmt::Debug;
78
use std::fs;
@@ -44,6 +45,24 @@ impl MigrationSource<'static> for PathBuf {
4445
}
4546
}
4647

48+
impl<'s, S: MigrationSource<'s> + Send + 's> MigrationSource<'s> for Vec<S> {
49+
fn resolve(self) -> BoxFuture<'s, Result<Vec<Migration>, BoxDynError>> {
50+
Box::pin(async move {
51+
let migration_sets: Vec<_> =
52+
future::join_all(self.into_iter().map(MigrationSource::resolve))
53+
.await
54+
.into_iter()
55+
.collect::<Result<_, _>>()?;
56+
57+
// Merge migration sets by version in ascending order
58+
let mut migrations: Vec<_> = migration_sets.into_iter().flatten().collect();
59+
migrations.sort_by_key(|migration| migration.version);
60+
61+
Ok(migrations)
62+
})
63+
}
64+
}
65+
4766
#[derive(thiserror::Error, Debug)]
4867
#[error("{message}")]
4968
pub struct ResolveError {

0 commit comments

Comments
 (0)