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