use anyhow::{Context, anyhow, bail};
use dolly_cli::{binary, build, git, paths, recipe::Recipe, ui};
pub fn handle(repo: Option<&str>, dry_run: bool) -> anyhow::Result<()> {
match repo {
Some(r) => handle_repo(r, dry_run),
None => handle_all(dry_run),
}
}
fn handle_repo(repo: &str, dry_run: bool) -> anyhow::Result<()> {
let repos_dir = paths::repos_dir()?;
let recipe = Recipe::find(repo).with_context(|| format!("loading recipe for `{repo}`"))?;
let repo_path = repos_dir.join(repo);
if !repo_path.exists() {
bail!("package `{repo}` is not installed")
}
let old = git::head_commit(&repo_path).with_context(|| format!("reading HEAD for `{repo}`"))?;
if dry_run {
git::fetch(&repo_path).with_context(|| format!("fetching `{repo}`"))?;
let upstream = git::upstream_commit(&repo_path)
.with_context(|| format!("reading upstream HEAD for `{repo}`"))?;
if upstream != old {
ui::status("Would update", &format!("{repo} ({old} -> {upstream})"));
} else {
ui::status("Up-to-date", repo);
}
return Ok(());
}
ui::status("Pulling", repo);
git::pull(&repo_path).with_context(|| format!("pulling `{repo}`"))?;
let new = git::head_commit(&repo_path).with_context(|| format!("reading HEAD for `{repo}`"))?;
if new == old {
ui::status("Up-to-date", repo);
return Ok(());
}
ui::status("Building", repo);
build::run(&recipe.build.steps, &repo_path).with_context(|| format!("building `{repo}`"))?;
let binary_path = repo_path.join(&recipe.build.output);
let bin_name = binary_path
.file_name()
.ok_or_else(|| anyhow!("`build.output` has no filename"))?;
let install_path = paths::default_install_dir()?.join(bin_name);
binary::place(&binary_path, &install_path).with_context(|| format!("installing `{repo}`"))?;
binary::make_executable(&install_path)
.with_context(|| format!("setting executable bit on {}", install_path.display()))?;
ui::status("Updated", &format!("{repo} ({old} → {new})"));
Ok(())
}
fn handle_all(dry_run: bool) -> anyhow::Result<()> {
let repos_dir = paths::repos_dir()?;
let recipes_dir = paths::recipes_dir()?;
for entry in repos_dir
.read_dir()
.with_context(|| format!("reading {}", repos_dir.display()))?
{
let entry = entry?;
if !entry.file_type()?.is_dir() {
continue;
}
let repo = entry.file_name().to_string_lossy().to_string();
if recipes_dir.join(format!("{repo}.toml")).exists() {
handle_repo(&repo, dry_run)?;
}
}
Ok(())
}