git_checks/
reject_separate_root.rs

1// Copyright Kitware, Inc.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use derive_builder::Builder;
10use git_checks_core::impl_prelude::*;
11
12/// A check which denies root commits.
13#[derive(Builder, Debug, Default, Clone, Copy)]
14#[builder(field(private))]
15pub struct RejectSeparateRoot {}
16
17impl RejectSeparateRoot {
18    /// Create a new builder.
19    pub fn builder() -> RejectSeparateRootBuilder {
20        Default::default()
21    }
22}
23
24impl Check for RejectSeparateRoot {
25    fn name(&self) -> &str {
26        "reject-separate-root"
27    }
28
29    fn check(&self, _: &CheckGitContext, commit: &Commit) -> Result<CheckResult, Box<dyn Error>> {
30        let mut result = CheckResult::new();
31
32        if commit.parents.is_empty() {
33            result.add_error(format!(
34                "commit {} not allowed; it is a root commit.",
35                commit.sha1,
36            ));
37        }
38
39        Ok(result)
40    }
41}
42
43#[cfg(feature = "config")]
44pub(crate) mod config {
45    use git_checks_config::{register_checks, CommitCheckConfig, IntoCheck};
46    use serde::Deserialize;
47    #[cfg(test)]
48    use serde_json::json;
49
50    use crate::RejectSeparateRoot;
51
52    /// Configuration for the `RejectSeparateRoot` check.
53    ///
54    /// No configuration available.
55    ///
56    /// This check is registered as a commit check with the name `"reject_separate_root"`.
57    #[derive(Deserialize, Debug)]
58    pub struct RejectSeparateRootConfig {}
59
60    impl IntoCheck for RejectSeparateRootConfig {
61        type Check = RejectSeparateRoot;
62
63        fn into_check(self) -> Self::Check {
64            Default::default()
65        }
66    }
67
68    register_checks! {
69        RejectSeparateRootConfig {
70            "reject_separate_root" => CommitCheckConfig,
71        },
72    }
73
74    #[test]
75    fn test_reject_separate_root_config_empty() {
76        let json = json!({});
77        let check: RejectSeparateRootConfig = serde_json::from_value(json).unwrap();
78
79        let _ = check.into_check();
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use git_checks_core::Check;
86
87    use crate::test::*;
88    use crate::RejectSeparateRoot;
89
90    const NO_ROOT_TOPIC: &str = "ba3dc3cb09a558c88282742413a2dccb17d444fc";
91    const WITH_ROOT_TOPIC: &str = "ff560e8798ef7a9d10bf43660695f7155b49b398";
92
93    #[test]
94    fn test_reject_separate_root_builder_default() {
95        assert!(RejectSeparateRoot::builder().build().is_ok());
96    }
97
98    #[test]
99    fn test_reject_separate_root_name_commit() {
100        let check = RejectSeparateRoot::default();
101        assert_eq!(Check::name(&check), "reject-separate-root");
102    }
103
104    #[test]
105    fn test_reject_separate_root_no_root() {
106        let check = RejectSeparateRoot::default();
107        run_check_ok("test_reject_separate_root_no_root", NO_ROOT_TOPIC, check);
108    }
109
110    #[test]
111    fn test_reject_separate_root_with_root() {
112        let check = RejectSeparateRoot::default();
113        let result = run_check(
114            "test_reject_separate_root_with_root",
115            WITH_ROOT_TOPIC,
116            check,
117        );
118        test_result_errors(
119            result,
120            &["commit ff560e8798ef7a9d10bf43660695f7155b49b398 not allowed; it is a root commit."],
121        );
122    }
123}