diff --git a/Taskfile.yaml b/Taskfile.yaml index d57fc08..46705f3 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -22,6 +22,11 @@ tasks: - cargo clippy --all-targets --all-features -- -D warnings - cargo test --all-features + lint:fix: + desc: "Auto-fix clippy lints where possible (same scope as `task test`)" + cmds: + - cargo clippy --fix --all-targets --all-features --allow-dirty --allow-staged + build: desc: "Build debug binary for the host" silent: true diff --git a/src/handlers/set_value.rs b/src/handlers/set_value.rs index 6223af2..d689d80 100644 --- a/src/handlers/set_value.rs +++ b/src/handlers/set_value.rs @@ -34,10 +34,16 @@ pub fn set_value(options: &Options) { fs::write(&options.full_env_path, lines.join("\n")).expect("Failed to write .env file"); } else { // Append new key to end of file + let existing = fs::read(&options.full_env_path).expect("Failed to read .env file"); + let needs_leading_newline = !existing.is_empty() && existing.last() != Some(&b'\n'); + let mut file = fs::OpenOptions::new() .append(true) .open(&options.full_env_path) .expect("Failed to open .env file for appending"); + if needs_leading_newline { + writeln!(file).expect("Failed to append to .env file"); + } writeln!(file, "{}", new_line).expect("Failed to append to .env file"); } } diff --git a/tests/set_and_delete.rs b/tests/set_and_delete.rs index 6d3a917..5deb1d1 100644 --- a/tests/set_and_delete.rs +++ b/tests/set_and_delete.rs @@ -464,3 +464,39 @@ fn add_and_delete_single_key_preserves_file() { hash2 ); } + +#[test] +fn add_key_to_file_without_trailing_newline() { + use std::io::Write; + let mut tmp = NamedTempFile::new().unwrap(); + tmp.write_all(b"FOO=bar").unwrap(); // deliberately no trailing newline + tmp.flush().unwrap(); + + bin() + .arg("BAZ") + .arg("--set") + .arg("qux") + .arg("--file") + .arg(tmp.path()) + .assert() + .success(); + + // Both keys must be readable and intact. + bin() + .arg("FOO") + .arg("--file") + .arg(tmp.path()) + .assert() + .success() + .stdout("bar\n"); + bin() + .arg("BAZ") + .arg("--file") + .arg(tmp.path()) + .assert() + .success() + .stdout("qux\n"); + + let content = fs::read_to_string(tmp.path()).unwrap(); + assert_eq!(content, "FOO=bar\nBAZ=qux\n"); +}