feat: show what ahab makes of this project

This commit is contained in:
2026-09-08 14:17:08 +00:00
parent 79ef5cca2d
commit 1ecf7f1ee4
7 changed files with 123 additions and 3 deletions

View File

@@ -182,6 +182,19 @@ fn stored_paths(repo: &Repo, dir: &Path) -> Result<Vec<(PathBuf, Stored)>> {
Ok(found)
}
// what the store holds, without the git questions check asks
pub fn stored_summary(ctx: &Ctx, store: Option<&Path>) -> Result<(PathBuf, usize, usize)> {
let repo = Repo::discover(ctx, store)?;
let stored = stored_paths(&repo, &repo.store)?;
let linked = stored
.iter()
.filter(|(_, state)| *state == Stored::Linked)
.count();
Ok((repo.store, linked, stored.len() - linked))
}
// list what the store holds for this repository, the inverse of check
pub fn list(ctx: &Ctx, store: Option<&Path>) -> Result<()> {
let repo = Repo::discover(ctx, store)?;

View File

@@ -2,3 +2,4 @@ pub mod completions;
pub mod django;
pub mod link;
pub mod postgres;
pub mod status;

85
src/commands/status.rs Normal file
View File

@@ -0,0 +1,85 @@
use std::path::Path;
use anyhow::Result;
use crate::cmd::{Cmd, Ps};
use crate::commands::link;
use crate::ctx::Ctx;
use crate::project::Project;
// which services ahab picked, what it would talk to, what the store holds
pub fn status(ctx: &Ctx, store: Option<&Path>) -> Result<()> {
// a role it cannot pick out is worth reporting; a project it cannot read is not
let project = match Project::resolve(ctx) {
Ok(project) => project,
Err(e) => {
stored(ctx, store);
return Err(e);
}
};
println!("services: {}", project.names().join(", "));
role(ctx, "django", project.django(), &project);
role(ctx, "postgres", project.postgres(), &project);
stored(ctx, store);
Ok(())
}
// what the store holds, and where to look for what it does not
fn stored(ctx: &Ctx, store: Option<&Path>) {
match link::stored_summary(ctx, store) {
Err(e) => println!("store: {e:#}"),
Ok((store, linked, other)) => {
println!("store: {}", store.display());
println!("\tlinked: {linked}");
if other > 0 {
println!("\tnot linked: {other} (see `ahab link list`)");
}
println!("\t`ahab link check` lists what a sandbox can still read");
}
}
}
// one detected service: the name, the image behind it, and whether it is up
fn role(ctx: &Ctx, role: &str, detected: Result<String>, project: &Project) {
let service = match detected {
Ok(service) => service,
Err(e) => {
println!("{role}: {e:#}");
return;
}
};
let image = project
.image(&service)
.unwrap_or("built from the project")
.to_string();
println!("{role}: {service} ({image})");
match Ps::id_of(&service).capture(ctx) {
Ok(id) if id.trim().is_empty() => println!("\tcontainer: not running"),
Ok(id) => println!("\tcontainer: {}", short(id.trim())),
Err(e) => println!("\tcontainer: {e:#}"),
}
if role == "postgres" {
let (user, database) = project.postgres_credentials(&service);
let source = |key: &str| match project.env(&service, key) {
Some(_) => "",
None => " (default, nothing in the environment)",
};
println!("\tuser: {user}{}", source("POSTGRES_USER"));
println!("\tdatabase: {database}{}", source("POSTGRES_DB"));
}
}
// a container id is 64 characters and only the first few are ever typed
fn short(id: &str) -> String {
id.chars().take(12).collect()
}