Skip to content

Commit

Permalink
apply transport related environment variables as config overrides
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Byron committed Nov 29, 2022
1 parent fc64693 commit 6715587
Show file tree
Hide file tree
Showing 16 changed files with 587 additions and 117 deletions.
9 changes: 7 additions & 2 deletions git-repository/src/clone/fetch/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,13 @@ pub fn replace_changed_local_config_file(repo: &mut Repository, mut config: git_
for id in ids_to_remove {
repo_config.remove_section_by_id(id);
}
crate::config::overrides::append(&mut config, &repo.options.api_config_overrides, git_config::Source::Api)
.expect("applied once and can be applied again");
crate::config::overrides::append(
&mut config,
&repo.options.api_config_overrides,
git_config::Source::Api,
|_| None,
)
.expect("applied once and can be applied again");
repo_config.append(config);
repo.reread_values_and_clear_caches()
.expect("values could be read once and can be read again");
Expand Down
2 changes: 1 addition & 1 deletion git-repository/src/config/cache/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl Cache {
.user_agent
.get_or_init(|| {
self.resolved
.string("gitoxide", None, "userAgent")
.string_by_key("gitoxide.userAgent")
.map(|s| s.to_string())
.unwrap_or_else(|| crate::env::agent().into())
})
Expand Down
248 changes: 172 additions & 76 deletions git-repository/src/config/cache/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use crate::{
config::{cache::util::ApplyLeniency, Cache},
repository,
};
use git_config::File;
use git_sec::Permission;
use std::borrow::Cow;

/// Initialization
impl Cache {
Expand All @@ -27,7 +30,7 @@ impl Cache {
home: home_env,
xdg_config_home: xdg_config_home_env,
ssh_prefix: _,
http_transport: _,
http_transport,
}: repository::permissions::Environment,
repository::permissions::Config {
git_binary: use_installation,
Expand Down Expand Up @@ -56,82 +59,82 @@ impl Cache {
..util::base_options(lossy)
};

let config =
{
let home_env = &home_env;
let xdg_config_home_env = &xdg_config_home_env;
let git_prefix = &git_prefix;
let metas = [
git_config::source::Kind::GitInstallation,
git_config::source::Kind::System,
git_config::source::Kind::Global,
]
.iter()
.flat_map(|kind| kind.sources())
.filter_map(|source| {
match source {
git_config::Source::GitInstallation if !use_installation => return None,
git_config::Source::System if !use_system => return None,
git_config::Source::Git if !use_git => return None,
git_config::Source::User if !use_user => return None,
_ => {}
}
source
.storage_location(&mut |name| {
match name {
git_ if git_.starts_with("GIT_") => Some(git_prefix),
"XDG_CONFIG_HOME" => Some(xdg_config_home_env),
"HOME" => Some(home_env),
_ => None,
}
.and_then(|perm| std::env::var_os(name).and_then(|val| perm.check_opt(val)))
})
.map(|p| (source, p.into_owned()))
})
.map(|(source, path)| git_config::file::Metadata {
path: Some(path),
source: *source,
level: 0,
trust: git_sec::Trust::Full,
});

let err_on_nonexisting_paths = false;
let mut globals = git_config::File::from_paths_metadata_buf(
metas,
&mut buf,
err_on_nonexisting_paths,
git_config::file::init::Options {
includes: git_config::file::includes::Options::no_follow(),
..options
},
)
.map_err(|err| match err {
git_config::file::init::from_paths::Error::Init(err) => Error::from(err),
git_config::file::init::from_paths::Error::Io(err) => err.into(),
})?
.unwrap_or_default();

globals.append(git_dir_config);
globals.resolve_includes(options)?;
if use_env {
globals.append(git_config::File::from_env(options)?.unwrap_or_default());
}
if !cli_config_overrides.is_empty() {
crate::config::overrides::append(&mut globals, cli_config_overrides, git_config::Source::Cli)
.map_err(|err| Error::ConfigOverrides {
err,
source: git_config::Source::Cli,
})?;
let config = {
let home_env = &home_env;
let xdg_config_home_env = &xdg_config_home_env;
let git_prefix = &git_prefix;
let metas = [
git_config::source::Kind::GitInstallation,
git_config::source::Kind::System,
git_config::source::Kind::Global,
]
.iter()
.flat_map(|kind| kind.sources())
.filter_map(|source| {
match source {
git_config::Source::GitInstallation if !use_installation => return None,
git_config::Source::System if !use_system => return None,
git_config::Source::Git if !use_git => return None,
git_config::Source::User if !use_user => return None,
_ => {}
}
if !api_config_overrides.is_empty() {
crate::config::overrides::append(&mut globals, api_config_overrides, git_config::Source::Api)
.map_err(|err| Error::ConfigOverrides {
err,
source: git_config::Source::Api,
})?;
}
globals
};
source
.storage_location(&mut |name| {
match name {
git_ if git_.starts_with("GIT_") => Some(git_prefix),
"XDG_CONFIG_HOME" => Some(xdg_config_home_env),
"HOME" => Some(home_env),
_ => None,
}
.and_then(|perm| std::env::var_os(name).and_then(|val| perm.check_opt(val)))
})
.map(|p| (source, p.into_owned()))
})
.map(|(source, path)| git_config::file::Metadata {
path: Some(path),
source: *source,
level: 0,
trust: git_sec::Trust::Full,
});

let err_on_nonexisting_paths = false;
let mut globals = git_config::File::from_paths_metadata_buf(
metas,
&mut buf,
err_on_nonexisting_paths,
git_config::file::init::Options {
includes: git_config::file::includes::Options::no_follow(),
..options
},
)
.map_err(|err| match err {
git_config::file::init::from_paths::Error::Init(err) => Error::from(err),
git_config::file::init::from_paths::Error::Io(err) => err.into(),
})?
.unwrap_or_default();

globals.append(git_dir_config);
globals.resolve_includes(options)?;
if use_env {
globals.append(git_config::File::from_env(options)?.unwrap_or_default());
}
if !cli_config_overrides.is_empty() {
crate::config::overrides::append(&mut globals, cli_config_overrides, git_config::Source::Cli, |_| None)
.map_err(|err| Error::ConfigOverrides {
err,
source: git_config::Source::Cli,
})?;
}
if !api_config_overrides.is_empty() {
crate::config::overrides::append(&mut globals, api_config_overrides, git_config::Source::Api, |_| None)
.map_err(|err| Error::ConfigOverrides {
err,
source: git_config::Source::Api,
})?;
}
apply_environment_overrides(&mut globals, *git_prefix, http_transport)?;
globals
};

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

Expand Down Expand Up @@ -208,3 +211,96 @@ impl Cache {
Ok(())
}
}

fn apply_environment_overrides(
config: &mut File<'static>,
git_prefix: Permission,
http_transport: Permission,
) -> Result<(), Error> {
fn var_as_bstring(var: &str, perm: Permission) -> Option<BString> {
perm.check_opt(var)
.and_then(std::env::var_os)
.and_then(|val| git_path::os_string_into_bstring(val).ok())
}

let mut env_override = git_config::File::new(git_config::file::Metadata::from(git_config::Source::EnvOverride));
{
let mut section = env_override
.new_section("http", None)
.expect("statically known valid section name");
for (var, key, permission) in [
("GIT_HTTP_LOW_SPEED_LIMIT", "lowSpeedLimit", git_prefix),
("GIT_HTTP_LOW_SPEED_TIME", "lowSpeedTime", git_prefix),
("GIT_HTTP_USER_AGENT", "userAgent", git_prefix),
("GIT_HTTP_PROXY_AUTHMETHOD", "proxyAuthMethod", git_prefix),
("all_proxy", "all-proxy-lower", http_transport),
("ALL_PROXY", "all-proxy", http_transport),
] {
if let Some(value) = var_as_bstring(var, permission) {
section.push_with_comment(
key.try_into().expect("statically known to be valid"),
Some(value.as_ref()),
format!("from {var}").as_str(),
);
}
}
if section.num_values() == 0 {
let id = section.id();
env_override.remove_section_by_id(id);
}
}

{
let mut section = env_override
.new_section("gitoxide", Some(Cow::Borrowed("https".into())))
.expect("statically known valid section name");

for (var, key) in [("HTTPS_PROXY", "proxy"), ("https_proxy", "proxy")] {
if let Some(value) = var_as_bstring(var, http_transport) {
section.push_with_comment(
key.try_into().expect("statically known to be valid"),
Some(value.as_ref()),
format!("from {var}").as_str(),
);
}
}

if section.num_values() == 0 {
let id = section.id();
env_override.remove_section_by_id(id);
}
}

{
let mut section = env_override
.new_section("gitoxide", Some(Cow::Borrowed("http".into())))
.expect("statically known valid section name");

for (var, key, permission) in [
("ALL_PROXY", "allProxy", http_transport),
("all_proxy", "allProxy", http_transport),
("NO_PROXY", "noProxy", http_transport),
("no_proxy", "noProxy", http_transport),
("http_proxy", "proxy", http_transport),
("GIT_CURL_VERBOSE", "verbose", git_prefix),
] {
if let Some(value) = var_as_bstring(var, permission) {
section.push_with_comment(
key.try_into().expect("statically known to be valid"),
Some(value.as_ref()),
format!("from {var}").as_str(),
);
}
}

if section.num_values() == 0 {
let id = section.id();
env_override.remove_section_by_id(id);
}
}

if !env_override.is_void() {
config.append(env_override);
}
Ok(())
}
15 changes: 10 additions & 5 deletions git-repository/src/config/overrides.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::convert::TryFrom;

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

/// The error returned by [SnapshotMut::apply_cli_overrides()][crate::config::SnapshotMut::apply_cli_overrides()].
/// The error returned by [SnapshotMut::apply_cli_overrides()][crate::config::SnapshotMut::append_config()].
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
Expand All @@ -21,6 +21,7 @@ pub(crate) fn append(
config: &mut git_config::File<'static>,
values: impl IntoIterator<Item = impl AsRef<BStr>>,
source: git_config::Source,
mut make_comment: impl FnMut(&BStr) -> Option<BString>,
) -> Result<(), Error> {
let mut file = git_config::File::new(git_config::file::Metadata::from(source));
for key_value in values {
Expand All @@ -31,13 +32,17 @@ pub(crate) fn append(
let key = git_config::parse::key(key.to_str().map_err(|_| Error::InvalidKey { input: key.into() })?)
.ok_or_else(|| Error::InvalidKey { input: key.into() })?;
let mut section = file.section_mut_or_create_new(key.section_name, key.subsection_name)?;
section.push(
let key =
git_config::parse::section::Key::try_from(key.value_name.to_owned()).map_err(|err| Error::SectionKey {
source: err,
key: key.value_name.into(),
})?,
value.map(|v| v.as_bstr()),
);
})?;
let comment = make_comment(key_value);
let value = value.map(|v| v.as_bstr());
match comment {
Some(comment) => section.push_with_comment(key, value, &**comment),
None => section.push(key, value),
}
}
config.append(file);
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion git-repository/src/config/snapshot/_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl Deref for Snapshot<'_> {
type Target = git_config::File<'static>;

fn deref(&self) -> &Self::Target {
&self.plumbing()
self.plumbing()
}
}

Expand Down
2 changes: 1 addition & 1 deletion git-repository/src/config/snapshot/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ impl<'repo> SnapshotMut<'repo> {
values: impl IntoIterator<Item = impl AsRef<BStr>>,
source: git_config::Source,
) -> Result<&mut Self, crate::config::overrides::Error> {
crate::config::overrides::append(&mut self.config, values, source)?;
crate::config::overrides::append(&mut self.config, values, source, |v| Some(format!("-c {v}").into()))?;
Ok(self)
}
/// Apply all changes made to this instance.
Expand Down
1 change: 0 additions & 1 deletion git-repository/src/open.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,6 @@ impl ThreadSafeRepository {
// TODO: The following vars should end up as overrides of the respective configuration values (see git-config).
// GIT_HTTP_PROXY_AUTHMETHOD, GIT_PROXY_SSL_CERT, GIT_PROXY_SSL_KEY, GIT_PROXY_SSL_CERT_PASSWORD_PROTECTED.
// GIT_PROXY_SSL_CAINFO, GIT_SSL_VERSION, GIT_SSL_CIPHER_LIST, GIT_HTTP_MAX_REQUESTS, GIT_CURL_FTP_NO_EPSV,
// GIT_HTTP_LOW_SPEED_LIMIT, GIT_HTTP_LOW_SPEED_TIME, GIT_HTTP_USER_AGENT,
// no_proxy, NO_PROXY, http_proxy, HTTPS_PROXY, https_proxy, ALL_PROXY, all_proxy
pub fn open_with_environment_overrides(
fallback_directory: impl Into<PathBuf>,
Expand Down
Loading

0 comments on commit 6715587

Please sign in to comment.