diff --git a/Cargo.lock b/Cargo.lock index d517ee08..0e27728d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -268,6 +268,18 @@ dependencies = [ "url", ] +[[package]] +name = "git2-curl" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8dcabbc09ece4d30a9aa983d5804203b7e2f8054a171f792deff59b56d31fa" +dependencies = [ + "curl", + "git2", + "log", + "url", +] + [[package]] name = "globset" version = "0.4.16" @@ -294,16 +306,13 @@ dependencies = [ [[package]] name = "goldie" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa70c42797cac60e6182e00f33f629212e02ba80d67e8a976f6168b57568d78e" +checksum = "68acc88a75803d1de7d2dbda0a2db67e8e70b689fe2c3720494be16e670cd5c9" dependencies = [ - "anyhow", - "once_cell", "pretty_assertions", "serde", - "serde_json", - "upon 0.8.1", + "upon", "yansi", ] @@ -472,12 +481,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - [[package]] name = "jobserver" version = "0.1.33" @@ -724,12 +727,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - [[package]] name = "same-file" version = "1.0.6" @@ -768,18 +765,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serde_json" -version = "1.0.141" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - [[package]] name = "serde_spanned" version = "1.0.0" @@ -801,6 +786,7 @@ dependencies = [ "curl", "fmutex", "git2", + "git2-curl", "globwalk", "goldie", "home", @@ -815,7 +801,7 @@ dependencies = [ "thiserror", "toml", "toml_edit", - "upon 0.10.0", + "upon", "url", "walkdir", "which", @@ -981,20 +967,9 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "upon" -version = "0.8.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fe29601d1624f104fa9a35ea71a5f523dd8bd1cfc8c31f8124ad2b829f013c0" -dependencies = [ - "serde", - "unicode-ident", - "unicode-width", -] +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "upon" @@ -1004,6 +979,8 @@ checksum = "f3ead40aa15464f4d808014183fa0b030761ff6f57e162f7fc76d6a900df7a28" dependencies = [ "aho-corasick", "serde", + "unicode-ident", + "unicode-width", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 245f6230..a93f2fa0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ constcat = "0.6.0" curl = "0.4.46" fmutex = "0.3.0" git2 = "0.20.0" +git2-curl = "0.21.0" globwalk = "0.9.1" home = "0.5.9" indexmap = { version = "2.4.0", features = ["rayon", "serde"] } @@ -64,7 +65,7 @@ features = [ anyhow = "1.0.86" [dev-dependencies] -goldie = "0.5.0" +goldie = { version = "0.6.0", default-features = false, features = ["color", "template"] } pretty_assertions = "1.4.0" tempfile = "3.12.0" upon = { version = "0.10.0", default-features = false, features = ["serde", "functions", "syntax"] } diff --git a/src/cli/color_choice.rs b/src/cli/color_choice.rs index 36538e4f..07e60994 100644 --- a/src/cli/color_choice.rs +++ b/src/cli/color_choice.rs @@ -6,22 +6,17 @@ use std::str::FromStr; use thiserror::Error; /// Whether messages should use color output. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub enum ColorChoice { /// Force color output. Always, /// Intelligently guess whether to use color output. + #[default] Auto, /// Force disable color output. Never, } -impl Default for ColorChoice { - fn default() -> Self { - Self::Auto - } -} - impl fmt::Display for ColorChoice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { diff --git a/src/context/mod.rs b/src/context/mod.rs index 884a8d8a..080fc15b 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -47,19 +47,14 @@ pub struct Output { } /// The requested verbosity of output. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Default)] pub enum Verbosity { Quiet, + #[default] Normal, Verbose, } -impl Default for Verbosity { - fn default() -> Self { - Self::Normal - } -} - impl Context { /// The location of the configuration directory. pub fn config_dir(&self) -> &Path { diff --git a/src/util/git.rs b/src/util/git.rs index 1b92d894..db3ba523 100644 --- a/src/util/git.rs +++ b/src/util/git.rs @@ -1,12 +1,13 @@ //! Git helpers. use std::path::Path; -use std::sync::LazyLock as Lazy; +use std::sync::{LazyLock as Lazy, Once}; use anyhow::Context as ResultExt; +use curl::easy::Easy; use git2::{ - BranchType, Cred, CredentialType, Error, FetchOptions, Oid, RemoteCallbacks, Repository, - ResetType, SubmoduleUpdateOptions, + BranchType, Config, Cred, CredentialType, Error, FetchOptions, Oid, RemoteCallbacks, + Repository, ResetType, SubmoduleUpdateOptions, }; use url::Url; @@ -15,6 +16,8 @@ fn with_fetch_options(f: F) -> anyhow::Result where F: FnOnce(FetchOptions<'_>) -> anyhow::Result, { + ensure_git_curl_transport_registered(); + let mut rcb = RemoteCallbacks::new(); rcb.credentials(|_, username, allowed| { if allowed.contains(CredentialType::SSH_KEY) { @@ -30,10 +33,14 @@ where )) }); - // Try to auto-detect the proxy from the git configuration so that - // Sheldon can be used behind a proxy. + // Prefer proxies set in Git config, + // otherwise fallback to auto detection which uses environment variables. let mut proxy_opts = git2::ProxyOptions::new(); - proxy_opts.auto(); + if let Some(proxy_url) = proxy_url_from_git_config() { + proxy_opts.url(&proxy_url); + } else { + proxy_opts.auto(); + } let mut opts = FetchOptions::new(); opts.remote_callbacks(rcb); @@ -41,6 +48,32 @@ where f(opts) } +fn proxy_url_from_git_config() -> Option { + let cfg = Config::open_default().ok()?; + + // Use the standard git http.proxy setting. + cfg.get_string("http.proxy").ok() +} + +fn ensure_git_curl_transport_registered() { + static REGISTER: Once = Once::new(); + + REGISTER.call_once(|| { + if let Err(err) = register_git_http_transport() { + eprintln!("warning: failed to enable libcurl transport for git: {err}"); + } + }); +} + +fn register_git_http_transport() -> anyhow::Result<()> { + let mut handle = Easy::new(); + handle.useragent(&format!("sheldon/{}", env!("CARGO_PKG_VERSION")))?; + unsafe { + git2_curl::register(handle); + } + Ok(()) +} + /// Open a Git repository. pub fn open(dir: &Path) -> anyhow::Result { let repo = Repository::open(dir) diff --git a/tests/testdata/github_bad_url/lock.stderr b/tests/testdata/github_bad_url/lock.stderr index 0456b022..cc27f486 100644 --- a/tests/testdata/github_bad_url/lock.stderr +++ b/tests/testdata/github_bad_url/lock.stderr @@ -3,4 +3,4 @@ LOADED ~/.config/sheldon/plugins.toml ERROR: failed to install source `https://github.com/rossmacarthur/sheldon-bad-url` due to: failed to git clone `https://github.com/rossmacarthur/sheldon-bad-url` - due to: remote authentication required but none available + due to: failed to receive HTTP 200 response: got 401; class=Net (12) diff --git a/tests/testdata/github_bad_url/source.stderr b/tests/testdata/github_bad_url/source.stderr index 91f3f4ff..638d2f28 100644 --- a/tests/testdata/github_bad_url/source.stderr +++ b/tests/testdata/github_bad_url/source.stderr @@ -4,4 +4,4 @@ LOADED ~/.config/sheldon/plugins.toml ERROR: failed to install source `https://github.com/rossmacarthur/sheldon-bad-url` due to: failed to git clone `https://github.com/rossmacarthur/sheldon-bad-url` - due to: remote authentication required but none available + due to: failed to receive HTTP 200 response: got 401; class=Net (12)