diff --git a/crates/committed/src/checks.rs b/crates/committed/src/checks.rs index 46f964a..c176d1d 100644 --- a/crates/committed/src/checks.rs +++ b/crates/committed/src/checks.rs @@ -1,5 +1,6 @@ use crate::report; use committed::Style; +use regex::Regex; pub(crate) fn check_message( source: report::Source<'_>, @@ -371,3 +372,24 @@ pub(crate) fn check_merge_commit( Ok(false) } } + +pub(crate) fn check_allowed_author( + source: report::Source<'_>, + commit: &git2::Commit<'_>, + re: &Regex, + report: report::Report, +) -> Result { + let author = commit.author().to_string(); + if !re.is_match(&author) { + report(report::Message::error( + source, + report::DisallowedAuthor { + used: author, + allowed: re.as_str(), + }, + )); + Ok(true) + } else { + Ok(false) + } +} diff --git a/crates/committed/src/config.rs b/crates/committed/src/config.rs index c6bed46..b8fa68f 100644 --- a/crates/committed/src/config.rs +++ b/crates/committed/src/config.rs @@ -30,6 +30,7 @@ pub(crate) struct Config { pub(crate) allowed_types: Option>, pub(crate) allowed_scopes: Option>, pub(crate) merge_commit: Option, + pub(crate) allowed_author_re: Option, } impl Config { @@ -49,6 +50,7 @@ impl Config { allowed_types: Some(empty.allowed_types().map(|s| s.to_owned()).collect()), allowed_scopes: Some(empty.allowed_scopes().map(|s| s.to_owned()).collect()), merge_commit: Some(empty.merge_commit()), + allowed_author_re: empty.allowed_author_re().map(|s| s.to_owned()), } } @@ -92,6 +94,9 @@ impl Config { if let Some(source) = source.merge_commit { self.merge_commit = Some(source); } + if let Some(source) = source.allowed_author_re { + self.allowed_author_re = Some(source); + } } pub(crate) fn ignore_author_re(&self) -> Option<&str> { @@ -157,4 +162,8 @@ impl Config { pub(crate) fn merge_commit(&self) -> bool { self.merge_commit.unwrap_or(true) } + + pub(crate) fn allowed_author_re(&self) -> Option<&str> { + self.allowed_author_re.as_deref() + } } diff --git a/crates/committed/src/main.rs b/crates/committed/src/main.rs index 784e752..e3af2c2 100644 --- a/crates/committed/src/main.rs +++ b/crates/committed/src/main.rs @@ -200,6 +200,11 @@ fn run() -> proc_exit::ExitResult { } false }; + let allowed_author_re = config + .allowed_author_re() + .map(regex::Regex::new) + .transpose() + .with_code(proc_exit::sysexits::CONFIG_ERR)?; let mut failed = false; if let Some(output_path) = options.dump_config.as_ref() { @@ -240,6 +245,10 @@ fn run() -> proc_exit::ExitResult { log::trace!("Ignoring {}", source); } else { log::trace!("Processing {}", source); + if let Some(re) = allowed_author_re.as_ref() { + failed |= checks::check_allowed_author(source, &commit, re, report) + .with_code(UNKNOWN_ERR)?; + } let message = commit.message().unwrap(); failed |= checks::check_message(source, message, &config, report) .with_code(UNKNOWN_ERR)?; @@ -272,6 +281,10 @@ fn run() -> proc_exit::ExitResult { log::trace!("Ignoring {}", source); } else { log::trace!("Processing {}", source); + if let Some(re) = allowed_author_re.as_ref() { + failed |= checks::check_allowed_author(source, &commit, re, report) + .with_code(UNKNOWN_ERR)?; + } let message = commit.message().unwrap(); failed |= checks::check_message(source, message, &config, report).with_code(UNKNOWN_ERR)?; diff --git a/crates/committed/src/report.rs b/crates/committed/src/report.rs index 499ca12..4f58352 100644 --- a/crates/committed/src/report.rs +++ b/crates/committed/src/report.rs @@ -68,6 +68,7 @@ pub(crate) enum Content<'s> { DisallowedCommitType(DisallowedCommitType), DisallowedCommitScope(DisallowedCommitScope), MergeCommitDisallowed(MergeCommitDisallowed), + DisallowedAuthor(DisallowedAuthor<'s>), } #[derive(Clone, Debug, serde::Serialize)] @@ -180,6 +181,19 @@ pub(crate) struct MergeCommitDisallowed {} #[display("Empty commits are disallowed")] pub(crate) struct EmptyCommit {} +#[derive(Clone, Debug, serde::Serialize)] +#[serde(rename_all = "snake_case")] +#[derive(derive_more::Display)] +#[display( + "Disallowed author `{}` used, please use one matching `{}`", + used, + allowed +)] +pub(crate) struct DisallowedAuthor<'s> { + pub(crate) used: String, + pub(crate) allowed: &'s str, +} + pub(crate) type Report = fn(msg: Message<'_>); pub(crate) fn print_silent(_: Message<'_>) {} diff --git a/docs/reference.md b/docs/reference.md index ad9a27b..b195f83 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -50,6 +50,7 @@ Configuration is read from the following (in precedence order) | Field | Argument | Format | Default | Description | | ---------------------- | ----------------- | -------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | ignore_author_re | \- | regx | (none) | Authors to ignore the commits for. Generally used with bots out of your control. | +| allowed_author_re | \- | regx | (none) | Require commit author to match this regular expression | | subject_length | \- | number | 50 | Number of columns the subject can occupy | | line_length | \- | number | 72 | Number of columns any line with a break can occupy, including subject | | hard_line_length | \- | number | 0 (none) | Max number of columns any line can occupy. |