Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
12 changes: 10 additions & 2 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_matches_non_utf8_paths_with_regex_include() {
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt as _;

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.
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.
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
18 changes: 17 additions & 1 deletion crates/prek/src/hooks/pre_commit_hooks/no_commit_to_branch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ 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) {
if pattern.is_match(branch)? {
return Ok(true);
}
Comment thread
j178 marked this conversation as resolved.
Resolved
}
Expand Down Expand Up @@ -67,3 +67,19 @@ 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_are_reported() {
let args = Args {
branches: Vec::new(),
patterns: vec![r"^(a|aa)+\1$".to_string()],
};
let branch = format!("{}b", "a".repeat(40));

assert!(args.check_protected(&branch).is_err());
}
}
Loading