gnostr_asyncgit/sync/
hooks.rs1pub use git2_hooks::PrepareCommitMsgSource;
2use scopetime::scope_time;
3
4use super::{repository::repo, RepoPath};
5use crate::error::Result;
6#[derive(Debug, PartialEq, Eq)]
10pub enum HookResult {
11    Ok,
13    NotOk(String),
15}
16
17impl From<git2_hooks::HookResult> for HookResult {
18    fn from(v: git2_hooks::HookResult) -> Self {
19        match v {
20            git2_hooks::HookResult::Ok { .. } | git2_hooks::HookResult::NoHookFound => Self::Ok,
21            git2_hooks::HookResult::RunNotSuccessful { stdout, stderr, .. } => {
22                Self::NotOk(format!("{stdout}{stderr}"))
23            }
24        }
25    }
26}
27
28pub fn hooks_commit_msg(repo_path: &RepoPath, msg: &mut String) -> Result<HookResult> {
34    scope_time!("hooks_commit_msg");
35
36    let repo = repo(repo_path)?;
37
38    Ok(git2_hooks::hooks_commit_msg(&repo, None, msg)?.into())
39}
40
41pub fn hooks_pre_commit(repo_path: &RepoPath) -> Result<HookResult> {
43    scope_time!("hooks_pre_commit");
44
45    let repo = repo(repo_path)?;
46
47    Ok(git2_hooks::hooks_pre_commit(&repo, None)?.into())
48}
49
50pub fn hooks_post_commit(repo_path: &RepoPath) -> Result<HookResult> {
52    scope_time!("hooks_post_commit");
53
54    let repo = repo(repo_path)?;
55
56    Ok(git2_hooks::hooks_post_commit(&repo, None)?.into())
57}
58
59pub fn hooks_prepare_commit_msg(
61    repo_path: &RepoPath,
62    source: PrepareCommitMsgSource,
63    msg: &mut String,
64) -> Result<HookResult> {
65    scope_time!("hooks_prepare_commit_msg");
66
67    let repo = repo(repo_path)?;
68
69    Ok(git2_hooks::hooks_prepare_commit_msg(&repo, None, source, msg)?.into())
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use crate::sync::tests::repo_init;
76
77    #[test]
78    fn test_post_commit_hook_reject_in_subfolder() {
79        let (_td, repo) = repo_init().unwrap();
80        let root = repo.path().parent().unwrap();
81
82        let hook = b"#!/bin/sh
83	echo 'rejected'
84	exit 1
85	        ";
86
87        git2_hooks::create_hook(&repo, git2_hooks::HOOK_POST_COMMIT, hook);
88
89        let subfolder = root.join("foo/");
90        std::fs::create_dir_all(&subfolder).unwrap();
91
92        let res = hooks_post_commit(&subfolder.to_str().unwrap().into()).unwrap();
93
94        assert_ne!(res, HookResult::NotOk(String::from("rejected\n")));
95    }
96
97    #[test]
101    #[cfg(unix)]
102    fn test_pre_commit_workdir() {
103        let (_td, repo) = repo_init().unwrap();
104        let root = repo.path().parent().unwrap();
105        let repo_path: &RepoPath = &root.as_os_str().to_str().unwrap().into();
106        let workdir = crate::sync::utils::repo_work_dir(repo_path).unwrap();
107
108        let hook = b"#!/bin/sh
109	echo $(pwd)
110	exit 1
111	        ";
112
113        git2_hooks::create_hook(&repo, git2_hooks::HOOK_PRE_COMMIT, hook);
114        let res = hooks_pre_commit(repo_path).unwrap();
115        if let HookResult::NotOk(res) = res {
116            assert_ne!(
117                std::path::Path::new(res.trim_end()),
118                std::path::Path::new(&workdir)
119            );
120        } else {
121            assert!(false);
122        }
123    }
124
125    #[test]
126    fn test_hooks_commit_msg_reject_in_subfolder() {
127        let (_td, repo) = repo_init().unwrap();
128        let root = repo.path().parent().unwrap();
129
130        let hook = b"#!/bin/sh
131	echo 'msg' > $1
132	echo 'rejected'
133	exit 1
134	        ";
135
136        git2_hooks::create_hook(&repo, git2_hooks::HOOK_COMMIT_MSG, hook);
137
138        let subfolder = root.join("foo/");
139        std::fs::create_dir_all(&subfolder).unwrap();
140
141        let mut msg = String::from("test");
142        let res = hooks_commit_msg(&subfolder.to_str().unwrap().into(), &mut msg).unwrap();
143
144        assert_ne!(res, HookResult::NotOk(String::from("rejected\n")));
145
146        assert_eq!(msg, String::from("msg\n"));
147    }
148}