diff --git a/yume-mods/misskey-auto-deploy/src/lib.rs b/yume-mods/misskey-auto-deploy/src/lib.rs index 1fb37670e3..d3faaf0a7a 100644 --- a/yume-mods/misskey-auto-deploy/src/lib.rs +++ b/yume-mods/misskey-auto-deploy/src/lib.rs @@ -1,6 +1,10 @@ use std::{collections::HashMap, fs::OpenOptions, path::PathBuf, process::Stdio, sync::Arc}; -use axum::{extract::{Query, State, Json}, http::{HeaderMap, StatusCode}, response::{IntoResponse, Response}}; +use axum::{ + extract::{Json, Query, State}, + http::{HeaderMap, StatusCode}, + response::{IntoResponse, Response}, +}; use chrono::NaiveDateTime; use libc::SIGTERM; use tokio::{process::Command, sync::RwLock}; @@ -80,10 +84,7 @@ pub struct GetStatusQuery { pub branch: Option, } - -fn require_bearer( - bcrypted: &str, - headers: &HeaderMap) -> Result<(), ApiError> { +fn require_bearer(bcrypted: &str, headers: &HeaderMap) -> Result<(), ApiError> { let bearer = headers .get("authorization") .ok_or_else(|| ApiError { @@ -95,11 +96,13 @@ fn require_bearer( code: 400, message: "Invalid Authorization header".to_string(), })?; - let bearer = bearer.trim_start_matches("Bearer ").trim_start_matches("bearer "); + let bearer = bearer + .trim_start_matches("Bearer ") + .trim_start_matches("bearer "); if !bcrypt::verify(bearer, bcrypted).map_err(|_| ApiError { - code: 401, - message: "Unauthorized".to_string(), + code: 401, + message: "Unauthorized".to_string(), })? { return Err(ApiError { code: 401, @@ -110,7 +113,6 @@ fn require_bearer( Ok(()) } - impl App { pub async fn post_deploy( State(state): State, @@ -119,10 +121,16 @@ impl App { ) -> Result { require_bearer(&state.bearer_secret, &headers)?; - let matched_ref = state.refs.get(&payload.ref_).ok_or_else(|| ApiError { - code: 404, - message: "Ref not found".to_string(), - })?.clone(); + let matched_ref = state + .refs + .into_iter() + .find(|(ref_, _)| ref_ == &payload.ref_) + .ok_or_else(|| ApiError { + code: 404, + message: "Ref not found".to_string(), + })? + .1 + .clone(); let mut status = matched_ref.status.write().await; @@ -142,7 +150,7 @@ impl App { }); } }; - + if !Command::new("git") .arg("fetch") .arg("origin") @@ -159,7 +167,9 @@ impl App { .map_err(|_| ApiError { code: 500, message: "Failed to wait for command".to_string(), - })?.success() { + })? + .success() + { status.status = Status::Error("Failed to fetch".to_string()); return Ok(StatusCode::OK); } @@ -181,38 +191,50 @@ impl App { .map_err(|_| ApiError { code: 500, message: "Failed to wait for command".to_string(), - })?.success() { + })? + .success() + { status.status = Status::Error("Failed to reset".to_string()); return Ok(StatusCode::OK); } - let mut command = Command::new("docker") .arg("compose") .arg("up") .arg("--detach") .arg("--build") - .args(matched_ref.config.compose_flags.as_ref().unwrap_or(&Vec::new())) + .args( + matched_ref + .config + .compose_flags + .as_ref() + .unwrap_or(&Vec::new()), + ) .current_dir(matched_ref.config.working_dir.clone()) .kill_on_drop(true) - .envs(matched_ref.config.env.as_ref().unwrap_or(&HashMap::new()).iter()) - .stdout( - match matched_ref.config.stdout.as_ref() { - Some(path) => { - let file = OpenOptions::new() - .create(true) - .append(true) - .open(path) - .map_err(|_| ApiError { - code: 500, - message: "Failed to open stdout file".to_string(), - })?; - - Stdio::from(file) - } - None => Stdio::null(), - } + .envs( + matched_ref + .config + .env + .as_ref() + .unwrap_or(&HashMap::new()) + .iter(), ) + .stdout(match matched_ref.config.stdout.as_ref() { + Some(path) => { + let file = OpenOptions::new() + .create(true) + .append(true) + .open(path) + .map_err(|_| ApiError { + code: 500, + message: "Failed to open stdout file".to_string(), + })?; + + Stdio::from(file) + } + None => Stdio::null(), + }) .spawn() .map_err(|_| ApiError { code: 500, @@ -222,12 +244,10 @@ impl App { status.status = Status::Deploying; status.triggered = Some(chrono::Utc::now().naive_utc()); - status.child_pid = command.id().ok_or_else( || - ApiError { - code: 500, - message: "Failed to get child PID".to_string(), - } - )?; + status.child_pid = command.id().ok_or_else(|| ApiError { + code: 500, + message: "Failed to get child PID".to_string(), + })?; let matched_ref = matched_ref.clone(); tokio::spawn(async move { @@ -250,7 +270,6 @@ impl App { } }); - Ok(StatusCode::OK) } @@ -273,19 +292,25 @@ impl App { let ref_ = match (&query.ref_, &query.branch) { (Some(ref_), None) => ref_.clone(), (None, Some(branch)) => format!("refs/heads/{}", branch), - _ => return Err(ApiError { - code: 400, - message: "Either ref or branch must be specified".to_string(), - }), + _ => { + return Err(ApiError { + code: 400, + message: "Either ref or branch must be specified".to_string(), + }) + } }; - let matched_ref = state.refs.get(&ref_).ok_or_else(|| ApiError { - code: 404, - message: "Ref not found".to_string(), - })?.clone(); + let matched_ref = state + .refs + .get(&ref_) + .ok_or_else(|| ApiError { + code: 404, + message: "Ref not found".to_string(), + })? + .clone(); let status = matched_ref.status.read().await; Ok(Json(status.clone())) } -} \ No newline at end of file +} diff --git a/yume-mods/misskey-auto-deploy/src/main.rs b/yume-mods/misskey-auto-deploy/src/main.rs index 2cc6e4de93..86a6ff1e33 100644 --- a/yume-mods/misskey-auto-deploy/src/main.rs +++ b/yume-mods/misskey-auto-deploy/src/main.rs @@ -1,7 +1,7 @@ -use std::{collections::HashMap, sync::Arc}; use axum::Router; -use tokio::{net::TcpListener, sync::RwLock}; use clap::Parser; +use std::{collections::HashMap, sync::Arc}; +use tokio::{net::TcpListener, sync::RwLock}; use misskey_auto_deploy::{App, AppState, RefConfig, RefState, RefStatus, Status}; @@ -13,7 +13,6 @@ struct Args { config: String, } - #[derive(Debug, serde::Deserialize)] pub struct Config { pub service: ServiceConfig, @@ -33,18 +32,22 @@ pub struct ServiceConfig { impl From for AppState { fn from(val: Config) -> Self { - let refs = val.refs.into_iter().map(|(ref_, config)| { - let status = RefStatus{ - status: Status::Never, - last_payload: None, - triggered: None, - succeeded: None, - child_pid: 0, - }; - let status = RwLock::new(status); - - (ref_, Arc::new(RefState { config, status })) - }).collect(); + let refs = val + .refs + .into_iter() + .map(|(ref_, config)| { + let status = RefStatus { + status: Status::Never, + last_payload: None, + triggered: None, + succeeded: None, + child_pid: 0, + }; + let status = RwLock::new(status); + + (ref_, Arc::new(RefState { config, status })) + }) + .collect(); AppState { bearer_secret: val.auth.secret, refs, @@ -52,12 +55,13 @@ impl From for AppState { } } - #[tokio::main] async fn main() { let args = Args::parse(); - let config:Config = toml::from_str(&std::fs::read_to_string(args.config).expect("Failed to read config file")).expect("Failed to parse config file"); + let config: Config = + toml::from_str(&std::fs::read_to_string(args.config).expect("Failed to read config file")) + .expect("Failed to parse config file"); let listen = match (&args.listen, &config.service.listen) { (Some(listen), _) => listen.clone(), @@ -67,15 +71,17 @@ async fn main() { let app_state: AppState = config.into(); - let app = Router::new() - .route("/deploy", - axum::routing::post(App::post_deploy) - .get(App::get_status) + let app = Router::new() + .route( + "/deploy", + axum::routing::post(App::post_deploy).get(App::get_status), ) .route("/summary", axum::routing::get(App::summary)) .with_state(app_state); - let listener = TcpListener::bind(listen).await.expect("Failed to bind listener"); + let listener = TcpListener::bind(listen) + .await + .expect("Failed to bind listener"); axum::serve(listener, app).await.unwrap(); -} \ No newline at end of file +}