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
103103 is_ci = "1.1.1"
104104 anyhow = "1"
105105 tempfile = "3.2.0"
106+serial_test = "0.8.0"
106107
107108 [package.metadata.docs.rs]
108109 features = ["document-features", "max-performance", "one-stop-shop", "unstable", "blocking-network-client"]
git-repository/src/config/cache.rs+58−6
@@ -1,9 +1,10 @@
11 use std::{convert::TryFrom, path::PathBuf};
22
3+use crate::repository;
34 use git_config::{Boolean, Integer};
45
56 use super::{Cache, Error};
6−use crate::{bstr::ByteSlice, permission};
7+use crate::bstr::ByteSlice;
78
89 /// A utility to deal with the cyclic dependency between the ref store and the configuration. The ref-store needs the
910 /// 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
7071 impl Cache {
7172 pub fn from_stage_one(
7273 StageOne {
73− git_dir_config: mut config,
74+ mut git_dir_config,
7475 buf: _,
7576 is_bare,
7677 object_hash,
@@ -79,15 +80,17 @@ impl Cache { · 2 unmodified lines
7980 git_dir: &std::path::Path,
8081 branch_name: Option<&git_ref::FullNameRef>,
8182 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,
8483 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,
8589 ) -> Result<Self, Error> {
8690 let home = std::env::var_os("HOME")
8791 .map(PathBuf::from)
8892 .and_then(|home| home_env.check(home).ok().flatten());
89− // TODO: don't forget to use the canonicalized home for initializing the stacked config.
90− // like git here: https://github.com/git/git/blob/master/config.c#L208:L208
93+
9194 let options = git_config::file::init::Options {
9295 lossy: !cfg!(debug_assertions),
9396 includes: git_config::file::includes::Options::follow(
@@ -98,6 +101,55 @@ impl Cache { · 4 unmodified lines
98101 },
99102 ),
100103 };
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+
101153 config.resolve_includes(options)?;
102154
103155 let excludes_file = config
git-repository/src/open.rs+1−2
@@ -285,9 +285,8 @@ impl ThreadSafeRepository { · 284 unmodified lines
285285 common_dir_ref,
286286 head.as_ref().and_then(|head| head.target.try_name()),
287287 filter_config_section.unwrap_or(crate::config::section::is_trusted),
288− env.xdg_config_home.clone(),
289− env.home.clone(),
290288 crate::path::install_dir().ok().as_deref(),
289+ env.clone(),
291290 )?;
292291
293292 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
2626 pub git_prefix: permission::env_var::Resource,
2727 }
2828
29+impl 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+
2940 impl Permissions {
3041 /// Return permissions similar to what git does when the repository isn't owned by the current user,
3142 /// thus refusing all operations in it.
3243 pub fn strict() -> Self {
3344 Permissions {
3445 git_dir: Access::resource(git_sec::ReadWrite::READ),
35− env: Environment {
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− },
46+ env: Environment::allow_all(),
4047 }
4148 }
4249
@@ -48,11 +55,7 @@ impl Permissions { · 5 unmodified lines
4855 pub fn secure() -> Self {
4956 Permissions {
5057 git_dir: Access::resource(git_sec::ReadWrite::all()),
51− env: Environment {
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− },
58+ env: Environment::allow_all(),
5659 }
5760 }
5861
@@ -61,11 +64,7 @@ impl Permissions { · 2 unmodified lines
6164 pub fn all() -> Self {
6265 Permissions {
6366 git_dir: Access::resource(git_sec::ReadWrite::all()),
64− env: Environment {
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− },
67+ env: Environment::allow_all(),
6968 }
7069 }
7170 }
git-repository/tests/fixtures/generated-archives/make_config_repo.tar.xz+2−2
@@ -1,3 +1,3 @@
11 version https://git-lfs.github.com/spec/v1
2−oid sha256:20b64234f3e8047502160c3c56841b8ddf659798a8b02fc72b5a21bc85dee920
3−size 9212
2+oid sha256:5c16dcf86219e5dd5755e21472c40d289b246d62fb92594378f166a4920bad58
3+size 9276
git-repository/tests/fixtures/make_config_repo.sh+14−2
@@ -3,7 +3,7 @@ set -eu -o pipefail · 2 unmodified lines
33
44 git init -q
55
6−cat <<EOF>>.git/config
6+cat <<EOF >>.git/config
77 [a]
88 bool = on
99 bad-bool = zero
@@ -20,7 +20,19 @@ cat <<EOF>>.git/config · 10 unmodified lines
2020 EOF
2121
2222
23−cat <<EOF>>a.config
23+cat <<EOF >>a.config
2424 [a]
2525 override = from-a.config
2626 EOF
27+cat <<EOF >>b.config
28+[a]
29+ system-override = from-b.config
30+EOF
31+
32+cat <<EOF >>system.config
33+[a]
34+ system = from-system.config
35+ system-override = base
36+[include]
37+ path = ./b.config
38+EOF
git-repository/tests/repository/config.rs+34−1
@@ -1,12 +1,37 @@
11 use crate::named_repo;
22 use git_repository as git;
3+use git_sec::{Access, Permission};
4+use git_testtools::Env;
5+use serial_test::serial;
36 use std::path::Path;
47
58 #[test]
9+#[serial]
610 fn access_values() {
711 for trust in [git_sec::Trust::Full, git_sec::Trust::Reduced] {
812 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();
1035
1136 let config = repo.config_snapshot();
1237
@@ -26,6 +51,14 @@ fn access_values() { · 13 unmodified lines
2651 );
2752
2853 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+ );
2962
3063 assert_eq!(config.boolean("core.missing"), None);
3164 assert_eq!(config.try_boolean("core.missing"), None);