1#![feature(try_blocks)]23pub mod cmds;4pub mod command;5pub mod host;6pub mod keys;78mod fleetdata;910use std::ffi::OsString;11use std::time::Duration;1213use anyhow::{bail, Result};14use clap::Parser;1516use cmds::{build_systems::BuildSystems, info::Info, secrets::Secrets};17use host::{Config, FleetOpts};18use indicatif::{ProgressState, ProgressStyle};19use tokio::process::Command;20use tracing::{info, metadata::LevelFilter};21use tracing_indicatif::IndicatifLayer;22use tracing_subscriber::{prelude::*, EnvFilter};2324#[derive(Parser)]25struct Prefetch {}26impl Prefetch {27 async fn run(&self, config: &Config) -> Result<()> {28 let mut prefetch_dir = config.directory.to_path_buf();29 prefetch_dir.push("prefetch");30 if !prefetch_dir.is_dir() {31 info!("nothing to prefetch: no prefetch directory");32 return Ok(());33 }34 for entry in std::fs::read_dir(&prefetch_dir)? {35 let entry = entry?;36 if !entry.metadata()?.is_file() {37 bail!("only files should exist in prefetch directory");38 }39 info!("prefetching {:?}", entry.file_name());40 let mut path = OsString::new();41 path.push("file://");42 path.push(entry.path());43 let status = Command::new("nix-prefetch-url").arg(path).status().await?;44 if !status.success() {45 bail!("failed with {status}");46 }47 }48 Ok(())49 }50}5152#[derive(Parser)]53enum Opts {54 55 BuildSystems(BuildSystems),56 57 #[clap(subcommand)]58 Secrets(Secrets),59 60 Prefetch(Prefetch),61 62 Info(Info),63}6465#[derive(Parser)]66#[clap(version = "1.0", author)]67struct RootOpts {68 #[clap(flatten)]69 fleet_opts: FleetOpts,70 #[clap(subcommand)]71 command: Opts,72}7374async fn run_command(config: &Config, command: Opts) -> Result<()> {75 match command {76 Opts::BuildSystems(c) => c.run(config).await?,77 Opts::Secrets(s) => s.run(config).await?,78 Opts::Info(i) => i.run(config).await?,79 Opts::Prefetch(p) => p.run(config).await?,80 };81 Ok(())82}8384#[tokio::main]85async fn main() -> Result<()> {86 let indicatif_layer = IndicatifLayer::new().with_progress_style(87 ProgressStyle::with_template(88 "{color_start}{span_child_prefix} {span_name}{{{span_fields}}}{color_end} {wide_msg} {color_start}{pos:>7}/{len:7}{elapsed}{color_end}",89 )90 .unwrap()91 .with_key(92 "color_start",93 |state: &ProgressState, writer: &mut dyn std::fmt::Write| {94 let elapsed = state.elapsed();9596 if elapsed > Duration::from_secs(60) {97 98 let _ = write!(writer, "\x1b[{}m", 1 + 30);99 } else if elapsed > Duration::from_secs(30) {100 101 let _ = write!(writer, "\x1b[{}m", 3 + 30);102 }103 },104 )105 .with_key(106 "color_end",107 |state: &ProgressState, writer: &mut dyn std::fmt::Write| {108 if state.elapsed() > Duration::from_secs(30) {109 let _ = write!(writer, "\x1b[0m");110 }111 },112 ),113 );114115 let filter = EnvFilter::from_default_env().add_directive(LevelFilter::INFO.into());116117 tracing_subscriber::registry()118 .with(119 tracing_subscriber::fmt::layer()120 .without_time()121 .with_target(false)122 .with_writer(indicatif_layer.get_stderr_writer())123 .with_filter(filter), 124 )125 .with(indicatif_layer)126 .init();127 info!("Starting");128 let mut os_args = std::env::args_os();129 let opts = RootOpts::parse_from((&mut os_args).take_while(|v| v != "--"));130 let config = opts.fleet_opts.build(os_args.collect()).await?;131132 match run_command(&config, opts.command).await {133 Ok(()) => {134 config.save()?;135 Ok(())136 }137 Err(e) => {138 let _ = config.save();139 Err(e)140 }141 }142}