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.
This commit is contained in:
@@ -35,14 +35,13 @@ cargo install --path .
|
|||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
dev_tools <project> <up|down> [--legacy]
|
dev_tools <project> <up|down>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Argument | Description |
|
| Argument | Description |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `project` | Project key (matches `<PROJECT>_DIR` in the config, case-insensitive) |
|
| `project` | Project key (matches `<PROJECT>_DIR` in the config, case-insensitive) |
|
||||||
| `up` / `down` | Start or stop the environment |
|
| `up` / `down` | Start or stop the environment |
|
||||||
| `-l`, `--legacy` | Enable legacy mode: also toggles `conf/global/connections.env` and runs `git update-index` |
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@@ -52,16 +51,13 @@ dev_tools foo up
|
|||||||
|
|
||||||
# Stop it
|
# Stop it
|
||||||
dev_tools foo down
|
dev_tools foo down
|
||||||
|
|
||||||
# Start with legacy env handling
|
|
||||||
dev_tools foo up --legacy
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## What it does
|
## What it does
|
||||||
|
|
||||||
1. Starts or stops the Docker Compose service defined by `DOCKER_SERVICE` / `DOCKER_DIR`.
|
1. Starts or stops the Docker Compose service defined by `DOCKER_SERVICE` / `DOCKER_DIR`.
|
||||||
2. Toggles database connection entries in `<PROJECT_DIR>/conf/local/.env` between `staging` and CI values.
|
2. Modifies `conf/global/connections.env` and marks it with `git update-index --assume-unchanged` to avoid accidental commits.
|
||||||
3. With `--legacy`: additionally modifies `conf/global/connections.env` and marks it with `git update-index --assume-unchanged` to avoid accidental commits.
|
3. Toggles database connection entries in `<PROJECT_DIR>/conf/local/.env`: `up` switches the connection to `ci`, `down` switches it to `staging`.
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
|
|||||||
@@ -13,13 +13,6 @@ pub struct Arguments {
|
|||||||
|
|
||||||
#[arg(help = "Possible values are 'up' and 'down'")]
|
#[arg(help = "Possible values are 'up' and 'down'")]
|
||||||
pub action: String,
|
pub action: String,
|
||||||
|
|
||||||
#[arg(
|
|
||||||
short = 'l',
|
|
||||||
long = "legacy",
|
|
||||||
help = "Legacy mode for old env reading."
|
|
||||||
)]
|
|
||||||
pub legacy: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arguments {
|
impl Arguments {
|
||||||
@@ -40,7 +33,6 @@ mod tests {
|
|||||||
Arguments {
|
Arguments {
|
||||||
project: "test".to_string(),
|
project: "test".to_string(),
|
||||||
action: action.to_string(),
|
action: action.to_string(),
|
||||||
legacy: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+107
-118
@@ -69,11 +69,81 @@ pub fn set_local_db(start: bool, config: &DevToolsConf) -> Result<FileChange> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<FileChange> {
|
pub fn set_dot_env(start: bool, config: &DevToolsConf) -> Result<FileChange> {
|
||||||
let mut docker_change = toggle_after_line("#docker", 4, start, config)?;
|
let local_connection_env = format!(
|
||||||
let cidb_change = toggle_after_line("#cidb", 2, !start, config)?;
|
"{}{}",
|
||||||
docker_change.diffs.extend(cidb_change.diffs);
|
config.project_dir_as_string(),
|
||||||
Ok(docker_change)
|
LOCAL_CONNECTION_PATH
|
||||||
|
);
|
||||||
|
let file: File = File::open(&local_connection_env)
|
||||||
|
.with_context(|| format!("could not read file `{}`", local_connection_env))?;
|
||||||
|
let reader: BufReader<File> = BufReader::new(file);
|
||||||
|
|
||||||
|
let mut modified_lines: Vec<String> = 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<String> = 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";
|
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<FileChange> {
|
|
||||||
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<File> = BufReader::new(file);
|
|
||||||
|
|
||||||
let mut modified_lines: Vec<String> = 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<String> = 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
|
/// 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.
|
/// legacy `connection` or the new `RCC_CONNECTION`), if the line sets it.
|
||||||
fn connection_key(line: &str) -> Option<&'static str> {
|
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]
|
#[test]
|
||||||
fn set_dot_env_merges_diffs_from_both_sections() {
|
fn set_dot_env_merges_diffs_from_both_sections() {
|
||||||
let dir = TempProjectDir::new("merge_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]
|
#[test]
|
||||||
fn file_change_display_no_diffs() {
|
fn file_change_display_no_diffs() {
|
||||||
let change = FileChange {
|
let change = FileChange {
|
||||||
|
|||||||
+1
-6
@@ -22,14 +22,9 @@ fn main() -> Result<()> {
|
|||||||
let start: bool = args.action == "up";
|
let start: bool = args.action == "up";
|
||||||
|
|
||||||
start_docker_compose(&config.container_service, &config.container_dir, start)?;
|
start_docker_compose(&config.container_service, &config.container_dir, start)?;
|
||||||
if args.legacy {
|
|
||||||
println!("[!] Legacy mode active.");
|
|
||||||
print!("{}", set_local_db(start, &config)?);
|
print!("{}", set_local_db(start, &config)?);
|
||||||
toggle_index(start, &config)?;
|
toggle_index(start, &config)?;
|
||||||
} else {
|
|
||||||
println!("[!] Legacy mode deactivated.");
|
|
||||||
}
|
|
||||||
|
|
||||||
print!("{}", set_dot_env(start, &config)?);
|
print!("{}", set_dot_env(start, &config)?);
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
|
|||||||
Reference in New Issue
Block a user