Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions crates/prek/src/cli/run/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -764,15 +764,23 @@ mod tests {

#[cfg(unix)]
#[test]
fn filename_filter_skips_non_utf8_paths_with_regex_include() {
fn filename_filter_regex_matches_valid_suffix_of_non_utf8_path() {
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt as _;

let include = regex_pattern(r".*\.py$");
let include = regex_pattern(r"\.py$");
let path = Path::new(OsStr::from_bytes(b"bad-\xff.py"));
let filter = FilenameFilter::new(Some(&include), None);

assert!(!filter.matches(path));
assert!(filter.matches(path));
}

#[test]
fn filename_filter_regex_keeps_unicode_character_classes() {
let include = regex_pattern(r"^\w+\.py$");
let filter = FilenameFilter::new(Some(&include), None);

assert!(filter.matches(Path::new("café.py")));
}

#[test]
Expand Down
20 changes: 11 additions & 9 deletions crates/prek/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::fmt::Display;
use std::path::Path;

use anyhow::Result;
use fancy_regex::Regex;
use fancy_regex::{BytesMode, Regex, RegexBuilder};
use globset::{Glob, GlobSet};
use itertools::Itertools;
use owo_colors::OwoColorize;
Expand Down Expand Up @@ -327,16 +327,18 @@ impl FilePattern {
}

pub(crate) fn regex(pattern: &str) -> Result<Self, fancy_regex::Error> {
Ok(Self::Regex(Regex::new(pattern)?))
let regex = RegexBuilder::new(pattern)
.bytes_mode(BytesMode::UnicodeBytes)
Comment thread
j178 marked this conversation as resolved.
.build()?;
Ok(Self::Regex(regex))
}

pub(crate) fn is_match(&self, path: &Path) -> bool {
match self {
FilePattern::Never => false,
// Regex patterns are text matchers; globs can match OS paths directly.
FilePattern::Regex(regex) => path
.to_str()
.is_some_and(|path| regex.is_match(path).unwrap_or(false)),
FilePattern::Regex(regex) => regex
.is_match(path.as_os_str().as_encoded_bytes())
.unwrap_or(false),
Comment thread
j178 marked this conversation as resolved.
FilePattern::Glob(globs) => globs.is_match(path),
}
}
Expand All @@ -360,9 +362,9 @@ impl TryFrom<FilePatternWire> for FilePattern {

fn try_from(value: FilePatternWire) -> Result<Self, Self::Error> {
match value {
FilePatternWire::Glob { glob } => Ok(Self::Glob(GlobPatterns::new(vec![glob])?)),
FilePatternWire::GlobList { glob } => Ok(Self::Glob(GlobPatterns::new(glob)?)),
FilePatternWire::Regex(pattern) => Ok(Self::Regex(Regex::new(&pattern)?)),
FilePatternWire::Glob { glob } => Ok(Self::glob(vec![glob])?),
FilePatternWire::GlobList { glob } => Ok(Self::glob(glob)?),
FilePatternWire::Regex(pattern) => Ok(Self::regex(&pattern)?),
}
}
}
Expand Down
27 changes: 25 additions & 2 deletions crates/prek/src/hooks/pre_commit_hooks/no_commit_to_branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ impl Args {
}

for pattern in &self.patterns {
let pattern = Regex::new(pattern).context("Failed to compile regex patterns")?;
if pattern.is_match(branch).unwrap_or(false) {
let regex = Regex::new(pattern)
.with_context(|| format!("Failed to compile regex pattern `{pattern}`"))?;
if regex.is_match(branch).with_context(|| {
format!("Failed to match branch against regex pattern `{pattern}`")
})? {
return Ok(true);
}
Comment thread
j178 marked this conversation as resolved.
}
Expand Down Expand Up @@ -67,3 +70,23 @@ pub(crate) async fn run(hook: &Hook) -> Result<HookOutput> {
Ok(HookOutput::unchanged(0, Vec::new()))
}
}

#[cfg(test)]
mod tests {
use super::Args;

#[test]
fn pattern_runtime_errors_include_pattern() {
let args = Args {
branches: Vec::new(),
patterns: vec![r"^(a|aa)+\1$".to_string()],
};
let branch = format!("{}b", "a".repeat(40));

let err = args.check_protected(&branch).unwrap_err();
assert_eq!(
err.to_string(),
r"Failed to match branch against regex pattern `^(a|aa)+\1$`"
);
}
}
2 changes: 1 addition & 1 deletion crates/prek/tests/builtin_hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2928,7 +2928,7 @@ fn no_commit_to_branch_hook_with_patterns() -> Result<()> {

----- stderr -----
error: Failed to run hook `no-commit-to-branch`
caused by: Failed to compile regex patterns
caused by: Failed to compile regex pattern `*invalid-pattern*`
caused by: Parsing error at position 0: Target of repeat operator is invalid
");

Expand Down
Loading