feat(pack): add list subcommand to show installed packages

list_installed_roots() searches WEFT_APP_STORE, then
~/.local/share/weft/apps, then /usr/share/weft/apps (same
priority order as weft-runtime and weft-appd). list_installed()
deduplicates by app_id (first root wins), sorts alphabetically within
each root, and prints id/name/version per line. Prints 'no packages
installed' when the store is empty or absent.
This commit is contained in:
Marco Allegretti 2026-03-11 11:57:08 +01:00
parent 826f144d9d
commit cab3a4a956

View file

@ -51,12 +51,16 @@ fn main() -> anyhow::Result<()> {
let app_id = args.get(2).context("usage: weft-pack uninstall <app_id>")?; let app_id = args.get(2).context("usage: weft-pack uninstall <app_id>")?;
uninstall_package(app_id)?; uninstall_package(app_id)?;
} }
Some("list") => {
list_installed();
}
_ => { _ => {
eprintln!("usage:"); eprintln!("usage:");
eprintln!(" weft-pack check <dir> validate a package directory"); eprintln!(" weft-pack check <dir> validate a package directory");
eprintln!(" weft-pack info <dir> print package metadata"); eprintln!(" weft-pack info <dir> print package metadata");
eprintln!(" weft-pack install <dir> install package to app store"); eprintln!(" weft-pack install <dir> install package to app store");
eprintln!(" weft-pack uninstall <app_id> remove installed package"); eprintln!(" weft-pack uninstall <app_id> remove installed package");
eprintln!(" weft-pack list list installed packages");
std::process::exit(1); std::process::exit(1);
} }
} }
@ -228,6 +232,55 @@ fn uninstall_package_from(app_id: &str, store_root: &Path) -> anyhow::Result<()>
Ok(()) Ok(())
} }
fn list_installed_roots() -> Vec<PathBuf> {
if let Ok(explicit) = std::env::var("WEFT_APP_STORE") {
return vec![PathBuf::from(explicit)];
}
let mut roots = Vec::new();
if let Ok(home) = std::env::var("HOME") {
roots.push(
PathBuf::from(home)
.join(".local")
.join("share")
.join("weft")
.join("apps"),
);
}
roots.push(PathBuf::from("/usr/share/weft/apps"));
roots
}
fn list_installed() {
let mut seen = std::collections::HashSet::new();
let mut count = 0usize;
for root in list_installed_roots() {
let Ok(entries) = std::fs::read_dir(&root) else {
continue;
};
let mut pkgs: Vec<(String, String, String)> = Vec::new();
for entry in entries.flatten() {
let manifest_path = entry.path().join("wapp.toml");
let Ok(contents) = std::fs::read_to_string(&manifest_path) else {
continue;
};
let Ok(m) = toml::from_str::<Manifest>(&contents) else {
continue;
};
if seen.insert(m.package.id.clone()) {
pkgs.push((m.package.id, m.package.name, m.package.version));
}
}
pkgs.sort_by(|a, b| a.0.cmp(&b.0));
for (id, name, version) in pkgs {
println!("{id} {name} {version}");
count += 1;
}
}
if count == 0 {
println!("no packages installed");
}
}
fn copy_dir(src: &Path, dst: &Path) -> anyhow::Result<()> { fn copy_dir(src: &Path, dst: &Path) -> anyhow::Result<()> {
std::fs::create_dir_all(dst)?; std::fs::create_dir_all(dst)?;
for entry in std::fs::read_dir(src)? { for entry in std::fs::read_dir(src)? {