mirror of
https://codeberg.org/Mo8it/git-webhook-client
synced 2024-11-21 11:06:32 +00:00
Use task instead of thread
This commit is contained in:
parent
3b853060d0
commit
3ac410372f
13 changed files with 89 additions and 66 deletions
|
@ -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"]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -9,6 +9,7 @@ mod routes;
|
|||
mod schema;
|
||||
mod states;
|
||||
mod templates;
|
||||
mod webhook;
|
||||
|
||||
use anyhow::Result;
|
||||
use axum::{
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
// 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 webhook_task_context = webhook::TaskContext {
|
||||
hook: hook.clone(),
|
||||
hook_log_id,
|
||||
hook_log_link: hook_log_link.clone(),
|
||||
db: db.clone(),
|
||||
mailer: mailer.clone(),
|
||||
};
|
||||
|
||||
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:?}"),
|
||||
};
|
||||
});
|
||||
}
|
||||
// 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.
|
||||
task::spawn(async move { webhook::run(webhook_task_context).await });
|
||||
|
||||
Ok(hook_log_link.into_response())
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
58
src/webhook.rs
Normal 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:?}"),
|
||||
};
|
||||
}
|
|
@ -4,8 +4,8 @@ Hook log id:
|
|||
Datetime:
|
||||
{{ datetime }}
|
||||
|
||||
Repository url:
|
||||
{{ repo_url }}
|
||||
Clone url:
|
||||
{{ clone_url }}
|
||||
|
||||
Command with arguments:
|
||||
{{ command_with_args }}
|
||||
|
|
Loading…
Reference in a new issue