Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/anolisa/crates/anolisa-cli/src/commands/tier1/forget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ mod tests {
let layout = common::resolve_layout(ctx);
let repo_v1 = layout.prefix.join("repo").join("v1");
fs::create_dir_all(&repo_v1).expect("mkdir repo");
fs::write(repo_v1.join("components.toml"), index).expect("write components.toml");
fs::write(repo_v1.join("components-v2.toml"), index).expect("write components-v2.toml");
fs::create_dir_all(&layout.etc_dir).expect("mkdir etc");
fs::write(
layout.etc_dir.join("repo.toml"),
Expand Down Expand Up @@ -862,10 +862,11 @@ mod tests {
seed_component_index(
&c,
r#"
schema_version = 1
schema_version = 2

[[components]]
name = "cosh"
targets = [{ os = "linux", arch = "x86_64" }]

[[components.backends]]
kind = "rpm"
Expand Down Expand Up @@ -907,10 +908,11 @@ name = "copilot-shell"
seed_component_index(
&c,
r#"
schema_version = 1
schema_version = 2

[[components]]
name = "cosh"
targets = [{ os = "linux", arch = "x86_64" }]

[[components.aliases]]
kind = "rpm-package"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use crate::commands::tier1::recovery::LockedJournalGate;
use crate::commands::tier1::rpm_install;
use crate::context::CliContext;
use crate::progress::{self, Activity};
use crate::resolution::ComponentIndex;
use crate::response::{CliError, render_json, render_json_with_status};

use super::types::InstallOutcome;
Expand Down Expand Up @@ -832,9 +833,26 @@ pub(crate) fn batch_status(outcome: InstallOutcome, dry_run: bool) -> &'static s
}
}

/// Load the component index and return names of components that support
/// the given backend. When `backend` is `None`, the repo's default
/// backend is used.
fn component_names_for_target(
index: &ComponentIndex,
backend: &str,
os: &str,
arch: &str,
) -> Vec<String> {
index
.components
.iter()
.filter(|entry| {
entry.backends.iter().any(|item| item.kind == backend)
&& entry.supports_target(os, arch)
})
.map(|entry| entry.name.clone())
.collect()
}

/// Load the component index and return names of components that support the
/// current target and selected backend. When `backend` is `None`, the repo's
/// default backend is used.
pub(crate) fn resolve_all_components(
ctx: &CliContext,
backend: Option<&str>,
Expand All @@ -857,14 +875,12 @@ pub(crate) fn resolve_all_components(
command: "install --all".to_string(),
reason: format!("{err}"),
})?;
let selected_backend = selected_backend.to_string();
let names: Vec<String> = index
.components
.iter()
.filter(|entry| entry.backends.iter().any(|b| b.kind == selected_backend))
.map(|entry| entry.name.clone())
.collect();
Ok(names)
Ok(component_names_for_target(
&index,
selected_backend,
&env.os,
&env.arch,
))
}

#[cfg(test)]
Expand Down Expand Up @@ -899,6 +915,22 @@ mod tests {
);
}

#[test]
fn batch_component_selection_uses_host_target() {
let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let index_path = manifest_dir.join("../../manifests/components-v2.toml");
let index = ComponentIndex::load(index_path).expect("component index template must parse");

assert_eq!(
component_names_for_target(&index, "raw", "linux", "aarch64"),
["cosh-ng", "tokenless"]
);
assert_eq!(
component_names_for_target(&index, "raw", "macos", "aarch64"),
["cosh-ng", "agentsight", "tokenless"]
);
}

/// Multi-package transaction fake: records each backend call with its
/// full package set, so tests can pin that a batch shared one native
/// transaction. `fail_install` fails the install verb as a whole.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2382,12 +2382,13 @@ mod tests {
let repo_v1 = repo_root.join("v1");
std::fs::create_dir_all(&repo_v1).expect("repo dir");
std::fs::write(
repo_v1.join("components.toml"),
repo_v1.join("components-v2.toml"),
r#"
schema_version = 1
schema_version = 2

[[components]]
name = "cosh"
targets = [{ os = "linux", arch = "x86_64" }]

[[components.backends]]
kind = "raw"
Expand All @@ -2399,6 +2400,7 @@ name = "legacy-name"

[[components]]
name = "sec-core"
targets = [{ os = "linux", arch = "x86_64" }]

[[components.backends]]
kind = "raw"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1378,23 +1378,29 @@ manifest_digest = "sha256:{manifest_sha}"
}
std::fs::write(v1.join("index.toml"), index).expect("write distribution index");
std::fs::write(
v1.join("components.toml"),
r#"schema_version = 1
v1.join("components-v2.toml"),
format!(
r#"schema_version = 2

[[components]]
name = "cosh"
targets = [{{ os = "{os}", arch = "{arch}" }}]

[[components.backends]]
kind = "raw"
package = "cosh"

[[components]]
name = "cosh-ng"
targets = [{{ os = "{os}", arch = "{arch}" }}]

[[components.backends]]
kind = "raw"
package = "cosh-ng"
"#,
os = env.os,
arch = env.arch,
),
)
.expect("write component index");
format!("file://{}", v1.display())
Expand Down
46 changes: 27 additions & 19 deletions src/anolisa/crates/anolisa-cli/src/commands/tier1/list.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! `anolisa list` — list available components from the component index.
//!
//! Reads the repo-side `components.toml` (the component identity index),
//! Reads the repo-side `components-v2.toml` (the component identity index),
//! merges install status from `installed.toml`, and renders as a human
//! table or `--json` envelope.

Expand All @@ -21,7 +21,9 @@ use crate::commands::common;
use crate::commands::common::RepoPersistPolicy;
use crate::commands::state_view::{StateScope, StateView, StateVisibility};
use crate::context::{CliContext, InstallMode};
use crate::resolution::{ComponentIndex, ComponentIndexEntry, load_component_index};
use crate::resolution::{
ComponentIndex, ComponentIndexEntry, ComponentTarget, load_component_index,
};
use crate::response::{CliError, render_json};

use self::render::render_human;
Expand All @@ -44,10 +46,10 @@ pub struct Row {
pub display_name: String,
pub summary: String,
pub backends: Vec<String>,
/// Platforms declared by the component index.
pub platforms: Vec<String>,
/// Whether the component declares support for the current platform.
pub platform_available: bool,
/// OS/architecture targets declared by the component index.
pub targets: Vec<ComponentTarget>,
/// Whether the component declares support for the current host target.
pub target_available: bool,
pub status: String,
pub local_state: String,
pub ownership: String,
Expand Down Expand Up @@ -101,6 +103,7 @@ pub fn handle(args: ListArgs, ctx: &CliContext) -> Result<(), CliError> {
&view,
rpm_query.as_ref().map(|query| query as &dyn PackageQuery),
&env.os,
&env.arch,
);

if ctx.json {
Expand All @@ -115,7 +118,7 @@ pub fn handle(args: ListArgs, ctx: &CliContext) -> Result<(), CliError> {

if !ctx.quiet {
render_warnings(&view.warnings);
render_human(&rows, ctx.no_color, &env.os);
render_human(&rows, ctx.no_color, &env.os, &env.arch);
}
Ok(())
}
Expand All @@ -127,16 +130,17 @@ fn build_rows(
state: &StateStore,
rpm_query: Option<&dyn PackageQuery>,
) -> Vec<Row> {
build_rows_for_platform(index, args, state, rpm_query, "linux")
build_rows_for_target(index, args, state, rpm_query, "linux", "x86_64")
}

#[cfg(test)]
fn build_rows_for_platform(
fn build_rows_for_target(
index: &ComponentIndex,
args: &ListArgs,
state: &StateStore,
rpm_query: Option<&dyn PackageQuery>,
platform: &str,
os: &str,
arch: &str,
) -> Vec<Row> {
index
.components
Expand All @@ -149,7 +153,8 @@ fn build_rows_for_platform(
Some(entry_to_row(
entry,
projection,
platform,
os,
arch,
RowScope {
scope: "none".to_string(),
active: false,
Expand All @@ -167,7 +172,8 @@ fn build_rows_from_view(
args: &ListArgs,
view: &StateView,
rpm_query: Option<&dyn PackageQuery>,
platform: &str,
os: &str,
arch: &str,
) -> Vec<Row> {
let visible_components = view.visible_components();
index
Expand Down Expand Up @@ -196,7 +202,7 @@ fn build_rows_from_view(
.map(str::to_string),
state_path: Some(record.root.state_path.display().to_string()),
};
Some(entry_to_row(entry, projection, platform, row_scope))
Some(entry_to_row(entry, projection, os, arch, row_scope))
})
.collect::<Vec<_>>();
}
Expand All @@ -219,7 +225,8 @@ fn build_rows_from_view(
vec![entry_to_row(
entry,
projection,
platform,
os,
arch,
RowScope {
scope: scope.to_string(),
active: false,
Expand All @@ -243,14 +250,15 @@ struct RowScope {
fn entry_to_row(
entry: &ComponentIndexEntry,
projection: LocalProjection,
platform: &str,
os: &str,
arch: &str,
row_scope: RowScope,
) -> Row {
let backends: Vec<String> = entry.backends.iter().map(|b| b.kind.clone()).collect();
let local_state = projection.local_state.label().to_string();
let ownership = projection.ownership_label().to_string();
Comment thread
ikunkun-sys marked this conversation as resolved.
let platform_available = entry.supports_platform(platform);
let install_available = platform_available && !backends.is_empty();
let target_available = entry.supports_target(os, arch);
let install_available = target_available && !backends.is_empty();
let action = if install_available || projection.action_label() != "install" {
projection.action_label().to_string()
} else {
Expand All @@ -264,8 +272,8 @@ fn entry_to_row(
.unwrap_or_else(|| entry.name.clone()),
summary: entry.summary.clone().unwrap_or_default(),
backends,
platforms: entry.platforms.clone(),
platform_available,
targets: entry.targets.clone(),
target_available,
status: projection.status,
local_state,
ownership,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ impl HumanWidths {
}
}

pub(super) fn render_human(rows: &[Row], no_color: bool, platform: &str) {
pub(super) fn render_human(rows: &[Row], no_color: bool, os: &str, arch: &str) {
let color = Palette::new(no_color);
println!(
"{}",
color.header(format!("Components for {}", display_platform(platform)))
color.header(format!("Components for {}/{arch}", display_os(os)))
);
println!();
if rows.is_empty() {
Expand All @@ -64,7 +64,7 @@ pub(super) fn render_human(rows: &[Row], no_color: bool, platform: &str) {
println!("{}", color.header(human_header(rows)));
for row in rows {
let availability = availability_label(row);
let availability = if row.platform_available {
let availability = if row.target_available {
color.ok(availability)
} else {
color.err(availability)
Expand All @@ -88,20 +88,20 @@ pub(super) fn render_human(rows: &[Row], no_color: bool, platform: &str) {
}

pub(super) fn availability_label(row: &Row) -> String {
if row.platform_available {
if row.target_available {
return "available".to_string();
}
let supported = row
.platforms
.targets
Comment thread
ikunkun-sys marked this conversation as resolved.
.iter()
.map(|platform| display_platform(platform))
.map(|target| format!("{}/{}", display_os(&target.os), target.arch))
.collect::<Vec<_>>()
.join("/");
.join(", ");
format!("{supported} only")
}

fn display_platform(platform: &str) -> String {
match platform {
fn display_os(os: &str) -> String {
match os {
"linux" => "Linux".to_string(),
"macos" => "macOS".to_string(),
other => other.to_string(),
Expand Down
Loading
Loading