mirror of
https://codeberg.org/Mo8it/git-webhook-client
synced 2024-11-24 11:21:36 +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,
|
repo_url TEXT NOT NULL,
|
||||||
command_with_args TEXT NOT NULL,
|
command_with_args TEXT NOT NULL,
|
||||||
current_dir TEXT NOT NULL,
|
current_dir TEXT NOT NULL,
|
||||||
stdout TEXT NOT NULL,
|
stdout TEXT,
|
||||||
stderr TEXT NOT NULL,
|
stderr TEXT,
|
||||||
status_code INTEGER CHECK (status_code >= 0)
|
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::prelude::*;
|
||||||
use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
|
use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::process::Output;
|
|
||||||
|
|
||||||
use crate::config::Hook;
|
use crate::config::Hook;
|
||||||
use crate::models::{HookLog, NewHookLog};
|
use crate::models::{HookLog, NewHookLog};
|
||||||
|
@ -27,7 +26,7 @@ fn get_conn(
|
||||||
.or(Err("Could not get database pool!".to_string()))
|
.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 conn = &mut get_conn(pool)?;
|
||||||
|
|
||||||
let command_with_args = hook.command.to_owned() + " " + &hook.args.join(" ");
|
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,
|
repo_url: &hook.repo_url,
|
||||||
command_with_args: &command_with_args,
|
command_with_args: &command_with_args,
|
||||||
current_dir: &hook.current_dir,
|
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)
|
.values(&new_hook_log)
|
||||||
.get_result::<HookLog>(conn)
|
.get_result::<HookLog>(conn)
|
||||||
{
|
.or_else(|e| Err(e.to_string()))
|
||||||
Ok(hook_log) => Ok(hook_log.id),
|
|
||||||
Err(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> {
|
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 {
|
fn is_valid_signature(secret: &[u8], received_signature: &[u8], payload: &[u8]) -> bool {
|
||||||
let mut mac = match Hmac::<Sha256>::new_from_slice(secret) {
|
let mut mac =
|
||||||
Ok(mac) => mac,
|
Hmac::<Sha256>::new_from_slice(secret).expect("Can not generate a mac from the secret!");
|
||||||
Err(_) => {
|
|
||||||
println!("Can not generate a mac from the secret!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
mac.update(payload);
|
mac.update(payload);
|
||||||
let expected_signature = mac.finalize().into_bytes();
|
let expected_signature = mac.finalize().into_bytes();
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,8 @@ pub struct HookLog {
|
||||||
pub repo_url: String,
|
pub repo_url: String,
|
||||||
pub command_with_args: String,
|
pub command_with_args: String,
|
||||||
pub current_dir: String,
|
pub current_dir: String,
|
||||||
pub stdout: String,
|
pub stdout: Option<String>,
|
||||||
pub stderr: String,
|
pub stderr: Option<String>,
|
||||||
pub status_code: Option<i32>,
|
pub status_code: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,4 @@ pub struct NewHookLog<'a> {
|
||||||
pub repo_url: &'a str,
|
pub repo_url: &'a str,
|
||||||
pub command_with_args: &'a str,
|
pub command_with_args: &'a str,
|
||||||
pub current_dir: &'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::{get, post, State};
|
||||||
use rocket_dyn_templates::Template;
|
use rocket_dyn_templates::Template;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use crate::guards;
|
use crate::guards;
|
||||||
|
@ -35,9 +36,9 @@ pub fn index(
|
||||||
|
|
||||||
#[post("/trigger", format = "json", data = "<repo>")]
|
#[post("/trigger", format = "json", data = "<repo>")]
|
||||||
pub fn trigger(
|
pub fn trigger(
|
||||||
|
repo: guards::Repo,
|
||||||
db_state: &State<states::DB>,
|
db_state: &State<states::DB>,
|
||||||
config_state: &State<states::Config>,
|
config_state: &State<states::Config>,
|
||||||
repo: guards::Repo,
|
|
||||||
) -> Result<String, BadRequest<String>> {
|
) -> Result<String, BadRequest<String>> {
|
||||||
let hook = match config_state.get_hook(repo.clone_url) {
|
let hook = match config_state.get_hook(repo.clone_url) {
|
||||||
Some(hook) => hook,
|
Some(hook) => hook,
|
||||||
|
@ -49,17 +50,48 @@ pub fn trigger(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = match Command::new(&hook.command)
|
let hook_log_id = match db::add_hook_log(&db_state.pool, hook) {
|
||||||
.args(&hook.args)
|
Ok(hook_log) => hook_log.id,
|
||||||
.current_dir(&hook.current_dir)
|
Err(e) => return Err(bad_req(e)),
|
||||||
.output()
|
|
||||||
{
|
|
||||||
Ok(output) => output,
|
|
||||||
Err(_) => return Err(bad_req("Can not run the hook command!")),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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)),
|
// Spawn and detach a thread that runs the command and fills the output in the log.
|
||||||
Err(e) => Err(bad_req(e)),
|
// 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,
|
repo_url -> Text,
|
||||||
command_with_args -> Text,
|
command_with_args -> Text,
|
||||||
current_dir -> Text,
|
current_dir -> Text,
|
||||||
stdout -> Text,
|
stdout -> Nullable<Text>,
|
||||||
stderr -> Text,
|
stderr -> Nullable<Text>,
|
||||||
status_code -> Nullable<Integer>,
|
status_code -> Nullable<Integer>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue