commit_wizard/engine/capabilities/commit/
push.rs1use crate::engine::{
2 capabilities::commit::check::ValidatedCommit,
3 models::policy::{Policy, check::CommitCheckEnforcement},
4};
5
6#[derive(Debug, Clone)]
7pub struct PushReport {
8 pub current_branch: String,
9 pub protected_branch: bool,
10 pub commits: Vec<ValidatedCommit>,
11 pub total_checked: usize,
12 pub invalid_count: usize,
13 pub blocked: bool,
14 pub block_reasons: Vec<String>,
15}
16
17pub fn evaluate_push(
18 policy: &Policy,
19 current_branch: &str,
20 commits: Vec<ValidatedCommit>,
21) -> PushReport {
22 let protected_branch = is_protected_branch(current_branch, &policy.branch.protected_patterns);
23 let total_checked = commits.len();
24 let invalid_count = commits.iter().filter(|c| !c.valid).count();
25
26 let mut blocked = false;
27 let mut block_reasons = Vec::<String>::new();
28
29 if policy.push.check_branch_policy && protected_branch && !policy.push.allow_protected {
31 blocked = true;
32 block_reasons.push(format!(
33 "push to protected branch '{}' is blocked by policy",
34 current_branch
35 ));
36 }
37
38 if policy.push.check_commits
40 && policy.check.commits_enabled
41 && policy.check.require_conventional
42 && should_enforce_commit_check(policy, protected_branch)
43 && invalid_count > 0
44 {
45 blocked = true;
46 block_reasons.push(format!(
47 "{} commit(s) do not satisfy conventional commit policy",
48 invalid_count
49 ));
50 }
51
52 PushReport {
53 current_branch: current_branch.to_string(),
54 protected_branch,
55 commits,
56 total_checked,
57 invalid_count,
58 blocked,
59 block_reasons,
60 }
61}
62
63fn should_enforce_commit_check(policy: &Policy, protected_branch: bool) -> bool {
64 match policy.check.enforcement {
65 CommitCheckEnforcement::AllBranches => true,
66 CommitCheckEnforcement::ProtectedBranches => protected_branch,
67 CommitCheckEnforcement::None => false,
68 }
69}
70
71fn is_protected_branch(branch: &str, patterns: &[String]) -> bool {
72 patterns
73 .iter()
74 .any(|pattern| branch_matches_pattern(branch, pattern))
75}
76
77fn branch_matches_pattern(branch: &str, pattern: &str) -> bool {
78 if let Some(prefix) = pattern.strip_suffix("/*") {
79 return branch == prefix || branch.starts_with(&format!("{prefix}/"));
80 }
81
82 branch == pattern
83}