Skip to main content

commit_wizard/engine/capabilities/commit/
push.rs

1use 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    // Branch protection
30    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    // Commit validation enforcement
39    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}