You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
malachite/src/main.rs

237 lines
5.4 KiB
Rust

use clap::Parser;
use futures_util::future;
use lazy_static::lazy_static;
use std::sync::Arc;
use tokio::sync::Semaphore;
use args::Args;
use args::GlobalArgs;
use args::Operation;
use errors::AppError;
use errors::AppResult;
use git::GitRepository;
use utils::uid;
use crate::build::{Build, BuildKind};
use crate::config::Config;
use crate::generate::GenerateKind;
use crate::info::GitInfo;
use crate::info::{BOLD, CLEAN, DIRTY, RESET, UNKNOWN};
use crate::lock::Lockfile;
use crate::prune::PackageFiles;
mod args;
mod build;
mod builders;
mod config;
mod errors;
mod generate;
mod git;
mod i18n;
mod info;
mod lock;
mod prune;
mod utils;
lazy_static!(
#[derive(Clone, Debug)]
pub static ref GLOBAL_ARGS: GlobalArgs = GlobalArgs::new();
);
#[tokio::main]
pub async fn main() -> AppResult<()> {
color_eyre::install().unwrap();
if uid() == 0 {
return Err(AppError::RunAsRoot);
}
let args: Args = Args::parse();
match args.subcommand {
Operation::Build {
packages,
concurrent,
} => cmd_build(packages, concurrent).await?,
Operation::Generate => cmd_generate().await?,
Operation::Init => cmd_init()?,
Operation::Pull {
rebuild,
concurrent,
} => cmd_pull(rebuild, concurrent).await?,
Operation::Clean { prune } => cmd_clean(prune).await?,
Operation::Info => cmd_info()?,
}
Ok(())
}
fn cmd_init() -> AppResult<()> {
Config::init()?;
Ok(())
}
async fn cmd_pull(rebuild: bool, concurrent: Option<u8>) -> AppResult<()> {
let config = Config::load()?;
for repository in &config.repositories.names {
let expanded = repository.expand(&config)?;
let path = config.base.src.join(&repository.name);
if path.exists() {
GitRepository::new(path).pull()?;
} else {
GitRepository::clone_repo(path, &expanded, repository.rev.clone())?;
}
}
let mut lock = Lockfile::new(&config)?;
let old_lock = lock.clone();
lock.update(&config)?;
if rebuild {
let mut packages_to_rebuild = vec![];
for (name, remote) in &old_lock.remote {
if let Some(new_remote) = lock.remote.get(name) {
if remote.commit != new_remote.commit {
packages_to_rebuild.push(name.clone());
}
}
}
if !packages_to_rebuild.is_empty() {
cmd_build(packages_to_rebuild, concurrent).await?;
}
}
lock.save()?;
Ok(())
}
async fn cmd_clean(prune: Option<u8>) -> AppResult<()> {
let config = Config::load()?;
let excluded = [
&config.repo.repo,
&config.repo.out,
&config.base.src.join(".mlc"),
&config.base.src.join(".git"),
];
let mut to_delete = Vec::new();
for entry in std::fs::read_dir(&config.base.src)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
let name = path.file_name().map(|s| s.to_string_lossy()).unwrap();
if !config.repositories.names.iter().any(|x| x.name == name)
&& !excluded.contains(&&path)
{
to_delete.push(path);
}
}
}
for path in to_delete {
std::fs::remove_dir_all(path)?;
}
if let Some(prune) = prune {
let mut packages = PackageFiles::new();
packages.scan(&config.repo.out)?;
let pruned = packages.prune(prune)?;
if pruned > 0 {
cmd_generate().await?;
}
}
Ok(())
}
async fn cmd_build(packages: Vec<String>, concurrent: Option<u8>) -> AppResult<()> {
let config = Config::load()?;
let kind = match config.base.podman {
true => BuildKind::Podman {
image: config.base.image,
},
false => BuildKind::Host,
};
let concurrent = if let Some(concurrent) = concurrent {
concurrent
} else {
1
};
let out = config.repo.out;
let sem = Arc::new(Semaphore::new(concurrent as usize));
let results = future::join_all(packages.into_iter().map(|name| async {
let sem = sem.clone();
let out = out.clone();
let kind = kind.clone();
let perm = sem.acquire().await.unwrap();
let build = Build::new(kind, name, out, vec![])?;
build.build().await?;
drop(perm);
Ok::<_, AppError>(())
}))
.await;
for result in results {
result?;
}
Ok(())
}
async fn cmd_generate() -> AppResult<()> {
let config = Config::load()?;
let kind = match config.base.podman {
true => GenerateKind::Podman {
image: config.base.image,
},
false => GenerateKind::Host,
};
generate::PacmanRepository::new(
kind,
config.repo.name,
config.repo.out,
config.repo.repo,
config.repo.security,
)
.generate()
.await?;
Ok(())
}
fn cmd_info() -> AppResult<()> {
let config = Config::load()?;
let repositories = config.repositories.names;
let mut overview = Vec::new();
for repo in repositories {
let path = config.base.src.join(&repo.name);
overview.push(GitInfo::new(path)?);
}
let mut table = tabled::Table::new(overview);
table.with(tabled::Style::rounded());
println!("{table}");
println!(
" {BOLD}Key:{RESET} {CLEAN}Clean {UNKNOWN}Unknown {DIRTY}Dirty{RESET}"
);
Ok(())
}