History

feat: repository now initializes global configuration files and resolves includes (#331)

Sebastian Thiel committed 4 years ago

Commit ebedd031 parent bd3f4d07 files · +124 −28Browse files

Unified · Split

7 files
  1. git-repository/Cargo.toml +1 −0
  2. git-repository/src/config/cache.rs +58 −6
  3. git-repository/src/open.rs +1 −2
  4. git-repository/src/repository/permissions.rs +14 −15
  5. git-repository/tests/fixtures/generated-archives/make_config_repo.tar.xz +2 −2
  6. git-repository/tests/fixtures/make_config_repo.sh +14 −2
  7. git-repository/tests/repository/config.rs +34 −1
git-repository/Cargo.toml+1−0
@@ -103,6 +103,7 @@ git-testtools = { path = "../tests/tools" } · 102 unmodified lines
103is_ci = "1.1.1"103is_ci = "1.1.1"
104anyhow = "1"104anyhow = "1"
105tempfile = "3.2.0"105tempfile = "3.2.0"
106serial_test = "0.8.0"
106 107
107[package.metadata.docs.rs]108[package.metadata.docs.rs]
108features = ["document-features", "max-performance", "one-stop-shop", "unstable", "blocking-network-client"]109features = ["document-features", "max-performance", "one-stop-shop", "unstable", "blocking-network-client"]
git-repository/src/config/cache.rs+58−6
@@ -1,9 +1,10 @@
1use std::{convert::TryFrom, path::PathBuf};1use std::{convert::TryFrom, path::PathBuf};
2 2
3use crate::repository;
3use git_config::{Boolean, Integer};4use git_config::{Boolean, Integer};
4 5
5use super::{Cache, Error};6use super::{Cache, Error};
6use crate::{bstr::ByteSlice, permission};7use crate::bstr::ByteSlice;
7 8
8/// A utility to deal with the cyclic dependency between the ref store and the configuration. The ref-store needs the9/// A utility to deal with the cyclic dependency between the ref store and the configuration. The ref-store needs the
9/// object hash kind, and the configuration needs the current branch name to resolve conditional includes with `onbranch`.10/// object hash kind, and the configuration needs the current branch name to resolve conditional includes with `onbranch`.
@@ -70,7 +71,7 @@ impl StageOne { · 60 unmodified lines
70impl Cache {71impl Cache {
71 pub fn from_stage_one(72 pub fn from_stage_one(
72 StageOne {73 StageOne {
73 git_dir_config: mut config,74 mut git_dir_config,
74 buf: _,75 buf: _,
75 is_bare,76 is_bare,
76 object_hash,77 object_hash,
@@ -79,15 +80,17 @@ impl Cache { · 2 unmodified lines
79 git_dir: &std::path::Path,80 git_dir: &std::path::Path,
80 branch_name: Option<&git_ref::FullNameRef>,81 branch_name: Option<&git_ref::FullNameRef>,
81 mut filter_config_section: fn(&git_config::file::Metadata) -> bool,82 mut filter_config_section: fn(&git_config::file::Metadata) -> bool,
82 xdg_config_home_env: permission::env_var::Resource,
83 home_env: permission::env_var::Resource,
84 git_install_dir: Option<&std::path::Path>,83 git_install_dir: Option<&std::path::Path>,
84 repository::permissions::Environment {
85 git_prefix,
86 home: home_env,
87 xdg_config_home: xdg_config_home_env,
88 }: repository::permissions::Environment,
85 ) -> Result<Self, Error> {89 ) -> Result<Self, Error> {
86 let home = std::env::var_os("HOME")90 let home = std::env::var_os("HOME")
87 .map(PathBuf::from)91 .map(PathBuf::from)
88 .and_then(|home| home_env.check(home).ok().flatten());92 .and_then(|home| home_env.check(home).ok().flatten());
89 // TODO: don't forget to use the canonicalized home for initializing the stacked config.93
90 // like git here: https://github.com/git/git/blob/master/config.c#L208:L208
91 let options = git_config::file::init::Options {94 let options = git_config::file::init::Options {
92 lossy: !cfg!(debug_assertions),95 lossy: !cfg!(debug_assertions),
93 includes: git_config::file::includes::Options::follow(96 includes: git_config::file::includes::Options::follow(
@@ -98,6 +101,55 @@ impl Cache { · 4 unmodified lines
98 },101 },
99 ),102 ),
100 };103 };
104
105 let mut config = {
106 let home_env = &home_env;
107 let xdg_config_home_env = &xdg_config_home_env;
108 let git_prefix = &git_prefix;
109 let metas = [git_config::source::Kind::System, git_config::source::Kind::Global]
110 .iter()
111 .flat_map(|kind| kind.sources())
112 .filter_map(|source| {
113 let path = source
114 .storage_location(&mut |name| {
115 match name {
116 git_ if git_.starts_with("GIT_") => Some(git_prefix),
117 "XDG_CONFIG_HOME" => Some(xdg_config_home_env),
118 "HOME" => Some(home_env),
119 _ => None,
120 }
121 .and_then(|perm| std::env::var_os(name).and_then(|val| perm.check(val).ok().flatten()))
122 })
123 .and_then(|p| p.is_file().then(|| p)) // todo: allow it to skip non-existing ones in the options to save check
124 .map(|p| p.into_owned());
125
126 git_config::file::Metadata {
127 path,
128 source: *source,
129 level: 0,
130 trust: git_sec::Trust::Full,
131 }
132 .into()
133 });
134
135 let no_include_options = git_config::file::init::Options {
136 includes: git_config::file::includes::Options::no_includes(),
137 ..options
138 };
139 git_dir_config.resolve_includes(options)?;
140 match git_config::File::from_paths_metadata(metas, no_include_options).map_err(|err| match err {
141 git_config::file::init::from_paths::Error::Init(err) => Error::from(err),
142 git_config::file::init::from_paths::Error::Io(err) => err.into(),
143 })? {
144 Some(mut globals) => {
145 globals.resolve_includes(options)?;
146 globals.append(git_dir_config);
147 globals
148 }
149 None => git_dir_config,
150 }
151 };
152
101 config.resolve_includes(options)?;153 config.resolve_includes(options)?;
102 154
103 let excludes_file = config155 let excludes_file = config
git-repository/src/open.rs+1−2
@@ -285,9 +285,8 @@ impl ThreadSafeRepository { · 284 unmodified lines
285 common_dir_ref,285 common_dir_ref,
286 head.as_ref().and_then(|head| head.target.try_name()),286 head.as_ref().and_then(|head| head.target.try_name()),
287 filter_config_section.unwrap_or(crate::config::section::is_trusted),287 filter_config_section.unwrap_or(crate::config::section::is_trusted),
288 env.xdg_config_home.clone(),
289 env.home.clone(),
290 crate::path::install_dir().ok().as_deref(),288 crate::path::install_dir().ok().as_deref(),
289 env.clone(),
291 )?;290 )?;
292 291
293 if **git_dir_perm != git_sec::ReadWrite::all() {292 if **git_dir_perm != git_sec::ReadWrite::all() {
git-repository/src/repository/permissions.rs+14−15
@@ -26,17 +26,24 @@ pub struct Environment { · 25 unmodified lines
26 pub git_prefix: permission::env_var::Resource,26 pub git_prefix: permission::env_var::Resource,
27}27}
28 28
29impl Environment {
30 /// Allow access to the entire environment.
31 pub fn allow_all() -> Self {
32 Environment {
33 xdg_config_home: Access::resource(git_sec::Permission::Allow),
34 home: Access::resource(git_sec::Permission::Allow),
35 git_prefix: Access::resource(git_sec::Permission::Allow),
36 }
37 }
38}
39
29impl Permissions {40impl Permissions {
30 /// Return permissions similar to what git does when the repository isn't owned by the current user,41 /// Return permissions similar to what git does when the repository isn't owned by the current user,
31 /// thus refusing all operations in it.42 /// thus refusing all operations in it.
32 pub fn strict() -> Self {43 pub fn strict() -> Self {
33 Permissions {44 Permissions {
34 git_dir: Access::resource(git_sec::ReadWrite::READ),45 git_dir: Access::resource(git_sec::ReadWrite::READ),
35 env: Environment {46 env: Environment::allow_all(),
36 xdg_config_home: Access::resource(git_sec::Permission::Allow),
37 home: Access::resource(git_sec::Permission::Allow),
38 git_prefix: Access::resource(git_sec::Permission::Allow),
39 },
40 }47 }
41 }48 }
42 49
@@ -48,11 +55,7 @@ impl Permissions { · 5 unmodified lines
48 pub fn secure() -> Self {55 pub fn secure() -> Self {
49 Permissions {56 Permissions {
50 git_dir: Access::resource(git_sec::ReadWrite::all()),57 git_dir: Access::resource(git_sec::ReadWrite::all()),
51 env: Environment {58 env: Environment::allow_all(),
52 xdg_config_home: Access::resource(git_sec::Permission::Allow),
53 home: Access::resource(git_sec::Permission::Allow),
54 git_prefix: Access::resource(git_sec::Permission::Allow),
55 },
56 }59 }
57 }60 }
58 61
@@ -61,11 +64,7 @@ impl Permissions { · 2 unmodified lines
61 pub fn all() -> Self {64 pub fn all() -> Self {
62 Permissions {65 Permissions {
63 git_dir: Access::resource(git_sec::ReadWrite::all()),66 git_dir: Access::resource(git_sec::ReadWrite::all()),
64 env: Environment {67 env: Environment::allow_all(),
65 xdg_config_home: Access::resource(git_sec::Permission::Allow),
66 home: Access::resource(git_sec::Permission::Allow),
67 git_prefix: Access::resource(git_sec::Permission::Allow),
68 },
69 }68 }
70 }69 }
71}70}
git-repository/tests/fixtures/generated-archives/make_config_repo.tar.xz+2−2
@@ -1,3 +1,3 @@
1version https://git-lfs.github.com/spec/v11version https://git-lfs.github.com/spec/v1
2oid sha256:20b64234f3e8047502160c3c56841b8ddf659798a8b02fc72b5a21bc85dee9202oid sha256:5c16dcf86219e5dd5755e21472c40d289b246d62fb92594378f166a4920bad58
3size 92123size 9276
git-repository/tests/fixtures/make_config_repo.sh+14−2
@@ -3,7 +3,7 @@ set -eu -o pipefail · 2 unmodified lines
3 3
4git init -q4git init -q
5 5
6cat <<EOF>>.git/config6cat <<EOF >>.git/config
7[a]7[a]
8 bool = on8 bool = on
9 bad-bool = zero9 bad-bool = zero
@@ -20,7 +20,19 @@ cat <<EOF>>.git/config · 10 unmodified lines
20EOF20EOF
21 21
22 22
23cat <<EOF>>a.config23cat <<EOF >>a.config
24[a]24[a]
25 override = from-a.config25 override = from-a.config
26EOF26EOF
27cat <<EOF >>b.config
28[a]
29 system-override = from-b.config
30EOF
31
32cat <<EOF >>system.config
33[a]
34 system = from-system.config
35 system-override = base
36[include]
37 path = ./b.config
38EOF
git-repository/tests/repository/config.rs+34−1
@@ -1,12 +1,37 @@
1use crate::named_repo;1use crate::named_repo;
2use git_repository as git;2use git_repository as git;
3use git_sec::{Access, Permission};
4use git_testtools::Env;
5use serial_test::serial;
3use std::path::Path;6use std::path::Path;
4 7
5#[test]8#[test]
9#[serial]
6fn access_values() {10fn access_values() {
7 for trust in [git_sec::Trust::Full, git_sec::Trust::Reduced] {11 for trust in [git_sec::Trust::Full, git_sec::Trust::Reduced] {
8 let repo = named_repo("make_config_repo.sh").unwrap();12 let repo = named_repo("make_config_repo.sh").unwrap();
9 let repo = git::open_opts(repo.git_dir(), repo.open_options().clone().with(trust)).unwrap();13 let _env = Env::new().set(
14 "GIT_CONFIG_SYSTEM",
15 repo.work_dir()
16 .expect("present")
17 .join("system.config")
18 .canonicalize()
19 .unwrap()
20 .display()
21 .to_string(),
22 );
23 let repo = git::open_opts(
24 repo.git_dir(),
25 repo.open_options().clone().with(trust).permissions(git::Permissions {
26 env: git::permissions::Environment {
27 xdg_config_home: Access::resource(Permission::Deny),
28 home: Access::resource(Permission::Deny),
29 ..git::permissions::Environment::allow_all()
30 },
31 ..Default::default()
32 }),
33 )
34 .unwrap();
10 35
11 let config = repo.config_snapshot();36 let config = repo.config_snapshot();
12 37
@@ -26,6 +51,14 @@ fn access_values() { · 13 unmodified lines
26 );51 );
27 52
28 assert_eq!(config.string("a.override").expect("present").as_ref(), "from-a.config");53 assert_eq!(config.string("a.override").expect("present").as_ref(), "from-a.config");
54 assert_eq!(
55 config.string("a.system").expect("present").as_ref(),
56 "from-system.config"
57 );
58 assert_eq!(
59 config.string("a.system-override").expect("present").as_ref(),
60 "from-b.config"
61 );
29 62
30 assert_eq!(config.boolean("core.missing"), None);63 assert_eq!(config.boolean("core.missing"), None);
31 assert_eq!(config.try_boolean("core.missing"), None);64 assert_eq!(config.try_boolean("core.missing"), None);