use serde::{Deserialize, Serialize}; use crate::service::GitService; use crate::service::gitcmd::run_exec_cmd; use crate::share::errors::GitError; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)] #[serde(rename_all = "snake_case")] pub enum HookEventKind { PreReceive, PostReceive, Update, Push, Tag, MergeRequest, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HookRefDto { pub name: String, pub old_oid: Option, pub new_oid: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HookEventDto { pub kind: HookEventKind, pub repo_id: String, pub refs: Vec, pub actor: String, pub timestamp: i64, } #[derive(Debug, Clone, Serialize)] pub struct HookResultDto { pub accepted: bool, pub message: Option, } #[derive(Debug, Clone, Serialize, utoipa::ToSchema)] pub struct ScriptOutputDto { pub exit_code: i32, pub stdout: String, pub stderr: String, } impl GitService { pub async fn hook_pre_receive(&self, _event: HookEventDto) -> Result { Ok(HookResultDto { accepted: false, message: Some("no pre-receive policy configured".to_string()), }) } pub async fn hook_post_receive(&self, _event: HookEventDto) -> Result { Ok(HookResultDto { accepted: true, message: None, }) } pub fn hook_supported(&self, kind: HookEventKind) -> bool { matches!(kind, HookEventKind::PreReceive | HookEventKind::PostReceive) } pub async fn hook_execute_script( &self, space_name: String, repo_name: String, script: HookEventKind, args: Vec, ) -> Result { if !self.hook_supported(script) { return Err(GitError::InvalidArgument(format!( "unsupported hook script `{script:?}`" ))); } if args.iter().any(|a| a.len() > 1024) || args.len() > 16 { return Err(GitError::InvalidArgument( "invalid hook arguments".to_string(), )); } let filename = match script { HookEventKind::PreReceive => "pre-receive", HookEventKind::PostReceive => "post-receive", _ => unreachable!("rejected by hook_supported"), }; let ctx = self.context(space_name, repo_name).await?; let dir = ctx.repo_dir().join("hooks"); let prog = dir.join(filename); let timeout = self.state.config.git.git_command_timeout_secs; let out = run_exec_cmd( "hooks::execute_script", &prog.to_string_lossy(), &dir, &args, timeout, 64 * 1024 * 1024, ) .await?; Ok(ScriptOutputDto { exit_code: out.status.code().unwrap_or(-1), stdout: String::from_utf8_lossy(&out.stdout).into_owned(), stderr: String::from_utf8_lossy(&out.stderr).into_owned(), }) } }