563bb99
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<String>,
pub new_oid: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HookEventDto {
pub kind: HookEventKind,
pub repo_id: String,
pub refs: Vec<HookRefDto>,
pub actor: String,
pub timestamp: i64,
}
#[derive(Debug, Clone, Serialize)]
pub struct HookResultDto {
pub accepted: bool,
pub message: Option<String>,
}
#[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<HookResultDto, GitError> {
Ok(HookResultDto {
accepted: false,
message: Some("no pre-receive policy configured".to_string()),
})
}
pub async fn hook_post_receive(&self, _event: HookEventDto) -> Result<HookResultDto, GitError> {
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<String>,
) -> Result<ScriptOutputDto, GitError> {
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(),
})
}
}