Skip to content

Commit 1e14b41

Browse files
committed
apply transport related environment variables as config overrides
Using git-configuration to store overrides from the environment is helpful as it more tighly integrates with the best configuration system there is, and also is nicely visualizable.
1 parent 8aac43c commit 1e14b41

File tree

16 files changed

+546
-113
lines changed

16 files changed

+546
-113
lines changed

git-repository/src/clone/fetch/util.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,13 @@ pub fn replace_changed_local_config_file(repo: &mut Repository, mut config: git_
3838
for id in ids_to_remove {
3939
repo_config.remove_section_by_id(id);
4040
}
41-
crate::config::overrides::append(&mut config, &repo.options.api_config_overrides, git_config::Source::Api)
42-
.expect("applied once and can be applied again");
41+
crate::config::overrides::append(
42+
&mut config,
43+
&repo.options.api_config_overrides,
44+
git_config::Source::Api,
45+
|_| None,
46+
)
47+
.expect("applied once and can be applied again");
4348
repo_config.append(config);
4449
repo.reread_values_and_clear_caches()
4550
.expect("values could be read once and can be read again");

git-repository/src/config/cache/access.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl Cache {
5050
.user_agent
5151
.get_or_init(|| {
5252
self.resolved
53-
.string("gitoxide", None, "userAgent")
53+
.string_by_key("gitoxide.userAgent")
5454
.map(|s| s.to_string())
5555
.unwrap_or_else(|| crate::env::agent().into())
5656
})

git-repository/src/config/cache/init.rs

+170-76
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ use crate::{
44
config::{cache::util::ApplyLeniency, Cache},
55
repository,
66
};
7+
use git_config::File;
8+
use git_sec::Permission;
9+
use std::borrow::Cow;
710

811
/// Initialization
912
impl Cache {
@@ -27,7 +30,7 @@ impl Cache {
2730
home: home_env,
2831
xdg_config_home: xdg_config_home_env,
2932
ssh_prefix: _,
30-
http_transport: _,
33+
http_transport,
3134
}: repository::permissions::Environment,
3235
repository::permissions::Config {
3336
git_binary: use_installation,
@@ -56,82 +59,82 @@ impl Cache {
5659
..util::base_options(lossy)
5760
};
5861

59-
let config =
60-
{
61-
let home_env = &home_env;
62-
let xdg_config_home_env = &xdg_config_home_env;
63-
let git_prefix = &git_prefix;
64-
let metas = [
65-
git_config::source::Kind::GitInstallation,
66-
git_config::source::Kind::System,
67-
git_config::source::Kind::Global,
68-
]
69-
.iter()
70-
.flat_map(|kind| kind.sources())
71-
.filter_map(|source| {
72-
match source {
73-
git_config::Source::GitInstallation if !use_installation => return None,
74-
git_config::Source::System if !use_system => return None,
75-
git_config::Source::Git if !use_git => return None,
76-
git_config::Source::User if !use_user => return None,
77-
_ => {}
78-
}
79-
source
80-
.storage_location(&mut |name| {
81-
match name {
82-
git_ if git_.starts_with("GIT_") => Some(git_prefix),
83-
"XDG_CONFIG_HOME" => Some(xdg_config_home_env),
84-
"HOME" => Some(home_env),
85-
_ => None,
86-
}
87-
.and_then(|perm| std::env::var_os(name).and_then(|val| perm.check_opt(val)))
88-
})
89-
.map(|p| (source, p.into_owned()))
90-
})
91-
.map(|(source, path)| git_config::file::Metadata {
92-
path: Some(path),
93-
source: *source,
94-
level: 0,
95-
trust: git_sec::Trust::Full,
96-
});
97-
98-
let err_on_nonexisting_paths = false;
99-
let mut globals = git_config::File::from_paths_metadata_buf(
100-
metas,
101-
&mut buf,
102-
err_on_nonexisting_paths,
103-
git_config::file::init::Options {
104-
includes: git_config::file::includes::Options::no_follow(),
105-
..options
106-
},
107-
)
108-
.map_err(|err| match err {
109-
git_config::file::init::from_paths::Error::Init(err) => Error::from(err),
110-
git_config::file::init::from_paths::Error::Io(err) => err.into(),
111-
})?
112-
.unwrap_or_default();
113-
114-
globals.append(git_dir_config);
115-
globals.resolve_includes(options)?;
116-
if use_env {
117-
globals.append(git_config::File::from_env(options)?.unwrap_or_default());
118-
}
119-
if !cli_config_overrides.is_empty() {
120-
crate::config::overrides::append(&mut globals, cli_config_overrides, git_config::Source::Cli)
121-
.map_err(|err| Error::ConfigOverrides {
122-
err,
123-
source: git_config::Source::Cli,
124-
})?;
62+
let config = {
63+
let home_env = &home_env;
64+
let xdg_config_home_env = &xdg_config_home_env;
65+
let git_prefix = &git_prefix;
66+
let metas = [
67+
git_config::source::Kind::GitInstallation,
68+
git_config::source::Kind::System,
69+
git_config::source::Kind::Global,
70+
]
71+
.iter()
72+
.flat_map(|kind| kind.sources())
73+
.filter_map(|source| {
74+
match source {
75+
git_config::Source::GitInstallation if !use_installation => return None,
76+
git_config::Source::System if !use_system => return None,
77+
git_config::Source::Git if !use_git => return None,
78+
git_config::Source::User if !use_user => return None,
79+
_ => {}
12580
}
126-
if !api_config_overrides.is_empty() {
127-
crate::config::overrides::append(&mut globals, api_config_overrides, git_config::Source::Api)
128-
.map_err(|err| Error::ConfigOverrides {
129-
err,
130-
source: git_config::Source::Api,
131-
})?;
132-
}
133-
globals
134-
};
81+
source
82+
.storage_location(&mut |name| {
83+
match name {
84+
git_ if git_.starts_with("GIT_") => Some(git_prefix),
85+
"XDG_CONFIG_HOME" => Some(xdg_config_home_env),
86+
"HOME" => Some(home_env),
87+
_ => None,
88+
}
89+
.and_then(|perm| std::env::var_os(name).and_then(|val| perm.check_opt(val)))
90+
})
91+
.map(|p| (source, p.into_owned()))
92+
})
93+
.map(|(source, path)| git_config::file::Metadata {
94+
path: Some(path),
95+
source: *source,
96+
level: 0,
97+
trust: git_sec::Trust::Full,
98+
});
99+
100+
let err_on_nonexisting_paths = false;
101+
let mut globals = git_config::File::from_paths_metadata_buf(
102+
metas,
103+
&mut buf,
104+
err_on_nonexisting_paths,
105+
git_config::file::init::Options {
106+
includes: git_config::file::includes::Options::no_follow(),
107+
..options
108+
},
109+
)
110+
.map_err(|err| match err {
111+
git_config::file::init::from_paths::Error::Init(err) => Error::from(err),
112+
git_config::file::init::from_paths::Error::Io(err) => err.into(),
113+
})?
114+
.unwrap_or_default();
115+
116+
globals.append(git_dir_config);
117+
globals.resolve_includes(options)?;
118+
if use_env {
119+
globals.append(git_config::File::from_env(options)?.unwrap_or_default());
120+
}
121+
if !cli_config_overrides.is_empty() {
122+
crate::config::overrides::append(&mut globals, cli_config_overrides, git_config::Source::Cli, |_| None)
123+
.map_err(|err| Error::ConfigOverrides {
124+
err,
125+
source: git_config::Source::Cli,
126+
})?;
127+
}
128+
if !api_config_overrides.is_empty() {
129+
crate::config::overrides::append(&mut globals, api_config_overrides, git_config::Source::Api, |_| None)
130+
.map_err(|err| Error::ConfigOverrides {
131+
err,
132+
source: git_config::Source::Api,
133+
})?;
134+
}
135+
apply_environment_overrides(&mut globals, *git_prefix, http_transport)?;
136+
globals
137+
};
135138

136139
let hex_len = util::parse_core_abbrev(&config, object_hash).with_leniency(lenient_config)?;
137140

@@ -208,3 +211,94 @@ impl Cache {
208211
Ok(())
209212
}
210213
}
214+
215+
fn apply_environment_overrides(
216+
config: &mut File<'static>,
217+
git_prefix: Permission,
218+
http_transport: Permission,
219+
) -> Result<(), Error> {
220+
fn var_as_bstring(var: &str, perm: Permission) -> Option<BString> {
221+
perm.check_opt(var)
222+
.and_then(std::env::var_os)
223+
.and_then(|val| git_path::os_string_into_bstring(val).ok())
224+
}
225+
226+
let mut env_override = git_config::File::new(git_config::file::Metadata::from(git_config::Source::EnvOverride));
227+
{
228+
let mut section = env_override
229+
.new_section("http", None)
230+
.expect("statically known valid section name");
231+
for (var, key, permission) in [
232+
("GIT_HTTP_LOW_SPEED_LIMIT", "lowSpeedLimit", git_prefix),
233+
("GIT_HTTP_LOW_SPEED_TIME", "lowSpeedTime", git_prefix),
234+
("GIT_HTTP_USER_AGENT", "userAgent", git_prefix),
235+
("GIT_HTTP_PROXY_AUTHMETHOD", "proxyAuthMethod", git_prefix),
236+
("all_proxy", "all-proxy-lower", http_transport),
237+
("ALL_PROXY", "all-proxy", http_transport),
238+
] {
239+
if let Some(value) = var_as_bstring(var, permission) {
240+
section.push_with_comment(
241+
key.try_into().expect("statically known to be valid"),
242+
Some(value.as_ref()),
243+
format!("from {var}").as_str(),
244+
);
245+
}
246+
}
247+
if section.num_values() == 0 {
248+
let id = section.id();
249+
env_override.remove_section_by_id(id);
250+
}
251+
}
252+
253+
{
254+
let mut section = env_override
255+
.new_section("gitoxide", Some(Cow::Borrowed("https".into())))
256+
.expect("statically known valid section name");
257+
258+
for (var, key) in [("HTTPS_PROXY", "proxy"), ("https_proxy", "proxy")] {
259+
if let Some(value) = var_as_bstring(var, http_transport) {
260+
section.push_with_comment(
261+
key.try_into().expect("statically known to be valid"),
262+
Some(value.as_ref()),
263+
format!("from {var}").as_str(),
264+
);
265+
}
266+
}
267+
268+
if section.num_values() == 0 {
269+
let id = section.id();
270+
env_override.remove_section_by_id(id);
271+
}
272+
}
273+
274+
{
275+
let mut section = env_override
276+
.new_section("gitoxide", Some(Cow::Borrowed("http".into())))
277+
.expect("statically known valid section name");
278+
279+
for (var, key, permission) in [
280+
("ALL_PROXY", "allProxy", http_transport),
281+
("all_proxy", "allProxy", http_transport),
282+
("http_proxy", "proxy", http_transport),
283+
("GIT_CURL_VERBOSE", "verbose", git_prefix),
284+
] {
285+
if let Some(value) = var_as_bstring(var, permission) {
286+
section.push_with_comment(
287+
key.try_into().expect("statically known to be valid"),
288+
Some(value.as_ref()),
289+
format!("from {var}").as_str(),
290+
);
291+
}
292+
}
293+
294+
if section.num_values() == 0 {
295+
let id = section.id();
296+
env_override.remove_section_by_id(id);
297+
}
298+
}
299+
300+
if !env_override.is_void() {
301+
config.append(env_override);
302+
}
303+
Ok(())
304+
}

git-repository/src/config/overrides.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::convert::TryFrom;
22

33
use crate::bstr::{BStr, BString, ByteSlice};
44

5-
/// The error returned by [SnapshotMut::apply_cli_overrides()][crate::config::SnapshotMut::apply_cli_overrides()].
5+
/// The error returned by [SnapshotMut::apply_cli_overrides()][crate::config::SnapshotMut::append_config()].
66
#[derive(Debug, thiserror::Error)]
77
#[allow(missing_docs)]
88
pub enum Error {
@@ -21,6 +21,7 @@ pub(crate) fn append(
2121
config: &mut git_config::File<'static>,
2222
values: impl IntoIterator<Item = impl AsRef<BStr>>,
2323
source: git_config::Source,
24+
mut make_comment: impl FnMut(&BStr) -> Option<BString>,
2425
) -> Result<(), Error> {
2526
let mut file = git_config::File::new(git_config::file::Metadata::from(source));
2627
for key_value in values {
@@ -31,13 +32,17 @@ pub(crate) fn append(
3132
let key = git_config::parse::key(key.to_str().map_err(|_| Error::InvalidKey { input: key.into() })?)
3233
.ok_or_else(|| Error::InvalidKey { input: key.into() })?;
3334
let mut section = file.section_mut_or_create_new(key.section_name, key.subsection_name)?;
34-
section.push(
35+
let key =
3536
git_config::parse::section::Key::try_from(key.value_name.to_owned()).map_err(|err| Error::SectionKey {
3637
source: err,
3738
key: key.value_name.into(),
38-
})?,
39-
value.map(|v| v.as_bstr()),
40-
);
39+
})?;
40+
let comment = make_comment(key_value);
41+
let value = value.map(|v| v.as_bstr());
42+
match comment {
43+
Some(comment) => section.push_with_comment(key, value, &**comment),
44+
None => section.push(key, value),
45+
}
4146
}
4247
config.append(file);
4348
Ok(())

git-repository/src/config/snapshot/_impls.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl Deref for Snapshot<'_> {
5151
type Target = git_config::File<'static>;
5252

5353
fn deref(&self) -> &Self::Target {
54-
&self.plumbing()
54+
self.plumbing()
5555
}
5656
}
5757

git-repository/src/config/snapshot/access.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ impl<'repo> SnapshotMut<'repo> {
8686
values: impl IntoIterator<Item = impl AsRef<BStr>>,
8787
source: git_config::Source,
8888
) -> Result<&mut Self, crate::config::overrides::Error> {
89-
crate::config::overrides::append(&mut self.config, values, source)?;
89+
crate::config::overrides::append(&mut self.config, values, source, |v| Some(format!("-c {v}").into()))?;
9090
Ok(self)
9191
}
9292
/// Apply all changes made to this instance.

git-repository/src/open.rs

-1
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,6 @@ impl ThreadSafeRepository {
340340
// TODO: The following vars should end up as overrides of the respective configuration values (see git-config).
341341
// GIT_HTTP_PROXY_AUTHMETHOD, GIT_PROXY_SSL_CERT, GIT_PROXY_SSL_KEY, GIT_PROXY_SSL_CERT_PASSWORD_PROTECTED.
342342
// GIT_PROXY_SSL_CAINFO, GIT_SSL_VERSION, GIT_SSL_CIPHER_LIST, GIT_HTTP_MAX_REQUESTS, GIT_CURL_FTP_NO_EPSV,
343-
// GIT_HTTP_LOW_SPEED_LIMIT, GIT_HTTP_LOW_SPEED_TIME, GIT_HTTP_USER_AGENT,
344343
// no_proxy, NO_PROXY, http_proxy, HTTPS_PROXY, https_proxy, ALL_PROXY, all_proxy
345344
pub fn open_with_environment_overrides(
346345
fallback_directory: impl Into<PathBuf>,

0 commit comments

Comments
 (0)