From 3873987c193bea0e3a775515787c14503a6e1ee4 Mon Sep 17 00:00:00 2001 From: mace Date: Thu, 17 Sep 2026 15:12:32 +0200 Subject: [PATCH] Always update connections.env and fix double-toggled connection line Drop the --legacy flag: set_local_db and toggle_index now run unconditionally instead of only when -l was passed, since that was the flag's only effect. Fix set_dot_env writing the connection/RCC_CONNECTION line twice with opposing polarity (once from the #docker pass, once from the #cidb pass), which flipped it to the wrong value. Collapsed the three separate open/read/write passes into one, toggling both sections and the connection line in a single scan so the connection line is only ever touched once. --- README.md | 10 +- src/arguments.rs | 8 -- src/config/parse.rs | 225 +++++++++++++++++++++----------------------- src/main.rs | 9 +- 4 files changed, 112 insertions(+), 140 deletions(-) diff --git a/README.md b/README.md index 1781b18..57b3535 100644 --- a/README.md +++ b/README.md @@ -35,14 +35,13 @@ cargo install --path . ## Usage ``` -dev_tools [--legacy] +dev_tools ``` | Argument | Description | |---|---| | `project` | Project key (matches `_DIR` in the config, case-insensitive) | | `up` / `down` | Start or stop the environment | -| `-l`, `--legacy` | Enable legacy mode: also toggles `conf/global/connections.env` and runs `git update-index` | ### Examples @@ -52,16 +51,13 @@ dev_tools foo up # Stop it dev_tools foo down - -# Start with legacy env handling -dev_tools foo up --legacy ``` ## What it does 1. Starts or stops the Docker Compose service defined by `DOCKER_SERVICE` / `DOCKER_DIR`. -2. Toggles database connection entries in `/conf/local/.env` between `staging` and CI values. -3. With `--legacy`: additionally modifies `conf/global/connections.env` and marks it with `git update-index --assume-unchanged` to avoid accidental commits. +2. Modifies `conf/global/connections.env` and marks it with `git update-index --assume-unchanged` to avoid accidental commits. +3. Toggles database connection entries in `/conf/local/.env`: `up` switches the connection to `ci`, `down` switches it to `staging`. ## Tests diff --git a/src/arguments.rs b/src/arguments.rs index 8566b84..6a42897 100644 --- a/src/arguments.rs +++ b/src/arguments.rs @@ -13,13 +13,6 @@ pub struct Arguments { #[arg(help = "Possible values are 'up' and 'down'")] pub action: String, - - #[arg( - short = 'l', - long = "legacy", - help = "Legacy mode for old env reading." - )] - pub legacy: bool, } impl Arguments { @@ -40,7 +33,6 @@ mod tests { Arguments { project: "test".to_string(), action: action.to_string(), - legacy: false, } } diff --git a/src/config/parse.rs b/src/config/parse.rs index 448169c..e516800 100644 --- a/src/config/parse.rs +++ b/src/config/parse.rs @@ -69,11 +69,81 @@ pub fn set_local_db(start: bool, config: &DevToolsConf) -> Result { }) } +const DOCKER_SECTION: &str = "#docker"; +const DOCKER_SECTION_LINES: usize = 4; +const CIDB_SECTION: &str = "#cidb"; +const CIDB_SECTION_LINES: usize = 2; + +/// Toggles the `#docker` and `#cidb` sections and the `connection` line of +/// `conf/local/.env` in a single read/write pass. `#docker` follows `start` +/// directly; `#cidb` and the connection line follow its inverse (`up` means +/// the local docker db is reachable, so `#cidb`'s CI credentials switch off +/// and the connection moves to `ci`; `down` is the reverse, ending on +/// `staging`). Every line outside both sections is checked against +/// `connection_key` exactly once, so it can't be toggled twice. pub fn set_dot_env(start: bool, config: &DevToolsConf) -> Result { - let mut docker_change = toggle_after_line("#docker", 4, start, config)?; - let cidb_change = toggle_after_line("#cidb", 2, !start, config)?; - docker_change.diffs.extend(cidb_change.diffs); - Ok(docker_change) + let local_connection_env = format!( + "{}{}", + config.project_dir_as_string(), + LOCAL_CONNECTION_PATH + ); + let file: File = File::open(&local_connection_env) + .with_context(|| format!("could not read file `{}`", local_connection_env))?; + let reader: BufReader = BufReader::new(file); + + let mut modified_lines: Vec = Vec::new(); + let mut diffs: Vec<(String, String)> = Vec::new(); + let mut section: Option<(bool, usize)> = None; + + for line in reader.lines() { + let line_content = line?; + let trimmed = line_content.trim_start(); + + if let Some((_, remaining)) = section { + if remaining == 0 { + section = None; + } + } + + if trimmed.starts_with(DOCKER_SECTION) { + section = Some((start, DOCKER_SECTION_LINES)); + modified_lines.push(line_content); + continue; + } + if trimmed.starts_with(CIDB_SECTION) { + section = Some((!start, CIDB_SECTION_LINES)); + modified_lines.push(line_content); + continue; + } + + let modified: Option = if let Some((activate, remaining)) = &mut section { + let new_line = toggle(&line_content, *activate); + *remaining -= 1; + (new_line != line_content).then_some(new_line) + } else { + connection_key(&line_content).and_then(|key| { + let value = if start { "ci" } else { "staging" }; + let new_line = format!("{}='{}'", key, value); + (new_line != line_content).then_some(new_line) + }) + }; + + match modified { + Some(new_line) => { + diffs.push((line_content, new_line.clone())); + modified_lines.push(new_line); + } + None => modified_lines.push(line_content), + } + } + + fs::write(&local_connection_env, modified_lines.join("\n")) + .context("Cannot write local connection env")?; + + Ok(FileChange { + path: local_connection_env, + diffs, + }) } const CI_DB_MASTER_IP: &str = "ci.db.master.ip"; @@ -112,68 +182,6 @@ pub(crate) fn should_commented_out(line: &str) -> bool { } } -fn toggle_after_line( - target_line_prefix: &str, - num_lines: usize, - activate: bool, - config: &DevToolsConf, -) -> Result { - let local_connection_env = format!( - "{}{}", - config.project_dir_as_string(), - LOCAL_CONNECTION_PATH - ); - let file: File = File::open(&local_connection_env) - .with_context(|| format!("could not read file `{}`", local_connection_env))?; - let reader: BufReader = BufReader::new(file); - - let mut modified_lines: Vec = Vec::new(); - let mut diffs: Vec<(String, String)> = Vec::new(); - let mut relevant = false; - let mut counter: usize = 0; - - for line in reader.lines() { - let line_content = line?; - - if counter == num_lines { - relevant = false; - } - if line_content.trim_start().starts_with(target_line_prefix) { - relevant = true; - modified_lines.push(line_content); - continue; - } - - let modified: Option = if relevant { - counter += 1; - let new_line = toggle(&line_content, activate); - (new_line != line_content).then_some(new_line) - } else if let Some(key) = connection_key(&line_content) { - let value = if activate { "staging" } else { "ci" }; - let new_line = format!("{}='{}'", key, value); - (new_line != line_content).then_some(new_line) - } else { - None - }; - - match modified { - Some(new_line) => { - diffs.push((line_content, new_line.clone())); - modified_lines.push(new_line); - } - None => modified_lines.push(line_content), - } - } - - fs::write(&local_connection_env, modified_lines.join("\n")) - .context("Cannot write local connection env")?; - - Ok(FileChange { - path: local_connection_env, - diffs, - }) -} - /// Returns the `connection` key as it appears on the given line (either the /// legacy `connection` or the new `RCC_CONNECTION`), if the line sets it. fn connection_key(line: &str) -> Option<&'static str> { @@ -303,58 +311,6 @@ mod tests { ); } - #[test] - fn toggle_after_line_records_diffs_for_toggled_section() { - let dir = TempProjectDir::new("toggle_section"); - fs::write( - dir.join(LOCAL_CONNECTION_PATH), - "#docker\nDOCKER_HOST=tcp://localhost:2375\n#DOCKER_TLS_VERIFY=1\n#DOCKER_CERT_PATH=/certs\nDOCKER_API_VERSION=1.41\nother=1", - ) - .unwrap(); - - let change = toggle_after_line("#docker", 4, true, &config_for(&dir)).unwrap(); - - assert_eq!( - change.diffs, - vec![ - ( - "#DOCKER_TLS_VERIFY=1".to_string(), - "DOCKER_TLS_VERIFY=1".to_string() - ), - ( - "#DOCKER_CERT_PATH=/certs".to_string(), - "DOCKER_CERT_PATH=/certs".to_string() - ), - ] - ); - } - - #[test] - fn toggle_after_line_records_diffs_for_both_connection_keys() { - let dir = TempProjectDir::new("connection_keys"); - fs::write( - dir.join(LOCAL_CONNECTION_PATH), - "#docker\nline1\nline2\nline3\nline4\nconnection='staging'\nRCC_CONNECTION='staging'", - ) - .unwrap(); - - let change = toggle_after_line("#cidb", 2, false, &config_for(&dir)).unwrap(); - - assert_eq!( - change.diffs, - vec![ - ( - "connection='staging'".to_string(), - "connection='ci'".to_string() - ), - ( - "RCC_CONNECTION='staging'".to_string(), - "RCC_CONNECTION='ci'".to_string() - ), - ] - ); - } - #[test] fn set_dot_env_merges_diffs_from_both_sections() { let dir = TempProjectDir::new("merge_sections"); @@ -386,6 +342,39 @@ mod tests { } } + #[test] + fn set_dot_env_merges_diffs_from_both_sections_on_stop() { + let dir = TempProjectDir::new("merge_sections_stop"); + fs::write( + dir.join(LOCAL_CONNECTION_PATH), + "#docker\nDOCKER_HOST=tcp://localhost:2375\n#DOCKER_TLS_VERIFY=1\n#DOCKER_CERT_PATH=/certs\nDOCKER_API_VERSION=1.41\n#cidb\n#ci.db.master.ip=127.0.0.1\n#ci.db.master.port=5432\nconnection='ci'\nRCC_CONNECTION='ci'", + ) + .unwrap(); + + let change = set_dot_env(false, &config_for(&dir)).unwrap(); + + assert_eq!(change.diffs.len(), 6); + for expected in [ + ( + "DOCKER_HOST=tcp://localhost:2375", + "#DOCKER_HOST=tcp://localhost:2375", + ), + ("DOCKER_API_VERSION=1.41", "#DOCKER_API_VERSION=1.41"), + ("#ci.db.master.ip=127.0.0.1", "ci.db.master.ip=127.0.0.1"), + ("#ci.db.master.port=5432", "ci.db.master.port=5432"), + ("connection='ci'", "connection='staging'"), + ("RCC_CONNECTION='ci'", "RCC_CONNECTION='staging'"), + ] { + assert!( + change + .diffs + .contains(&(expected.0.to_string(), expected.1.to_string())), + "missing diff {:?}", + expected + ); + } + } + #[test] fn file_change_display_no_diffs() { let change = FileChange { diff --git a/src/main.rs b/src/main.rs index 4ab3287..befd15a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,14 +22,9 @@ fn main() -> Result<()> { let start: bool = args.action == "up"; start_docker_compose(&config.container_service, &config.container_dir, start)?; - if args.legacy { - println!("[!] Legacy mode active."); - print!("{}", set_local_db(start, &config)?); - toggle_index(start, &config)?; - } else { - println!("[!] Legacy mode deactivated."); - } + print!("{}", set_local_db(start, &config)?); + toggle_index(start, &config)?; print!("{}", set_dot_env(start, &config)?); println!(