Restore connections.env without triggering post-checkout hooks
git checkout fires post-checkout even for a single-file checkout (flag=0), which was tripping an rsync hook in the target repo. Read the committed blob with git cat-file instead and write it directly - plumbing, no worktree update, no hooks - with a test that plants a post-checkout hook and asserts it never runs.
This commit is contained in:
@@ -56,7 +56,7 @@ dev_tools foo down
|
||||
## What it does
|
||||
|
||||
1. Starts or stops the Docker Compose service defined by `DOCKER_SERVICE` / `DOCKER_DIR`.
|
||||
2. `conf/global/connections.env`: `up` toggles it to the CI credentials and marks it `git update-index --assume-unchanged` to avoid accidental commits; `down` unmarks it and restores it to its committed state with `git checkout` (rather than computing the reverse toggle), so it's guaranteed byte-for-byte identical to HEAD.
|
||||
2. `conf/global/connections.env`: `up` toggles it to the CI credentials and marks it `git update-index --assume-unchanged` to avoid accidental commits; `down` unmarks it and restores it to its committed state by reading the committed blob with `git cat-file` (rather than computing the reverse toggle or running `git checkout`, which would fire the repo's `post-checkout` hook), so it's guaranteed byte-for-byte identical to HEAD.
|
||||
3. Toggles database connection entries in `<PROJECT_DIR>/conf/local/.env`: `up` switches the connection to `ci`, `down` switches it to `staging`.
|
||||
|
||||
## Tests
|
||||
|
||||
+51
-12
@@ -1,6 +1,7 @@
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
|
||||
use anyhow::{ensure, Result};
|
||||
use anyhow::{ensure, Context, Result};
|
||||
|
||||
use crate::{config::parse::GLOBAL_CONNECTION_PATH, env::config::DevToolsConf};
|
||||
|
||||
@@ -36,20 +37,31 @@ pub fn toggle_index(start: bool, config: &DevToolsConf) -> Result<()> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Restores `connections.env` to its committed state via `git checkout`,
|
||||
/// used on `down` instead of computing the reverse toggle so the file ends
|
||||
/// up byte-for-byte identical to HEAD, with no risk of a parsing mismatch.
|
||||
/// Restores `connections.env` to its committed state, used on `down` instead
|
||||
/// of computing the reverse toggle so the file ends up byte-for-byte
|
||||
/// identical to HEAD, with no risk of a parsing mismatch. Reads the
|
||||
/// committed blob with `git cat-file` and writes it directly rather than
|
||||
/// running `git checkout`, since `checkout` fires `post-checkout` even for a
|
||||
/// single-file checkout and `cat-file` (plumbing, no worktree update) never
|
||||
/// triggers hooks.
|
||||
pub fn restore_connections_env(config: &DevToolsConf) -> Result<()> {
|
||||
let project_path = format!(
|
||||
"{}{}",
|
||||
config.project_dir_as_string(),
|
||||
GLOBAL_CONNECTION_PATH
|
||||
let project_dir = config.project_dir_as_string();
|
||||
let project_path = format!("{project_dir}{GLOBAL_CONNECTION_PATH}");
|
||||
|
||||
let output = Command::new("git")
|
||||
.current_dir(&project_dir)
|
||||
.args(["cat-file", "-p", &format!("HEAD:{GLOBAL_CONNECTION_PATH}")])
|
||||
.output()?;
|
||||
|
||||
ensure!(
|
||||
output.status.success(),
|
||||
"git cat-file -p HEAD:{GLOBAL_CONNECTION_PATH} failed with {}",
|
||||
output.status
|
||||
);
|
||||
|
||||
run_git(
|
||||
&config.project_dir_as_string(),
|
||||
&["checkout", "--", &project_path],
|
||||
)
|
||||
fs::write(&project_path, output.stdout).context("Cannot write connections.env")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -141,6 +153,33 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restore_connections_env_does_not_trigger_post_checkout_hook() {
|
||||
let repo = TempGitRepo::new("no_hook");
|
||||
repo.commit_connections_env("ci.db.user=\"m.rothenhaeusler\"\n");
|
||||
fs::write(repo.join(GLOBAL_CONNECTION_PATH), "corrupted content\n").unwrap();
|
||||
|
||||
let marker = repo.join("hook-ran");
|
||||
let hooks_dir = repo.join(".git/hooks");
|
||||
fs::create_dir_all(&hooks_dir).unwrap();
|
||||
let hook_path = hooks_dir.join("post-checkout");
|
||||
fs::write(
|
||||
&hook_path,
|
||||
format!("#!/bin/sh\ntouch {}\n", marker.display()),
|
||||
)
|
||||
.unwrap();
|
||||
let mut perms = fs::metadata(&hook_path).unwrap().permissions();
|
||||
std::os::unix::fs::PermissionsExt::set_mode(&mut perms, 0o755);
|
||||
fs::set_permissions(&hook_path, perms).unwrap();
|
||||
|
||||
restore_connections_env(&config_for(&repo)).unwrap();
|
||||
|
||||
assert!(
|
||||
!marker.exists(),
|
||||
"restore_connections_env must not trigger post-checkout hooks"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restore_connections_env_fails_when_not_a_git_repo() {
|
||||
let dir = std::env::temp_dir().join(format!(
|
||||
|
||||
Reference in New Issue
Block a user