mirror of
https://codeberg.org/Mo8it/git-webhook-client
synced 2024-11-21 11:06:32 +00:00
Run the command in an attached thread
This commit is contained in:
parent
5b1b2108bf
commit
aa6ee45c2f
6 changed files with 88 additions and 36 deletions
|
@ -4,7 +4,7 @@ CREATE TABLE hooklog (
|
|||
repo_url TEXT NOT NULL,
|
||||
command_with_args TEXT NOT NULL,
|
||||
current_dir TEXT NOT NULL,
|
||||
stdout TEXT NOT NULL,
|
||||
stderr TEXT NOT NULL,
|
||||
stdout TEXT,
|
||||
stderr TEXT,
|
||||
status_code INTEGER CHECK (status_code >= 0)
|
||||
);
|
||||
|
|
46
src/db.rs
46
src/db.rs
|
@ -2,7 +2,6 @@ use chrono::Local;
|
|||
use diesel::prelude::*;
|
||||
use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
|
||||
use std::env;
|
||||
use std::process::Output;
|
||||
|
||||
use crate::config::Hook;
|
||||
use crate::models::{HookLog, NewHookLog};
|
||||
|
@ -27,7 +26,7 @@ fn get_conn(
|
|||
.or(Err("Could not get database pool!".to_string()))
|
||||
}
|
||||
|
||||
pub fn add_hook_log(pool: &DBPool, hook: &Hook, output: &Output) -> Result<i32, String> {
|
||||
pub fn add_hook_log(pool: &DBPool, hook: &Hook) -> Result<HookLog, String> {
|
||||
let conn = &mut get_conn(pool)?;
|
||||
|
||||
let command_with_args = hook.command.to_owned() + " " + &hook.args.join(" ");
|
||||
|
@ -37,18 +36,47 @@ pub fn add_hook_log(pool: &DBPool, hook: &Hook, output: &Output) -> Result<i32,
|
|||
repo_url: &hook.repo_url,
|
||||
command_with_args: &command_with_args,
|
||||
current_dir: &hook.current_dir,
|
||||
stdout: std::str::from_utf8(&output.stdout).unwrap_or("Could not convert stdout to str!"),
|
||||
stderr: std::str::from_utf8(&output.stderr).unwrap_or("Could not convert stderr to str!"),
|
||||
status_code: output.status.code(),
|
||||
};
|
||||
|
||||
match diesel::insert_into(hooklog::table)
|
||||
diesel::insert_into(hooklog::table)
|
||||
.values(&new_hook_log)
|
||||
.get_result::<HookLog>(conn)
|
||||
{
|
||||
Ok(hook_log) => Ok(hook_log.id),
|
||||
Err(e) => Err(e.to_string()),
|
||||
.or_else(|e| Err(e.to_string()))
|
||||
}
|
||||
|
||||
pub fn fill_hook_log(
|
||||
pool: &DBPool,
|
||||
hook_log_id: i32,
|
||||
stdout: &[u8],
|
||||
stderr: &[u8],
|
||||
status_code: Option<i32>,
|
||||
) {
|
||||
let conn = &mut match get_conn(pool) {
|
||||
Ok(pool) => pool,
|
||||
Err(e) => {
|
||||
println!("Error while trying to fill hook log: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match diesel::update(hooklog::dsl::hooklog.find(hook_log_id))
|
||||
.set((
|
||||
hooklog::dsl::stdout.eq(Some(
|
||||
std::str::from_utf8(&stdout).unwrap_or("Could not convert stdout to str!"),
|
||||
)),
|
||||
hooklog::dsl::stderr.eq(Some(
|
||||
std::str::from_utf8(stderr).unwrap_or("Could not convert stderr to str!"),
|
||||
)),
|
||||
hooklog::dsl::status_code.eq(status_code),
|
||||
))
|
||||
.execute(conn)
|
||||
{
|
||||
Ok(_) => return,
|
||||
Err(e) => {
|
||||
println!("Could not update hook log: {}", e.to_string());
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_hook_log(pool: &DBPool, id: i32) -> Result<HookLog, String> {
|
||||
|
|
|
@ -106,13 +106,8 @@ impl<'r> FromData<'r> for Repo<'r> {
|
|||
}
|
||||
|
||||
fn is_valid_signature(secret: &[u8], received_signature: &[u8], payload: &[u8]) -> bool {
|
||||
let mut mac = match Hmac::<Sha256>::new_from_slice(secret) {
|
||||
Ok(mac) => mac,
|
||||
Err(_) => {
|
||||
println!("Can not generate a mac from the secret!");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let mut mac =
|
||||
Hmac::<Sha256>::new_from_slice(secret).expect("Can not generate a mac from the secret!");
|
||||
mac.update(payload);
|
||||
let expected_signature = mac.finalize().into_bytes();
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ pub struct HookLog {
|
|||
pub repo_url: String,
|
||||
pub command_with_args: String,
|
||||
pub current_dir: String,
|
||||
pub stdout: String,
|
||||
pub stderr: String,
|
||||
pub stdout: Option<String>,
|
||||
pub stderr: Option<String>,
|
||||
pub status_code: Option<i32>,
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,4 @@ pub struct NewHookLog<'a> {
|
|||
pub repo_url: &'a str,
|
||||
pub command_with_args: &'a str,
|
||||
pub current_dir: &'a str,
|
||||
pub stdout: &'a str,
|
||||
pub stderr: &'a str,
|
||||
pub status_code: Option<i32>,
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use rocket::response::status::BadRequest;
|
|||
use rocket::{get, post, State};
|
||||
use rocket_dyn_templates::Template;
|
||||
use std::process::Command;
|
||||
use std::thread;
|
||||
|
||||
use crate::db;
|
||||
use crate::guards;
|
||||
|
@ -35,9 +36,9 @@ pub fn index(
|
|||
|
||||
#[post("/trigger", format = "json", data = "<repo>")]
|
||||
pub fn trigger(
|
||||
repo: guards::Repo,
|
||||
db_state: &State<states::DB>,
|
||||
config_state: &State<states::Config>,
|
||||
repo: guards::Repo,
|
||||
) -> Result<String, BadRequest<String>> {
|
||||
let hook = match config_state.get_hook(repo.clone_url) {
|
||||
Some(hook) => hook,
|
||||
|
@ -49,17 +50,48 @@ pub fn trigger(
|
|||
}
|
||||
};
|
||||
|
||||
let output = match Command::new(&hook.command)
|
||||
.args(&hook.args)
|
||||
.current_dir(&hook.current_dir)
|
||||
.output()
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(_) => return Err(bad_req("Can not run the hook command!")),
|
||||
let hook_log_id = match db::add_hook_log(&db_state.pool, hook) {
|
||||
Ok(hook_log) => hook_log.id,
|
||||
Err(e) => return Err(bad_req(e)),
|
||||
};
|
||||
|
||||
match db::add_hook_log(&db_state.pool, hook, &output) {
|
||||
Ok(new_hook_log_id) => Ok(format!("{}/?id={}", config_state.base_url, new_hook_log_id)),
|
||||
Err(e) => Err(bad_req(e)),
|
||||
{
|
||||
// Spawn and detach a thread that runs the command and fills the output in the log.
|
||||
// This is useful to give a quick response to the git server in case that the command has a long
|
||||
// execution time. It prevents reaching the webhook timeout of the git server.
|
||||
|
||||
let command = hook.command.clone();
|
||||
let args = hook.args.clone();
|
||||
let current_dir = hook.current_dir.clone();
|
||||
let db_pool = db_state.pool.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
let stdout: Vec<u8>;
|
||||
let stderr: Vec<u8>;
|
||||
let status_code: Option<i32>;
|
||||
|
||||
match Command::new(command)
|
||||
.args(args)
|
||||
.current_dir(current_dir)
|
||||
.output()
|
||||
{
|
||||
Ok(output) => {
|
||||
stdout = output.stdout;
|
||||
stderr = output.stderr;
|
||||
status_code = output.status.code();
|
||||
}
|
||||
Err(e) => {
|
||||
stdout = Vec::new();
|
||||
stderr = format!("Error while running the hook command: {}", e.to_string())
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
status_code = Some(1);
|
||||
}
|
||||
};
|
||||
|
||||
db::fill_hook_log(&db_pool, hook_log_id, &stdout, &stderr, status_code);
|
||||
});
|
||||
}
|
||||
|
||||
Ok(format!("{}/?id={}", config_state.base_url, hook_log_id))
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ diesel::table! {
|
|||
repo_url -> Text,
|
||||
command_with_args -> Text,
|
||||
current_dir -> Text,
|
||||
stdout -> Text,
|
||||
stderr -> Text,
|
||||
stdout -> Nullable<Text>,
|
||||
stderr -> Nullable<Text>,
|
||||
status_code -> Nullable<Integer>,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue