refactor(nothooks): make hook runner language-agnostic
Some checks failed
Public Repo Readiness / Check Public Repo Readiness (push) Failing after 6s
Some checks failed
Public Repo Readiness / Check Public Repo Readiness (push) Failing after 6s
Remove hardcoded nu interpreter. HookSpec gains an optional interpreter field; runner infers from file extension (.sh, .nu, .py, .rb, .zsh, .bash) when not set. Integration tests now use sh — no nu runtime dep on CI.
This commit is contained in:
@@ -4,6 +4,28 @@ use notcore::{HookPhase, HookSpec};
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
fn resolve_interpreter(spec: &HookSpec) -> Result<String, String> {
|
||||
if let Some(interp) = &spec.interpreter {
|
||||
return Ok(interp.clone());
|
||||
}
|
||||
let ext = std::path::Path::new(&spec.script)
|
||||
.extension()
|
||||
.and_then(|e| e.to_str())
|
||||
.unwrap_or("");
|
||||
match ext {
|
||||
"nu" => Ok("nu".into()),
|
||||
"sh" => Ok("sh".into()),
|
||||
"bash" => Ok("bash".into()),
|
||||
"zsh" => Ok("zsh".into()),
|
||||
"py" => Ok("python3".into()),
|
||||
"rb" => Ok("ruby".into()),
|
||||
_ => Err(format!(
|
||||
"cannot infer interpreter: no interpreter set and unknown extension {:?}",
|
||||
ext
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HookRunner {
|
||||
state_dir: PathBuf,
|
||||
force: bool,
|
||||
@@ -31,8 +53,11 @@ impl HookRunner {
|
||||
return HookResult::Skipped;
|
||||
}
|
||||
|
||||
// TODO: support other shells
|
||||
let result = Command::new("nu").arg(&spec.script).status();
|
||||
let interp = match resolve_interpreter(spec) {
|
||||
Ok(i) => i,
|
||||
Err(msg) => return HookResult::Failed(msg),
|
||||
};
|
||||
let result = Command::new(&interp).arg(&spec.script).status();
|
||||
|
||||
match result {
|
||||
Ok(status) if status.success() => {
|
||||
|
||||
@@ -4,19 +4,20 @@ use std::fs;
|
||||
use tempfile::TempDir;
|
||||
|
||||
fn make_hook_script(dir: &TempDir, name: &str, content: &str) -> HookSpec {
|
||||
let path = dir.path().join(format!("{name}.nu"));
|
||||
let path = dir.path().join(format!("{name}.sh"));
|
||||
fs::write(&path, content).unwrap();
|
||||
HookSpec {
|
||||
name: name.to_string(),
|
||||
script: path.to_str().unwrap().to_string(),
|
||||
phase: HookPhase::Dot,
|
||||
interpreter: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hook_success() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let spec = make_hook_script(&dir, "ok-hook", "print hello");
|
||||
let spec = make_hook_script(&dir, "ok-hook", "echo hello");
|
||||
let runner = HookRunner::new(dir.path().to_path_buf());
|
||||
let result = runner.run_hook(&spec);
|
||||
assert!(matches!(result, HookResult::Ok));
|
||||
@@ -25,7 +26,7 @@ fn test_hook_success() {
|
||||
#[test]
|
||||
fn test_hook_failure() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let spec = make_hook_script(&dir, "fail-hook", "exit 1\n");
|
||||
let spec = make_hook_script(&dir, "fail-hook", "exit 1");
|
||||
let runner = HookRunner::new(dir.path().to_path_buf());
|
||||
let result = runner.run_hook(&spec);
|
||||
assert!(matches!(result, HookResult::Failed(_)));
|
||||
@@ -34,7 +35,7 @@ fn test_hook_failure() {
|
||||
#[test]
|
||||
fn test_setup_hook_skipped_on_rerun() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let mut spec = make_hook_script(&dir, "setup-hook", "print ran");
|
||||
let mut spec = make_hook_script(&dir, "setup-hook", "echo ran");
|
||||
spec.phase = notcore::HookPhase::Setup;
|
||||
|
||||
let runner = HookRunner::new(dir.path().to_path_buf());
|
||||
@@ -49,7 +50,7 @@ fn test_setup_hook_skipped_on_rerun() {
|
||||
#[test]
|
||||
fn test_setup_hook_force_reruns() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let mut spec = make_hook_script(&dir, "force-hook", "print ran");
|
||||
let mut spec = make_hook_script(&dir, "force-hook", "echo ran");
|
||||
spec.phase = notcore::HookPhase::Setup;
|
||||
|
||||
let runner = HookRunner::new(dir.path().to_path_buf());
|
||||
|
||||
Reference in New Issue
Block a user