Skip to content

Commit 9441c26

Browse files
committed
apply 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. It's now done for transport related configuration as well as all other environment variables we previously queried on the fly.
1 parent fc64693 commit 9441c26

File tree

21 files changed

+1240
-729
lines changed

21 files changed

+1240
-729
lines changed

Diff for: 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");

Diff for: git-repository/src/config/cache/access.rs

+3-4
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
})
@@ -72,9 +72,8 @@ impl Cache {
7272
pub(crate) fn url_scheme(
7373
&self,
7474
) -> Result<&remote::url::SchemePermission, remote::url::scheme_permission::init::Error> {
75-
self.url_scheme.get_or_try_init(|| {
76-
remote::url::SchemePermission::from_config(&self.resolved, self.git_prefix, self.filter_config_section)
77-
})
75+
self.url_scheme
76+
.get_or_try_init(|| remote::url::SchemePermission::from_config(&self.resolved, self.filter_config_section))
7877
}
7978

8079
/// Returns (file-timeout, pack-refs timeout)

Diff for: git-repository/src/config/cache/init.rs

+220-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-
})?;
125-
}
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-
})?;
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+
_ => {}
13280
}
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,144 @@ impl Cache {
208211
Ok(())
209212
}
210213
}
214+
215+
impl crate::Repository {
216+
/// Causes our configuration to re-read cached values which will also be applied to the repository in-memory state if applicable.
217+
///
218+
/// Similar to `reread_values_and_clear_caches_replacing_config()`, but works on the existing instance instead of a passed
219+
/// in one that it them makes the default.
220+
#[cfg(feature = "blocking-network-client")]
221+
pub(crate) fn reread_values_and_clear_caches(&mut self) -> Result<(), Error> {
222+
self.config.reread_values_and_clear_caches()?;
223+
self.apply_changed_values();
224+
Ok(())
225+
}
226+
227+
/// Replace our own configuration with `config` and re-read all cached values, and apply them to select in-memory instances.
228+
pub(crate) fn reread_values_and_clear_caches_replacing_config(
229+
&mut self,
230+
config: crate::Config,
231+
) -> Result<(), Error> {
232+
self.config.reread_values_and_clear_caches_replacing_config(config)?;
233+
self.apply_changed_values();
234+
Ok(())
235+
}
236+
237+
fn apply_changed_values(&mut self) {
238+
self.refs.write_reflog = util::reflog_or_default(self.config.reflog, self.work_dir().is_some());
239+
}
240+
}
241+
242+
fn apply_environment_overrides(
243+
config: &mut File<'static>,
244+
git_prefix: Permission,
245+
http_transport: Permission,
246+
) -> Result<(), Error> {
247+
fn var_as_bstring(var: &str, perm: Permission) -> Option<BString> {
248+
perm.check_opt(var)
249+
.and_then(std::env::var_os)
250+
.and_then(|val| git_path::os_string_into_bstring(val).ok())
251+
}
252+
253+
let mut env_override = git_config::File::new(git_config::file::Metadata::from(git_config::Source::EnvOverride));
254+
{
255+
let mut section = env_override
256+
.new_section("http", None)
257+
.expect("statically known valid section name");
258+
for (var, key, permission) in [
259+
("GIT_HTTP_LOW_SPEED_LIMIT", "lowSpeedLimit", git_prefix),
260+
("GIT_HTTP_LOW_SPEED_TIME", "lowSpeedTime", git_prefix),
261+
("GIT_HTTP_USER_AGENT", "userAgent", git_prefix),
262+
("GIT_HTTP_PROXY_AUTHMETHOD", "proxyAuthMethod", git_prefix),
263+
("all_proxy", "all-proxy-lower", http_transport),
264+
("ALL_PROXY", "all-proxy", http_transport),
265+
] {
266+
if let Some(value) = var_as_bstring(var, permission) {
267+
section.push_with_comment(
268+
key.try_into().expect("statically known to be valid"),
269+
Some(value.as_ref()),
270+
format!("from {var}").as_str(),
271+
);
272+
}
273+
}
274+
if section.num_values() == 0 {
275+
let id = section.id();
276+
env_override.remove_section_by_id(id);
277+
}
278+
}
279+
280+
{
281+
let mut section = env_override
282+
.new_section("gitoxide", Some(Cow::Borrowed("https".into())))
283+
.expect("statically known valid section name");
284+
285+
for (var, key) in [("HTTPS_PROXY", "proxy"), ("https_proxy", "proxy")] {
286+
if let Some(value) = var_as_bstring(var, http_transport) {
287+
section.push_with_comment(
288+
key.try_into().expect("statically known to be valid"),
289+
Some(value.as_ref()),
290+
format!("from {var}").as_str(),
291+
);
292+
}
293+
}
294+
295+
if section.num_values() == 0 {
296+
let id = section.id();
297+
env_override.remove_section_by_id(id);
298+
}
299+
}
300+
301+
{
302+
let mut section = env_override
303+
.new_section("gitoxide", Some(Cow::Borrowed("allow".into())))
304+
.expect("statically known valid section name");
305+
306+
for (var, key) in [("GIT_PROTOCOL_FROM_USER", "protocolFromUser")] {
307+
if let Some(value) = var_as_bstring(var, http_transport) {
308+
section.push_with_comment(
309+
key.try_into().expect("statically known to be valid"),
310+
Some(value.as_ref()),
311+
format!("from {var}").as_str(),
312+
);
313+
}
314+
}
315+
316+
if section.num_values() == 0 {
317+
let id = section.id();
318+
env_override.remove_section_by_id(id);
319+
}
320+
}
321+
322+
{
323+
let mut section = env_override
324+
.new_section("gitoxide", Some(Cow::Borrowed("http".into())))
325+
.expect("statically known valid section name");
326+
327+
for (var, key, permission) in [
328+
("ALL_PROXY", "allProxy", http_transport),
329+
("all_proxy", "allProxy", http_transport),
330+
("NO_PROXY", "noProxy", http_transport),
331+
("no_proxy", "noProxy", http_transport),
332+
("http_proxy", "proxy", http_transport),
333+
("GIT_CURL_VERBOSE", "verbose", git_prefix),
334+
] {
335+
if let Some(value) = var_as_bstring(var, permission) {
336+
section.push_with_comment(
337+
key.try_into().expect("statically known to be valid"),
338+
Some(value.as_ref()),
339+
format!("from {var}").as_str(),
340+
);
341+
}
342+
}
343+
344+
if section.num_values() == 0 {
345+
let id = section.id();
346+
env_override.remove_section_by_id(id);
347+
}
348+
}
349+
350+
if !env_override.is_void() {
351+
config.append(env_override);
352+
}
353+
Ok(())
354+
}

Diff for: git-repository/src/config/cache/util.rs

+11
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,17 @@ where
9393
}
9494
}
9595

96+
pub(crate) fn reflog_or_default(
97+
config_reflog: Option<git_ref::store::WriteReflog>,
98+
has_worktree: bool,
99+
) -> git_ref::store::WriteReflog {
100+
config_reflog.unwrap_or_else(|| {
101+
has_worktree
102+
.then(|| git_ref::store::WriteReflog::Normal)
103+
.unwrap_or(git_ref::store::WriteReflog::Disable)
104+
})
105+
}
106+
96107
pub(crate) fn parse_core_abbrev(
97108
config: &git_config::File<'static>,
98109
object_hash: git_hash::Kind,

0 commit comments

Comments
 (0)