Skip to content

Commit 230e325

Browse files
authored
fix(config): [env] relative paths definition (#16957)
### What does this PR try to resolve? `EnvConfigValue` was wrapped in `Value<T>`, which captured the table-level `Definition` for path resolution. 1. In `merge_helper`, we merge all keys in a table but not the definition of the table itself: <https://github.com/rust-lang/cargo/blob/60960cbe4148295890ccd0f2890c2fb5dd9a0914/src/cargo/util/context/config_value.rs?plain=1#L177-L199> It is probably hard to defined the source definition of a merged struct though, perhaps people shouldn't access it to do anything meaningful. 2. When deserializing a `[env]` struct with `Value<T>`, `Value<T>` delegate to `ValueDeserializer` to deserialize the value, which only takes the struct definition into account. And that turns out to be the stale table definition: <https://github.com/rust-lang/cargo/blob/60960cbe4148295890ccd0f2890c2fb5dd9a0914/src/cargo/util/context/de.rs?plain=1#L119-L137> 3. Unlike file config discovery, `include` is loaded before the including config, hence its definition is stale and is used. This caused in #16954 that `[env]` struct got a stale old config source definition. ### How to test and review this PR? New regression tests should have covered cases in <#16954>, and also captured the non-canonicalized case which is a separate issue. This fix doesn't touch the core problem that a merged `Value<T>` table holds a stale source definition. Holding either new or old doesn't seem correct to me, as it is a merged one. This is really a footgun. Fixes #16954
2 parents 5fbaf43 + b8ba2f0 commit 230e325

3 files changed

Lines changed: 68 additions & 10 deletions

File tree

src/cargo/util/context/schema.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ impl<'de> Deserialize<'de> for ProgressConfig {
422422
enum EnvConfigValueInner {
423423
Simple(String),
424424
WithOptions {
425-
value: String,
425+
value: ConfigRelativePath,
426426
force: bool,
427427
relative: bool,
428428
},
@@ -435,7 +435,7 @@ impl<'de> Deserialize<'de> for EnvConfigValueInner {
435435
{
436436
#[derive(Deserialize)]
437437
struct WithOptions {
438-
value: String,
438+
value: ConfigRelativePath,
439439
#[serde(default)]
440440
force: bool,
441441
#[serde(default)]
@@ -473,13 +473,13 @@ impl<'de> Deserialize<'de> for EnvConfigValueInner {
473473
#[derive(Debug, Deserialize)]
474474
#[serde(transparent)]
475475
pub struct EnvConfigValue {
476-
inner: Value<EnvConfigValueInner>,
476+
inner: EnvConfigValueInner,
477477
}
478478

479479
impl EnvConfigValue {
480480
/// Whether this value should override existing environment variables.
481481
pub fn is_force(&self) -> bool {
482-
match self.inner.val {
482+
match self.inner {
483483
EnvConfigValueInner::Simple(_) => false,
484484
EnvConfigValueInner::WithOptions { force, .. } => force,
485485
}
@@ -490,18 +490,18 @@ impl EnvConfigValue {
490490
/// If `relative = true`,
491491
/// the value is interpreted as a [`ConfigRelativePath`]-like path.
492492
pub fn resolve<'a>(&'a self, cwd: &Path) -> Cow<'a, OsStr> {
493-
match self.inner.val {
493+
match self.inner {
494494
EnvConfigValueInner::Simple(ref s) => Cow::Borrowed(OsStr::new(s.as_str())),
495495
EnvConfigValueInner::WithOptions {
496496
ref value,
497497
relative,
498498
..
499499
} => {
500500
if relative {
501-
let p = self.inner.definition.root(cwd).join(&value);
501+
let p = value.value().definition.root(cwd).join(value.raw_value());
502502
Cow::Owned(p.into_os_string())
503503
} else {
504-
Cow::Borrowed(OsStr::new(value.as_str()))
504+
Cow::Borrowed(OsStr::new(value.raw_value()))
505505
}
506506
}
507507
}

tests/testsuite/cargo_env_config.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,6 @@ fn env_invalid() {
6262
.with_stderr_data(str![[r#"
6363
[ERROR] error in [ROOT]/foo/.cargo/config.toml: could not load config key `env.ENV_TEST_BOOL`
6464
65-
Caused by:
66-
error in [ROOT]/foo/.cargo/config.toml: could not load config key `env.ENV_TEST_BOOL`
67-
6865
Caused by:
6966
invalid type: boolean `false`, expected a string or map
7067

tests/testsuite/config_include.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,3 +645,64 @@ fn env_relative_path_included_from_upper_level() {
645645
str!["[ROOT]/val"],
646646
);
647647
}
648+
649+
#[cargo_test]
650+
fn env_relative_path_included_override() {
651+
// See https://github.com/rust-lang/cargo/issues/16954
652+
write_config_at(
653+
"foo/.cargo/config.toml",
654+
"
655+
include = ['../../inc/inc.toml']
656+
657+
[env]
658+
MY_ENV = { value = 'inner', relative = true }
659+
",
660+
);
661+
write_config_at(
662+
"inc/inc.toml",
663+
"
664+
[env]
665+
MY_ENV = { value = 'outer', relative = true }
666+
",
667+
);
668+
let gctx = GlobalContextBuilder::new().cwd("foo").build();
669+
let env = gctx.env_config().unwrap();
670+
let my_env = env.get("MY_ENV").unwrap();
671+
672+
assert_e2e().eq(my_env.to_str().unwrap(), str!["[ROOT]/foo/inner"]);
673+
}
674+
675+
#[cargo_test]
676+
fn env_relative_path_nested_included_override() {
677+
// See https://github.com/rust-lang/cargo/issues/16954
678+
write_config_at(
679+
"foo/.cargo/config.toml",
680+
"
681+
include = ['../../mid/mid.toml']
682+
683+
[env]
684+
MY_ENV = { value = 'inner', relative = true }
685+
",
686+
);
687+
write_config_at(
688+
"mid/mid.toml",
689+
"
690+
include = ['../deep/deep.toml']
691+
692+
[env]
693+
MY_ENV = { value = 'mid', relative = true }
694+
",
695+
);
696+
write_config_at(
697+
"deep/deep.toml",
698+
"
699+
[env]
700+
MY_ENV = { value = 'deep', relative = true }
701+
",
702+
);
703+
let gctx = GlobalContextBuilder::new().cwd("foo").build();
704+
let env = gctx.env_config().unwrap();
705+
let my_env = env.get("MY_ENV").unwrap();
706+
707+
assert_e2e().eq(my_env.to_str().unwrap(), str!["[ROOT]/foo/inner"]);
708+
}

0 commit comments

Comments
 (0)