1
0
Fork 0
mirror of https://codeberg.org/Mo8it/git-webhook-client synced 2024-11-22 11:08:04 +00:00

Use task instead of thread

This commit is contained in:
Mo 2022-12-26 23:43:44 +01:00
parent 3b853060d0
commit 3ac410372f
13 changed files with 89 additions and 66 deletions

View file

@ -29,7 +29,7 @@ The program looks for the configuration file configured with the environment var
1. `secret`: The secret of the webhook.
1. `base_url`: The base_url of the webhook client.
1. `hooks`: List of webhooks.
1. `repo_url`: Repository url.
1. `clone_url`: Repository url.
1. `current_dir`: The directory to run the command in.
1. `command`: The command without any arguments.
1. `args`: List of arguments separated by a comma.
@ -43,7 +43,7 @@ secret: CHANGE_ME!
base_url: https://webhook.mo8it.com
hooks:
repo_url: https://codeberg.org/Mo8it/git-webhook-client
clone_url: https://codeberg.org/Mo8it/git-webhook-client
current_dir: .
command: ls
args: ["-l", "-a", "test_directory"]

View file

@ -1,7 +1,7 @@
CREATE TABLE hooklog (
id INTEGER NOT NULL PRIMARY KEY,
datetime TEXT NOT NULL,
repo_url TEXT NOT NULL,
clone_url TEXT NOT NULL,
command_with_args TEXT NOT NULL,
current_dir TEXT NOT NULL,
stdout TEXT,

View file

@ -28,10 +28,10 @@ pub struct Logging {
pub filename: String,
}
#[derive(Deserialize)]
#[derive(Clone, Deserialize)]
pub struct Hook {
pub name: String,
pub repo_url: String,
pub clone_url: String,
pub current_dir: String,
pub command: String,
pub args: Vec<String>,

View file

@ -51,7 +51,7 @@ pub fn add_hook_log(pool: &DBPool, hook: &Hook) -> Result<HookLog> {
let new_hook_log = NewHookLog {
datetime: &Local::now().format("%d.%m.%Y %T").to_string(),
repo_url: &hook.repo_url,
clone_url: &hook.clone_url,
command_with_args: &command_with_args,
current_dir: &hook.current_dir,
};

View file

@ -44,7 +44,7 @@ impl Mailer {
})
}
pub fn send(&self, hook_name: &str, hook_log_link: &str, status: &str) -> Result<()> {
pub async fn send(&self, hook_name: &str, hook_log_link: &str, status: &str) -> Result<()> {
let email = self
.message_builder
.clone()

View file

@ -9,6 +9,7 @@ mod routes;
mod schema;
mod states;
mod templates;
mod webhook;
use anyhow::Result;
use axum::{

View file

@ -7,7 +7,7 @@ use crate::schema::hooklog;
pub struct HookLog {
pub id: i32,
pub datetime: String,
pub repo_url: String,
pub clone_url: String,
pub command_with_args: String,
pub current_dir: String,
pub stdout: Option<String>,
@ -19,7 +19,7 @@ pub struct HookLog {
#[diesel(table_name = hooklog)]
pub struct NewHookLog<'a> {
pub datetime: &'a str,
pub repo_url: &'a str,
pub clone_url: &'a str,
pub command_with_args: &'a str,
pub current_dir: &'a str,
}

View file

@ -6,10 +6,11 @@ use axum::{
};
use serde::Deserialize;
use serde_json::Value;
use std::{process::Command, sync::Arc, thread};
use tracing::{error, info};
use std::sync::Arc;
use tokio::task;
use tracing::info;
use crate::{db, errors, extractors, mailer, states, templates};
use crate::{db, errors, extractors, mailer, states, templates, webhook};
#[derive(Deserialize)]
pub struct IndexQuery {
@ -66,55 +67,18 @@ pub async fn trigger(
let hook_log_link = format!("{}/?id={}", state_config.base_url, hook_log_id);
{
// Spawn and detach a thread that runs the command and fills the output in the log.
let webhook_task_context = webhook::TaskContext {
hook: hook.clone(),
hook_log_id,
hook_log_link: hook_log_link.clone(),
db: db.clone(),
mailer: mailer.clone(),
};
// Spawn and detach a task 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.pool.clone();
let clone_url = clone_url.to_string();
let hook_name = hook.name.clone();
let hook_log_link = hook_log_link.clone();
thread::spawn(move || {
info!("Running webhook for Repo: {clone_url}");
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}")
.as_bytes()
.to_vec();
status_code = Some(1);
}
};
let status = if status_code == Some(0) { "Ok" } else { "Err" };
db::fill_hook_log(&db_pool, hook_log_id, &stdout, &stderr, status_code);
match mailer.send(&hook_name, &hook_log_link, status) {
Ok(_) => info!("Sent email with hook name {hook_name}"),
Err(e) => error!("{e:?}"),
};
});
}
task::spawn(async move { webhook::run(webhook_task_context).await });
Ok(hook_log_link.into_response())
}

View file

@ -4,7 +4,7 @@ diesel::table! {
hooklog (id) {
id -> Integer,
datetime -> Text,
repo_url -> Text,
clone_url -> Text,
command_with_args -> Text,
current_dir -> Text,
stdout -> Nullable<Text>,

View file

@ -34,7 +34,7 @@ impl From<config::Config> for StateConfig {
impl StateConfig {
pub fn get_hook(&self, clone_url: &str) -> Option<&config::Hook> {
self.hooks.iter().find(|&hook| hook.repo_url == clone_url)
self.hooks.iter().find(|&hook| hook.clone_url == clone_url)
}
}

View file

@ -7,7 +7,7 @@ use crate::models;
pub struct HookLog {
pub id: i32,
pub datetime: String,
pub repo_url: String,
pub clone_url: String,
pub command_with_args: String,
pub current_dir: String,
pub stdout: String,
@ -20,7 +20,7 @@ impl From<models::HookLog> for HookLog {
Self {
id: hook_log.id,
datetime: hook_log.datetime,
repo_url: hook_log.repo_url,
clone_url: hook_log.clone_url,
command_with_args: hook_log.command_with_args,
current_dir: hook_log.current_dir,
stdout: hook_log.stdout.unwrap_or_default(),

58
src/webhook.rs Normal file
View file

@ -0,0 +1,58 @@
use std::{process::Command, sync::Arc};
use tracing::{error, info};
use crate::{config, db, mailer, states};
pub struct TaskContext {
pub hook: config::Hook,
pub hook_log_id: i32,
pub hook_log_link: String,
pub db: Arc<states::DB>,
pub mailer: Arc<mailer::Mailer>,
}
pub async fn run(context: TaskContext) {
info!("Running webhook for repo: {}", context.hook.clone_url);
let stdout: Vec<u8>;
let stderr: Vec<u8>;
let status_code: Option<i32>;
match Command::new(&context.hook.command)
.args(&context.hook.args)
.current_dir(&context.hook.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}")
.as_bytes()
.to_vec();
status_code = Some(1);
}
};
let status = if status_code == Some(0) { "Ok" } else { "Err" };
db::fill_hook_log(
&context.db.pool,
context.hook_log_id,
&stdout,
&stderr,
status_code,
);
match context
.mailer
.send(&context.hook.name, &context.hook_log_link, status)
.await
{
Ok(_) => info!("Sent email with hook name {}", context.hook.name),
Err(e) => error!("{e:?}"),
};
}

View file

@ -4,8 +4,8 @@ Hook log id:
Datetime:
{{ datetime }}
Repository url:
{{ repo_url }}
Clone url:
{{ clone_url }}
Command with arguments:
{{ command_with_args }}