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.
237 lines
5.4 KiB
Rust
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(())
|
|
}
|