Remove the one-time env-to-config migration from the settings

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-09 10:38:06 +01:00
1 parent 151a92959d
commit c9f553dc60
8 files changed
+9 -157

No files matched your search

-71
View File
@@ -470,57 +470,6 @@ pub fn load_or_init(path: &Path) -> Result<(Config, String)> {
Ok((cfg, text))
}
/// Replace `${NAME}` references in all string values with the environment's values,
/// so the settings live in the config only. Comments and formatting stay; references
/// to unset variables are kept. Returns (new text, replaced names, unset names).
pub fn inline_env(text: &str) -> Result<(String, Vec<String>, Vec<String>)> {
use std::collections::BTreeSet;
let mut doc: toml_edit::DocumentMut = text.parse().context("config is not valid TOML")?;
let re = regex::Regex::new(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}").unwrap();
let (mut done, mut missing) = (BTreeSet::new(), BTreeSet::new());
let mut fix = |s: &str| -> Option<String> {
if !re.is_match(s) {
return None;
}
let out = re.replace_all(s, |c: &regex::Captures| match std::env::var(&c[1]).ok().filter(|v| !v.is_empty()) {
Some(v) => {
done.insert(c[1].to_string());
v
}
None => {
missing.insert(c[1].to_string());
c[0].to_string()
}
});
(out != s).then(|| out.into_owned())
};
fn walk_value(v: &mut toml_edit::Value, fix: &mut dyn FnMut(&str) -> Option<String>) {
match v {
toml_edit::Value::String(f) => {
if let Some(new) = fix(f.value()) {
let decor = f.decor().clone();
let mut nv = toml_edit::Formatted::new(new);
*nv.decor_mut() = decor;
*f = nv;
}
}
toml_edit::Value::Array(a) => a.iter_mut().for_each(|x| walk_value(x, fix)),
toml_edit::Value::InlineTable(t) => t.iter_mut().for_each(|(_, x)| walk_value(x, fix)),
_ => {}
}
}
fn walk_item(i: &mut toml_edit::Item, fix: &mut dyn FnMut(&str) -> Option<String>) {
match i {
toml_edit::Item::Value(v) => walk_value(v, fix),
toml_edit::Item::Table(t) => t.iter_mut().for_each(|(_, x)| walk_item(x, fix)),
toml_edit::Item::ArrayOfTables(a) => a.iter_mut().for_each(|t| t.iter_mut().for_each(|(_, x)| walk_item(x, fix))),
toml_edit::Item::None => {}
}
}
walk_item(doc.as_item_mut(), &mut fix);
Ok((doc.to_string(), done.into_iter().collect(), missing.into_iter().collect()))
}
/// A structured edit coming from the UI. Applied with toml_edit so comments survive.
#[derive(Debug, Deserialize)]
pub struct ConfigPatch {
@@ -667,26 +616,6 @@ pub fn redacted_json(cfg: &Config) -> serde_json::Value {
v
}
#[cfg(test)]
mod inline_env_tests {
#[test]
fn replaces_set_variables_and_keeps_comments() {
// SAFETY: test-only, the variable names are unique to this test.
unsafe {
std::env::set_var("CC_TEST_INLINE_KEY", "sk-123");
std::env::remove_var("CC_TEST_INLINE_UNSET");
}
let text = "# keys: use ${NAME}\n[providers.a]\napi_key = \"${CC_TEST_INLINE_KEY}\" # stored\nheaders = { Authorization = \"Bearer ${CC_TEST_INLINE_KEY}\" }\nother = \"${CC_TEST_INLINE_UNSET}\"\n";
let (out, done, missing) = super::inline_env(text).unwrap();
assert!(out.contains("api_key = \"sk-123\" # stored"), "{out}");
assert!(out.contains("Bearer sk-123"));
assert!(out.contains("# keys: use ${NAME}"), "comments untouched");
assert!(out.contains("${CC_TEST_INLINE_UNSET}"));
assert_eq!(done, vec!["CC_TEST_INLINE_KEY"]);
assert_eq!(missing, vec!["CC_TEST_INLINE_UNSET"]);
}
}
#[cfg(test)]
mod tests {
use super::*;