From 57c80cbc3339d984b5a4f9f9ee0ab88670b70eb3 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Fri, 31 Jul 2026 06:06:45 +0900 Subject: [PATCH] config: don't leak nested-group names into the flat package set expand_group inserted the group's own name into `resolved` (the package accumulator), so a nested-group reference (kde-desktop -> dbus-services) added a bogus "dbus-services" package. `repo cook --filesystem` then failed with 'Package PackageName("dbus-services") not found'. Track expanded GROUP names in a separate `expanded` set used only for dedup/cycle short-circuit; only real leaf package names go into `resolved`. Groups contribute their members, never themselves. Co-Authored-By: Claude Fable 5 --- src/config/mod.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index d26b17e4fd..5343d492ac 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -108,7 +108,8 @@ impl Config { // detect cycles via a stack of currently-visiting names. let mut resolved_here: BTreeSet = BTreeSet::new(); let mut visiting: Vec = Vec::new(); - self.expand_group(name, &mut visiting, &mut resolved_here)?; + let mut expanded_groups: BTreeSet = BTreeSet::new(); + self.expand_group(name, &mut visiting, &mut resolved_here, &mut expanded_groups)?; for pkg in &resolved_here { // Mark a group-expanded package for install using the SAME // representation an explicit `pkg = {}` entry deserializes to @@ -140,9 +141,16 @@ impl Config { &self, name: &str, visiting: &mut Vec, + // ACTUAL package names accumulated so far (this is what gets installed). resolved: &mut BTreeSet, + // Group names already fully expanded — tracked SEPARATELY from + // `resolved` so a group's own name never leaks into the package set + // (a nested-group reference like `kde-desktop` -> `dbus-services` must + // expand to expat/dbus, NOT add a bogus "dbus-services" package that + // `repo` then fails to cook with "Package not found"). + expanded: &mut BTreeSet, ) -> Result<()> { - if resolved.contains(name) { + if expanded.contains(name) { return Ok(()); } if visiting.iter().any(|n| n == name) { @@ -173,17 +181,18 @@ impl Config { visiting.push(name.to_string()); for member in &group.packages { if self.package_groups.contains_key(member) { - self.expand_group(member, visiting, resolved)?; + self.expand_group(member, visiting, resolved, expanded)?; } else { - // Treat as a package name. It's an error only if neither - // group nor package exists; we'll surface that at the end. - // For now, record it so the cycle detector sees it; the - // final install will surface the missing-package error. + // A leaf member: an actual package name. (If it names neither a + // group nor a real package, the missing-package error surfaces + // later at repo/install time.) resolved.insert(member.clone()); } } visiting.pop(); - resolved.insert(name.to_string()); + // Mark this GROUP as expanded (dedup / short-circuit only). Do NOT put + // it in `resolved` — it is a group, not a package. + expanded.insert(name.to_string()); Ok(()) }