mirror of
https://github.com/launchbadge/sqlx.git
synced 2026-03-19 08:39:44 +00:00
feat: teach sqlx-cli about migrate.migrations-dir
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use crate::migrate;
|
||||
use crate::opt::ConnectOpts;
|
||||
use crate::{migrate, Config};
|
||||
use crate::opt::{ConnectOpts, MigrationSourceOpt};
|
||||
use console::style;
|
||||
use promptly::{prompt, ReadlineError};
|
||||
use sqlx::any::Any;
|
||||
@@ -44,18 +44,19 @@ pub async fn drop(connect_opts: &ConnectOpts, confirm: bool, force: bool) -> any
|
||||
}
|
||||
|
||||
pub async fn reset(
|
||||
migration_source: &str,
|
||||
config: &Config,
|
||||
migration_source: &MigrationSourceOpt,
|
||||
connect_opts: &ConnectOpts,
|
||||
confirm: bool,
|
||||
force: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
drop(connect_opts, confirm, force).await?;
|
||||
setup(migration_source, connect_opts).await
|
||||
setup(config, migration_source, connect_opts).await
|
||||
}
|
||||
|
||||
pub async fn setup(migration_source: &str, connect_opts: &ConnectOpts) -> anyhow::Result<()> {
|
||||
pub async fn setup(config: &Config, migration_source: &MigrationSourceOpt, connect_opts: &ConnectOpts) -> anyhow::Result<()> {
|
||||
create(connect_opts).await?;
|
||||
migrate::run(migration_source, connect_opts, false, false, None).await
|
||||
migrate::run(config, migration_source, connect_opts, false, false, None).await
|
||||
}
|
||||
|
||||
fn ask_to_continue_drop(db_url: &str) -> bool {
|
||||
|
||||
@@ -39,6 +39,7 @@ pub async fn run(opt: Opt) -> Result<()> {
|
||||
connect_opts.populate_db_url(config)?;
|
||||
|
||||
migrate::run(
|
||||
config,
|
||||
&source,
|
||||
&connect_opts,
|
||||
dry_run,
|
||||
@@ -57,6 +58,7 @@ pub async fn run(opt: Opt) -> Result<()> {
|
||||
connect_opts.populate_db_url(config)?;
|
||||
|
||||
migrate::revert(
|
||||
config,
|
||||
&source,
|
||||
&connect_opts,
|
||||
dry_run,
|
||||
@@ -71,9 +73,9 @@ pub async fn run(opt: Opt) -> Result<()> {
|
||||
} => {
|
||||
connect_opts.populate_db_url(config)?;
|
||||
|
||||
migrate::info(&source, &connect_opts).await?
|
||||
migrate::info(config, &source, &connect_opts).await?
|
||||
},
|
||||
MigrateCommand::BuildScript { source, force } => migrate::build_script(&source, force)?,
|
||||
MigrateCommand::BuildScript { source, force } => migrate::build_script(config, &source, force)?,
|
||||
},
|
||||
|
||||
Command::Database(database) => match database.command {
|
||||
@@ -96,14 +98,14 @@ pub async fn run(opt: Opt) -> Result<()> {
|
||||
force,
|
||||
} => {
|
||||
connect_opts.populate_db_url(config)?;
|
||||
database::reset(&source, &connect_opts, !confirmation.yes, force).await?
|
||||
database::reset(config, &source, &connect_opts, !confirmation.yes, force).await?
|
||||
},
|
||||
DatabaseCommand::Setup {
|
||||
source,
|
||||
mut connect_opts,
|
||||
} => {
|
||||
connect_opts.populate_db_url(config)?;
|
||||
database::setup(&source, &connect_opts).await?
|
||||
database::setup(config, &source, &connect_opts).await?
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::opt::{AddMigrationOpts, ConnectOpts};
|
||||
use crate::opt::{AddMigrationOpts, ConnectOpts, MigrationSourceOpt};
|
||||
use anyhow::{bail, Context};
|
||||
use console::style;
|
||||
use sqlx::migrate::{AppliedMigration, Migrate, MigrateError, MigrationType, Migrator};
|
||||
use sqlx::migrate::{AppliedMigration, Migrate, MigrateError, MigrationType, Migrator, ResolveWith};
|
||||
use sqlx::Connection;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
@@ -11,58 +11,34 @@ use std::path::Path;
|
||||
use std::time::Duration;
|
||||
use crate::config::Config;
|
||||
|
||||
fn create_file(
|
||||
migration_source: &str,
|
||||
file_prefix: &str,
|
||||
description: &str,
|
||||
migration_type: MigrationType,
|
||||
) -> anyhow::Result<()> {
|
||||
use std::path::PathBuf;
|
||||
|
||||
let mut file_name = file_prefix.to_string();
|
||||
file_name.push('_');
|
||||
file_name.push_str(&description.replace(' ', "_"));
|
||||
file_name.push_str(migration_type.suffix());
|
||||
|
||||
let mut path = PathBuf::new();
|
||||
path.push(migration_source);
|
||||
path.push(&file_name);
|
||||
|
||||
println!("Creating {}", style(path.display()).cyan());
|
||||
|
||||
let mut file = File::create(&path).context("Failed to create migration file")?;
|
||||
|
||||
std::io::Write::write_all(&mut file, migration_type.file_content().as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add(
|
||||
config: &Config,
|
||||
opts: AddMigrationOpts,
|
||||
) -> anyhow::Result<()> {
|
||||
fs::create_dir_all(&opts.source).context("Unable to create migrations directory")?;
|
||||
let source = opts.source.resolve(config);
|
||||
|
||||
fs::create_dir_all(source).context("Unable to create migrations directory")?;
|
||||
|
||||
let migrator = Migrator::new(opts.source.as_ref()).await?;
|
||||
let migrator = Migrator::new(Path::new(source)).await?;
|
||||
|
||||
let version_prefix = opts.version_prefix(config, &migrator);
|
||||
|
||||
if opts.reversible(config, &migrator) {
|
||||
create_file(
|
||||
&opts.source,
|
||||
source,
|
||||
&version_prefix,
|
||||
&opts.description,
|
||||
MigrationType::ReversibleUp,
|
||||
)?;
|
||||
create_file(
|
||||
&opts.source,
|
||||
source,
|
||||
&version_prefix,
|
||||
&opts.description,
|
||||
MigrationType::ReversibleDown,
|
||||
)?;
|
||||
} else {
|
||||
create_file(
|
||||
&opts.source,
|
||||
source,
|
||||
&version_prefix,
|
||||
&opts.description,
|
||||
MigrationType::Simple,
|
||||
@@ -70,13 +46,13 @@ pub async fn add(
|
||||
}
|
||||
|
||||
// if the migrations directory is empty
|
||||
let has_existing_migrations = fs::read_dir(&opts.source)
|
||||
let has_existing_migrations = fs::read_dir(source)
|
||||
.map(|mut dir| dir.next().is_some())
|
||||
.unwrap_or(false);
|
||||
|
||||
if !has_existing_migrations {
|
||||
let quoted_source = if *opts.source != "migrations" {
|
||||
format!("{:?}", *opts.source)
|
||||
let quoted_source = if opts.source.source.is_some() {
|
||||
format!("{source:?}")
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
@@ -114,6 +90,32 @@ See: https://docs.rs/sqlx/{version}/sqlx/macro.migrate.html
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_file(
|
||||
migration_source: &str,
|
||||
file_prefix: &str,
|
||||
description: &str,
|
||||
migration_type: MigrationType,
|
||||
) -> anyhow::Result<()> {
|
||||
use std::path::PathBuf;
|
||||
|
||||
let mut file_name = file_prefix.to_string();
|
||||
file_name.push('_');
|
||||
file_name.push_str(&description.replace(' ', "_"));
|
||||
file_name.push_str(migration_type.suffix());
|
||||
|
||||
let mut path = PathBuf::new();
|
||||
path.push(migration_source);
|
||||
path.push(&file_name);
|
||||
|
||||
println!("Creating {}", style(path.display()).cyan());
|
||||
|
||||
let mut file = File::create(&path).context("Failed to create migration file")?;
|
||||
|
||||
std::io::Write::write_all(&mut file, migration_type.file_content().as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn short_checksum(checksum: &[u8]) -> String {
|
||||
let mut s = String::with_capacity(checksum.len() * 2);
|
||||
for b in checksum {
|
||||
@@ -122,8 +124,10 @@ fn short_checksum(checksum: &[u8]) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
pub async fn info(migration_source: &str, connect_opts: &ConnectOpts) -> anyhow::Result<()> {
|
||||
let migrator = Migrator::new(Path::new(migration_source)).await?;
|
||||
pub async fn info(config: &Config, migration_source: &MigrationSourceOpt, connect_opts: &ConnectOpts) -> anyhow::Result<()> {
|
||||
let source = migration_source.resolve(config);
|
||||
|
||||
let migrator = Migrator::new(ResolveWith(Path::new(source), config.migrate.to_resolve_config())).await?;
|
||||
let mut conn = crate::connect(connect_opts).await?;
|
||||
|
||||
conn.ensure_migrations_table().await?;
|
||||
@@ -202,13 +206,16 @@ fn validate_applied_migrations(
|
||||
}
|
||||
|
||||
pub async fn run(
|
||||
migration_source: &str,
|
||||
config: &Config,
|
||||
migration_source: &MigrationSourceOpt,
|
||||
connect_opts: &ConnectOpts,
|
||||
dry_run: bool,
|
||||
ignore_missing: bool,
|
||||
target_version: Option<i64>,
|
||||
) -> anyhow::Result<()> {
|
||||
let migrator = Migrator::new(Path::new(migration_source)).await?;
|
||||
let source = migration_source.resolve(config);
|
||||
|
||||
let migrator = Migrator::new(Path::new(source)).await?;
|
||||
if let Some(target_version) = target_version {
|
||||
if !migrator.version_exists(target_version) {
|
||||
bail!(MigrateError::VersionNotPresent(target_version));
|
||||
@@ -295,13 +302,15 @@ pub async fn run(
|
||||
}
|
||||
|
||||
pub async fn revert(
|
||||
migration_source: &str,
|
||||
config: &Config,
|
||||
migration_source: &MigrationSourceOpt,
|
||||
connect_opts: &ConnectOpts,
|
||||
dry_run: bool,
|
||||
ignore_missing: bool,
|
||||
target_version: Option<i64>,
|
||||
) -> anyhow::Result<()> {
|
||||
let migrator = Migrator::new(Path::new(migration_source)).await?;
|
||||
let source = migration_source.resolve(config);
|
||||
let migrator = Migrator::new(Path::new(source)).await?;
|
||||
if let Some(target_version) = target_version {
|
||||
if target_version != 0 && !migrator.version_exists(target_version) {
|
||||
bail!(MigrateError::VersionNotPresent(target_version));
|
||||
@@ -388,7 +397,9 @@ pub async fn revert(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn build_script(migration_source: &str, force: bool) -> anyhow::Result<()> {
|
||||
pub fn build_script(config: &Config, migration_source: &MigrationSourceOpt, force: bool) -> anyhow::Result<()> {
|
||||
let source = migration_source.resolve(config);
|
||||
|
||||
anyhow::ensure!(
|
||||
Path::new("Cargo.toml").exists(),
|
||||
"must be run in a Cargo project root"
|
||||
@@ -403,7 +414,7 @@ pub fn build_script(migration_source: &str, force: bool) -> anyhow::Result<()> {
|
||||
r#"// generated by `sqlx migrate build-script`
|
||||
fn main() {{
|
||||
// trigger recompilation when a new migration is added
|
||||
println!("cargo:rerun-if-changed={migration_source}");
|
||||
println!("cargo:rerun-if-changed={source}");
|
||||
}}
|
||||
"#,
|
||||
);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::env;
|
||||
use std::ops::{Deref, Not};
|
||||
use std::path::Path;
|
||||
use anyhow::Context;
|
||||
use chrono::Utc;
|
||||
use clap::{Args, Parser};
|
||||
@@ -98,7 +97,7 @@ pub enum DatabaseCommand {
|
||||
confirmation: Confirmation,
|
||||
|
||||
#[clap(flatten)]
|
||||
source: Source,
|
||||
source: MigrationSourceOpt,
|
||||
|
||||
#[clap(flatten)]
|
||||
connect_opts: ConnectOpts,
|
||||
@@ -111,7 +110,7 @@ pub enum DatabaseCommand {
|
||||
/// Creates the database specified in your DATABASE_URL and runs any pending migrations.
|
||||
Setup {
|
||||
#[clap(flatten)]
|
||||
source: Source,
|
||||
source: MigrationSourceOpt,
|
||||
|
||||
#[clap(flatten)]
|
||||
connect_opts: ConnectOpts,
|
||||
@@ -197,7 +196,7 @@ pub enum MigrateCommand {
|
||||
/// Run all pending migrations.
|
||||
Run {
|
||||
#[clap(flatten)]
|
||||
source: Source,
|
||||
source: MigrationSourceOpt,
|
||||
|
||||
/// List all the migrations to be run without applying
|
||||
#[clap(long)]
|
||||
@@ -218,7 +217,7 @@ pub enum MigrateCommand {
|
||||
/// Revert the latest migration with a down file.
|
||||
Revert {
|
||||
#[clap(flatten)]
|
||||
source: Source,
|
||||
source: MigrationSourceOpt,
|
||||
|
||||
/// List the migration to be reverted without applying
|
||||
#[clap(long)]
|
||||
@@ -240,7 +239,7 @@ pub enum MigrateCommand {
|
||||
/// List all available migrations.
|
||||
Info {
|
||||
#[clap(flatten)]
|
||||
source: Source,
|
||||
source: MigrationSourceOpt,
|
||||
|
||||
#[clap(flatten)]
|
||||
connect_opts: ConnectOpts,
|
||||
@@ -251,7 +250,7 @@ pub enum MigrateCommand {
|
||||
/// Must be run in a Cargo project root.
|
||||
BuildScript {
|
||||
#[clap(flatten)]
|
||||
source: Source,
|
||||
source: MigrationSourceOpt,
|
||||
|
||||
/// Overwrite the build script if it already exists.
|
||||
#[clap(long)]
|
||||
@@ -264,7 +263,7 @@ pub struct AddMigrationOpts {
|
||||
pub description: String,
|
||||
|
||||
#[clap(flatten)]
|
||||
pub source: Source,
|
||||
pub source: MigrationSourceOpt,
|
||||
|
||||
/// If set, create an up-migration only. Conflicts with `--reversible`.
|
||||
#[clap(long, conflicts_with = "reversible")]
|
||||
@@ -289,23 +288,21 @@ pub struct AddMigrationOpts {
|
||||
|
||||
/// Argument for the migration scripts source.
|
||||
#[derive(Args, Debug)]
|
||||
pub struct Source {
|
||||
pub struct MigrationSourceOpt {
|
||||
/// Path to folder containing migrations.
|
||||
#[clap(long, default_value = "migrations")]
|
||||
source: String,
|
||||
///
|
||||
/// Defaults to `migrations/` if not specified, but a different default may be set by `sqlx.toml`.
|
||||
#[clap(long)]
|
||||
pub source: Option<String>,
|
||||
}
|
||||
|
||||
impl Deref for Source {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.source
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for Source {
|
||||
fn as_ref(&self) -> &Path {
|
||||
Path::new(&self.source)
|
||||
impl MigrationSourceOpt {
|
||||
pub fn resolve<'a>(&'a self, config: &'a Config) -> &'a str {
|
||||
if let Some(source) = &self.source {
|
||||
return source;
|
||||
}
|
||||
|
||||
config.migrate.migrations_dir()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user