staxman-old/src/main.rs

115 lines
2.9 KiB
Rust

use crate::git::{GitConfig, ThreadSafeRepository};
use crate::http::response::response_interceptor;
use anyhow::{anyhow, Result};
use axum::middleware::from_fn;
use bollard::Docker;
use clap::Parser;
use git2::Config;
use std::{net::SocketAddr, path::PathBuf};
use tokio::fs;
mod git;
mod http;
mod nix;
mod node;
mod route;
mod stack;
/// GitOps+WebUI for arion-based stacks
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Path to root of stacks
#[arg(short = 'd', long = "stack-dir", env = "STAX_DIR")]
stack_dir: PathBuf,
/// Address:port to bind
#[arg(short, long, default_value = "0.0.0.0:3000", env = "STAX_BIND")]
bind: SocketAddr,
/// Path to arion
#[arg(
long = "arion-bin",
env = "STAX_ARION_BIN",
default_value = "/run/current-system/sw/bin/arion"
)]
arion_binary: PathBuf,
#[arg(
long = "git-author",
default_value = "staxman <staxman@example.tld>",
env = "STAX_GIT_AUTHOR"
)]
git_author: String,
}
#[derive(Clone)]
pub struct AppState {
pub stack_dir: PathBuf,
pub arion_bin: PathBuf,
pub docker: Docker,
pub repository: ThreadSafeRepository,
}
#[tokio::main]
async fn main() -> Result<()> {
// Initialize logging and env
_ = dotenvy::dotenv();
tracing_subscriber::fmt::init();
// Parse args
let args = Args::parse();
tracing::info!("listening on {}", &args.bind);
// Try to connect to docker server
let docker = Docker::connect_with_local_defaults()?;
// Ping to make sure it works
let version = docker.version().await?;
tracing::info!("docker version: {}", version.version.unwrap());
let (author_name, author_email) = parse_author(&args.git_author)?;
let gitconfig = GitConfig {
author_name,
author_email,
};
// Fix up local git config for docker scenarios
let mut config = Config::open_default()?;
// Add "*" to safe.directory
config.set_str("safe.directory", "*")?;
// Make sure stack arg exists and is properly initialized
fs::create_dir_all(&args.stack_dir).await?;
let repository = ThreadSafeRepository::ensure_repository(gitconfig, &args.stack_dir)?;
let state = AppState {
stack_dir: args.stack_dir,
arion_bin: args.arion_binary,
docker,
repository,
};
let app = route::router()
.with_state(state)
.layer(from_fn(response_interceptor));
axum::Server::bind(&args.bind)
.serve(app.into_make_service())
.await?;
Ok(())
}
fn parse_author(git_author: &str) -> Result<(String, String)> {
match git_author.split_once('<') {
Some((name, email)) => Ok((
name.trim().to_string(),
email.trim_end_matches('>').trim().to_string(),
)),
None => Err(anyhow!(
"invalid git author format (email must be specified)"
)),
}
}