mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-08 03:10:26 +00:00
hooks: warn when a matcher pattern fails to compile
A hook matcher regex that failed to compile was dropped silently in compile_matchers, while matches() carried a comment claiming the failure had "already warned" — which never happened. A typo'd matcher made a hook silently never fire, with no signal. Emit a warning at construction (naming the pattern and error), flatten the nested conditionals, and correct the stale comment. Add a test asserting an invalid pattern never matches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6d6a1d56a1
commit
4242d2e71f
1 changed files with 38 additions and 6 deletions
|
|
@ -80,11 +80,22 @@ impl HookRunner {
|
|||
fn compile_matchers(config: &HookSettings) -> HashMap<String, regex::Regex> {
|
||||
let mut map = HashMap::new();
|
||||
for hook in &config.hooks {
|
||||
if let Some(ref pattern) = hook.matcher {
|
||||
if !map.contains_key(pattern) {
|
||||
if let Ok(re) = regex::Regex::new(pattern) {
|
||||
map.insert(pattern.clone(), re);
|
||||
}
|
||||
let Some(pattern) = hook.matcher.as_ref() else {
|
||||
continue;
|
||||
};
|
||||
if map.contains_key(pattern) {
|
||||
continue;
|
||||
}
|
||||
match regex::Regex::new(pattern) {
|
||||
Ok(re) => {
|
||||
map.insert(pattern.clone(), re);
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::warn!(
|
||||
pattern = %pattern,
|
||||
error = %error,
|
||||
"hook matcher pattern failed to compile; hooks using it will never match"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -148,7 +159,8 @@ impl HookRunner {
|
|||
return true;
|
||||
};
|
||||
let Some(re) = self.compiled_matchers.get(pattern) else {
|
||||
// Pattern failed to compile during construction — already warned
|
||||
// Pattern failed to compile at construction (warned there); treat as
|
||||
// no match so a broken matcher never silently matches everything.
|
||||
return false;
|
||||
};
|
||||
[
|
||||
|
|
@ -431,6 +443,26 @@ mod tests {
|
|||
assert!(runner.filter_hooks(&ctx).is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn invalid_matcher_pattern_never_matches() {
|
||||
// A matcher that fails to compile is dropped at construction (with a
|
||||
// warning); the hook must then never match rather than silently match
|
||||
// everything.
|
||||
let mut hook = make_hook(HookEvent::StageStart, "bad-matcher");
|
||||
hook.matcher = Some("[unterminated(".into());
|
||||
let config = HookSettings { hooks: vec![hook] };
|
||||
let runner = HookRunner::with_executor(
|
||||
config,
|
||||
Arc::new(MockExecutor {
|
||||
decision: HookDecision::Proceed,
|
||||
}),
|
||||
);
|
||||
|
||||
let mut ctx = make_context(HookEvent::StageStart);
|
||||
ctx.node_id = Some("agent".into());
|
||||
assert!(runner.filter_hooks(&ctx).is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn blocking_hook_block_decision() {
|
||||
let config = HookSettings {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue