mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-12-29 21:00:54 +00:00
feat: make sqlx-cli aware of database-url-var
This commit is contained in:
parent
13f6ef0ab0
commit
65ef27f70c
@ -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?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -152,25 +152,7 @@ impl Config {
|
||||
/// ### Panics
|
||||
/// If the file exists but an unrecoverable error was encountered while parsing it.
|
||||
pub fn from_crate() -> &'static Self {
|
||||
Self::try_from_crate().unwrap_or_else(|e| {
|
||||
match e {
|
||||
ConfigError::NotFound { path } => {
|
||||
// Non-fatal
|
||||
tracing::debug!("Not reading config, file {path:?} not found");
|
||||
CACHE.get_or_init(Config::default)
|
||||
}
|
||||
// FATAL ERRORS BELOW:
|
||||
// In the case of migrations,
|
||||
// we can't proceed with defaults as they may be completely wrong.
|
||||
e @ ConfigError::ParseDisabled { .. } => {
|
||||
// Only returned if the file exists but the feature is not enabled.
|
||||
panic!("{e}")
|
||||
}
|
||||
e => {
|
||||
panic!("failed to read sqlx config: {e}")
|
||||
}
|
||||
}
|
||||
})
|
||||
Self::read_with_or_default(get_crate_path)
|
||||
}
|
||||
|
||||
/// Get the cached config, or to read `$CARGO_MANIFEST_DIR/sqlx.toml`.
|
||||
@ -179,11 +161,7 @@ impl Config {
|
||||
///
|
||||
/// Errors if `CARGO_MANIFEST_DIR` is not set, or if the config file could not be read.
|
||||
pub fn try_from_crate() -> Result<&'static Self, ConfigError> {
|
||||
Self::try_get_with(|| {
|
||||
let mut path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?);
|
||||
path.push("sqlx.toml");
|
||||
Ok(path)
|
||||
})
|
||||
Self::try_read_with(get_crate_path)
|
||||
}
|
||||
|
||||
/// Get the cached config, or attempt to read `sqlx.toml` from the current working directory.
|
||||
@ -192,7 +170,7 @@ impl Config {
|
||||
///
|
||||
/// Errors if the config file does not exist, or could not be read.
|
||||
pub fn try_from_current_dir() -> Result<&'static Self, ConfigError> {
|
||||
Self::try_get_with(|| Ok("sqlx.toml".into()))
|
||||
Self::try_read_with(|| Ok("sqlx.toml".into()))
|
||||
}
|
||||
|
||||
/// Get the cached config, or attempt to read it from the path returned by the closure.
|
||||
@ -200,7 +178,7 @@ impl Config {
|
||||
/// On success, the config is cached in a `static` and returned by future calls.
|
||||
///
|
||||
/// Errors if the config file does not exist, or could not be read.
|
||||
pub fn try_get_with(
|
||||
pub fn try_read_with(
|
||||
make_path: impl FnOnce() -> Result<PathBuf, ConfigError>,
|
||||
) -> Result<&'static Self, ConfigError> {
|
||||
CACHE.get_or_try_init(|| {
|
||||
@ -209,6 +187,36 @@ impl Config {
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the cached config, or attempt to read it from the path returned by the closure.
|
||||
///
|
||||
/// On success, the config is cached in a `static` and returned by future calls.
|
||||
///
|
||||
/// Returns `Config::default()` if the file does not exist.
|
||||
pub fn read_with_or_default(
|
||||
make_path: impl FnOnce() -> Result<PathBuf, ConfigError>,
|
||||
) -> &'static Self {
|
||||
CACHE.get_or_init(|| {
|
||||
match make_path().and_then(Self::read_from) {
|
||||
Ok(config) => config,
|
||||
Err(ConfigError::NotFound { path }) => {
|
||||
// Non-fatal
|
||||
tracing::debug!("Not reading config, file {path:?} not found");
|
||||
Config::default()
|
||||
}
|
||||
// FATAL ERRORS BELOW:
|
||||
// In the case of migrations,
|
||||
// we can't proceed with defaults as they may be completely wrong.
|
||||
Err(e @ ConfigError::ParseDisabled { .. }) => {
|
||||
// Only returned if the file exists but the feature is not enabled.
|
||||
panic!("{e}")
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("failed to read sqlx config: {e}")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "sqlx-toml")]
|
||||
fn read_from(path: PathBuf) -> Result<Self, ConfigError> {
|
||||
// The `toml` crate doesn't provide an incremental reader.
|
||||
@ -238,3 +246,9 @@ impl Config {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_crate_path() -> Result<PathBuf, ConfigError> {
|
||||
let mut path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?);
|
||||
path.push("sqlx.toml");
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
@ -169,6 +169,12 @@ pub mod prelude {
|
||||
#[cfg(feature = "_unstable-doc")]
|
||||
pub use sqlx_core::config;
|
||||
|
||||
// NOTE: APIs exported in this module are SemVer-exempt.
|
||||
#[doc(hidden)]
|
||||
pub mod _unstable {
|
||||
pub use sqlx_core::config;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg_attr(
|
||||
all(feature = "chrono", feature = "time"),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user