From 4982dce71601d547c454bcc56f91f48152b9d451 Mon Sep 17 00:00:00 2001 From: KeisukeYamashita <19yamashita15@gmail.com> Date: Tue, 8 Oct 2024 15:58:04 +0200 Subject: [PATCH 1/2] feat(cli): add optional field for scope Signed-off-by: KeisukeYamashita <19yamashita15@gmail.com> --- src/rule/scope.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/rule/scope.rs b/src/rule/scope.rs index f469898..d3947bd 100644 --- a/src/rule/scope.rs +++ b/src/rule/scope.rs @@ -15,6 +15,10 @@ pub struct Scope { /// Options represents the options of the rule. /// If the option is empty, it means that no scope is allowed. options: Vec, + + /// Optional scope. + /// If true, even if the scope is not present, it is allowed. + optional: bool, } /// Scope represents the scope rule. @@ -37,7 +41,7 @@ impl Rule for Scope { fn validate(&self, message: &Message) -> Option { match &message.scope { None => { - if self.options.is_empty() { + if self.options.is_empty() || self.optional { return None; } } @@ -64,6 +68,7 @@ impl Default for Scope { fn default() -> Self { Self { level: Some(Self::LEVEL), + optional: false, options: vec![], } } @@ -235,5 +240,54 @@ mod tests { "scope invalid is not allowed. Only [\"api\", \"web\"] are allowed".to_string() ); } + + #[test] + fn test_optional_scope_with_non_empty_scope() { + let rule = Scope { + options: vec!["api".to_string(), "web".to_string()], + optional: true, + ..Default::default() + }; + + let message = Message { + body: None, + description: None, + footers: None, + r#type: Some("feat".to_string()), + raw: "feat(invalid): broadcast $destroy event on scope destruction".to_string(), + scope: Some("invalid".to_string()), + subject: None, + }; + + let violation = rule.validate(&message); + assert!(violation.is_some()); + assert_eq!(violation.clone().unwrap().level, Level::Error); + assert_eq!( + violation.unwrap().message, + "scope invalid is not allowed. Only [\"api\", \"web\"] are allowed".to_string() + ); + } + + #[test] + fn test_optional_scope_with_empty_scope() { + let rule = Scope { + options: vec!["api".to_string(), "web".to_string()], + optional: true, + ..Default::default() + }; + + let message = Message { + body: None, + description: None, + footers: None, + r#type: Some("feat".to_string()), + raw: "feat: broadcast $destroy event on scope destruction".to_string(), + scope: None, + subject: None, + }; + + let violation = rule.validate(&message); + assert!(violation.is_none()); + } } } From 45d7d06e6de04458af72f77afe8aaedcdf1deb60 Mon Sep 17 00:00:00 2001 From: KeisukeYamashita <19yamashita15@gmail.com> Date: Tue, 8 Oct 2024 16:00:38 +0200 Subject: [PATCH 2/2] feat(web): add optional scopes example Signed-off-by: KeisukeYamashita <19yamashita15@gmail.com> --- web/src/content/docs/rules/scope.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/web/src/content/docs/rules/scope.md b/web/src/content/docs/rules/scope.md index 0ff1301..d11af2a 100644 --- a/web/src/content/docs/rules/scope.md +++ b/web/src/content/docs/rules/scope.md @@ -3,7 +3,9 @@ title: Scope description: Allowlist for scopes --- -* Default: `ignore` +* Default: + * Level: `ignore` + * Optional: `false` In this example, we assumed that you have a project with the following scopes: @@ -42,6 +44,19 @@ rules: - web ``` +### Optional scopes `api` and `web` + +```yaml +rules: + scope: + level: error + optional: true + options: + - api +``` + +With this configuration, `feat(api): xxx` and `feat: xxx` are valid commits. + ### Disallow all scopes ```yaml