1pub mod cmds;2pub mod command;3pub mod host;4pub mod keys;56mod fleetdata;78use std::ffi::OsString;9use std::io;10use std::time::Duration;1112use anyhow::{anyhow, bail, Result};13use clap::Parser;1415use cmds::{build_systems::BuildSystems, info::Info, secrets::Secrets};16use host::{Config, FleetOpts};17use indicatif::{ProgressState, ProgressStyle};18use tokio::fs;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}83fn elapsed_subsec(state: &ProgressState, writer: &mut dyn std::fmt::Write) {84 let _ = writer.write_str(&format!("{:?}", state.elapsed()));85}8687#[tokio::main]88async fn main() -> Result<()> {89 let indicatif_layer = IndicatifLayer::new().with_progress_style(90 ProgressStyle::with_template(91 "{color_start}{span_child_prefix} {span_name}{{{span_fields}}}{color_end} {wide_msg} {color_start}{pos:>7}/{len:7}{elapsed}{color_end}",92 )93 .unwrap()94 .with_key(95 "color_start",96 |state: &ProgressState, writer: &mut dyn std::fmt::Write| {97 let elapsed = state.elapsed();9899 if elapsed > Duration::from_secs(60) {100 101 let _ = write!(writer, "\x1b[{}m", 1 + 30);102 } else if elapsed > Duration::from_secs(30) {103 104 let _ = write!(writer, "\x1b[{}m", 3 + 30);105 }106 },107 )108 .with_key(109 "color_end",110 |state: &ProgressState, writer: &mut dyn std::fmt::Write| {111 if state.elapsed() > Duration::from_secs(30) {112 let _ = write!(writer, "\x1b[0m");113 }114 },115 ),116 );117118 let filter = EnvFilter::from_default_env().add_directive(LevelFilter::INFO.into());119120 tracing_subscriber::registry()121 .with(122 tracing_subscriber::fmt::layer()123 .without_time()124 .with_target(false)125 .with_writer(indicatif_layer.get_stderr_writer())126 .with_filter(filter), 127 )128 .with(indicatif_layer)129 .init();130 info!("Starting");131 let mut os_args = std::env::args_os();132 let opts = RootOpts::parse_from((&mut os_args).take_while(|v| v != "--"));133 let config = opts.fleet_opts.build(os_args.collect()).await?;134135 match run_command(&config, opts.command).await {136 Ok(()) => {137 config.save()?;138 Ok(())139 }140 Err(e) => {141 let _ = config.save();142 Err(e)143 }144 }145}