feat: make sqlx-cli aware of database-url-var

This commit is contained in:
Austin Bonander
2024-09-20 00:46:43 -07:00
parent 13f6ef0ab0
commit 65ef27f70c
5 changed files with 148 additions and 53 deletions

View File

@@ -17,14 +17,14 @@ pub async fn create(connect_opts: &ConnectOpts) -> anyhow::Result<()> {
std::sync::atomic::Ordering::Release,
);
Any::create_database(connect_opts.required_db_url()?).await?;
Any::create_database(connect_opts.expect_db_url()?).await?;
}
Ok(())
}
pub async fn drop(connect_opts: &ConnectOpts, confirm: bool, force: bool) -> anyhow::Result<()> {
if confirm && !ask_to_continue_drop(connect_opts.required_db_url()?) {
if confirm && !ask_to_continue_drop(connect_opts.expect_db_url()?) {
return Ok(());
}
@@ -34,9 +34,9 @@ pub async fn drop(connect_opts: &ConnectOpts, confirm: bool, force: bool) -> any
if exists {
if force {
Any::force_drop_database(connect_opts.required_db_url()?).await?;
Any::force_drop_database(connect_opts.expect_db_url()?).await?;
} else {
Any::drop_database(connect_opts.required_db_url()?).await?;
Any::drop_database(connect_opts.expect_db_url()?).await?;
}
}

View File

@@ -1,7 +1,8 @@
use std::io;
use std::path::{Path, PathBuf};
use std::time::Duration;
use anyhow::Result;
use anyhow::{Context, Result};
use futures::{Future, TryFutureExt};
use sqlx::{AnyConnection, Connection};
@@ -20,7 +21,12 @@ mod prepare;
pub use crate::opt::Opt;
pub use sqlx::_unstable::config;
use crate::config::Config;
pub async fn run(opt: Opt) -> Result<()> {
let config = config_from_current_dir()?;
match opt.command {
Command::Migrate(migrate) => match migrate.command {
MigrateCommand::Add {
@@ -34,9 +40,11 @@ pub async fn run(opt: Opt) -> Result<()> {
source,
dry_run,
ignore_missing,
connect_opts,
mut connect_opts,
target_version,
} => {
connect_opts.populate_db_url(config)?;
migrate::run(
&source,
&connect_opts,
@@ -50,9 +58,11 @@ pub async fn run(opt: Opt) -> Result<()> {
source,
dry_run,
ignore_missing,
connect_opts,
mut connect_opts,
target_version,
} => {
connect_opts.populate_db_url(config)?;
migrate::revert(
&source,
&connect_opts,
@@ -64,37 +74,56 @@ pub async fn run(opt: Opt) -> Result<()> {
}
MigrateCommand::Info {
source,
connect_opts,
} => migrate::info(&source, &connect_opts).await?,
mut connect_opts,
} => {
connect_opts.populate_db_url(config)?;
migrate::info(&source, &connect_opts).await?
},
MigrateCommand::BuildScript { source, force } => migrate::build_script(&source, force)?,
},
Command::Database(database) => match database.command {
DatabaseCommand::Create { connect_opts } => database::create(&connect_opts).await?,
DatabaseCommand::Create { mut connect_opts } => {
connect_opts.populate_db_url(config)?;
database::create(&connect_opts).await?
},
DatabaseCommand::Drop {
confirmation,
connect_opts,
mut connect_opts,
force,
} => database::drop(&connect_opts, !confirmation.yes, force).await?,
} => {
connect_opts.populate_db_url(config)?;
database::drop(&connect_opts, !confirmation.yes, force).await?
},
DatabaseCommand::Reset {
confirmation,
source,
connect_opts,
mut connect_opts,
force,
} => database::reset(&source, &connect_opts, !confirmation.yes, force).await?,
} => {
connect_opts.populate_db_url(config)?;
database::reset(&source, &connect_opts, !confirmation.yes, force).await?
},
DatabaseCommand::Setup {
source,
connect_opts,
} => database::setup(&source, &connect_opts).await?,
mut connect_opts,
} => {
connect_opts.populate_db_url(config)?;
database::setup(&source, &connect_opts).await?
},
},
Command::Prepare {
check,
all,
workspace,
connect_opts,
mut connect_opts,
args,
} => prepare::run(check, all, workspace, connect_opts, args).await?,
} => {
connect_opts.populate_db_url(config)?;
prepare::run(check, all, workspace, connect_opts, args).await?
},
#[cfg(feature = "completions")]
Command::Completions { shell } => completions::run(shell),
@@ -122,7 +151,7 @@ where
{
sqlx::any::install_default_drivers();
let db_url = opts.required_db_url()?;
let db_url = opts.expect_db_url()?;
backoff::future::retry(
backoff::ExponentialBackoffBuilder::new()
@@ -147,3 +176,18 @@ where
)
.await
}
async fn config_from_current_dir() -> anyhow::Result<&'static Config> {
// Tokio does file I/O on a background task anyway
tokio::task::spawn_blocking(|| {
let path = PathBuf::from("sqlx.toml");
if path.exists() {
eprintln!("Found `sqlx.toml` in current directory; reading...");
}
Config::read_with_or_default(move || Ok(path))
})
.await
.context("unexpected error loading config")
}

View File

@@ -1,8 +1,10 @@
use std::env;
use std::ops::{Deref, Not};
use anyhow::Context;
use clap::{Args, Parser};
#[cfg(feature = "completions")]
use clap_complete::Shell;
use sqlx::config::Config;
#[derive(Parser, Debug)]
#[clap(version, about, author)]
@@ -242,7 +244,7 @@ impl Deref for Source {
#[derive(Args, Debug)]
pub struct ConnectOpts {
/// Location of the DB, by default will be read from the DATABASE_URL env var or `.env` files.
#[clap(long, short = 'D', env)]
#[clap(long, short = 'D')]
pub database_url: Option<String>,
/// The maximum time, in seconds, to try connecting to the database server before
@@ -266,12 +268,41 @@ pub struct ConnectOpts {
impl ConnectOpts {
/// Require a database URL to be provided, otherwise
/// return an error.
pub fn required_db_url(&self) -> anyhow::Result<&str> {
self.database_url.as_deref().ok_or_else(
|| anyhow::anyhow!(
"the `--database-url` option or the `DATABASE_URL` environment variable must be provided"
)
)
pub fn expect_db_url(&self) -> anyhow::Result<&str> {
self.database_url.as_deref().context("BUG: database_url not populated")
}
/// Populate `database_url` from the environment, if not set.
pub fn populate_db_url(&mut self, config: &Config) -> anyhow::Result<()> {
if self.database_url.is_some() {
return Ok(());
}
let var = config.common.database_url_var();
let context = if var != "DATABASE_URL" {
" (`common.database-url-var` in `sqlx.toml`)"
} else {
""
};
match env::var(var) {
Ok(url) => {
if !context.is_empty() {
eprintln!("Read database url from `{var}`{context}");
}
self.database_url = Some(url)
},
Err(env::VarError::NotPresent) => {
anyhow::bail!("`--database-url` or `{var}`{context} must be set")
}
Err(env::VarError::NotUnicode(_)) => {
anyhow::bail!("`{var}`{context} is not valid UTF-8");
}
}
Ok(())
}
}