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 <noreply@anthropic.com>
This commit is contained in:
Red Bear OS
2026-07-31 06:06:45 +09:00
parent 700620569c
commit 57c80cbc33
+17 -8
View File
@@ -108,7 +108,8 @@ impl Config {
// detect cycles via a stack of currently-visiting names.
let mut resolved_here: BTreeSet<String> = BTreeSet::new();
let mut visiting: Vec<String> = Vec::new();
self.expand_group(name, &mut visiting, &mut resolved_here)?;
let mut expanded_groups: BTreeSet<String> = 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<String>,
// ACTUAL package names accumulated so far (this is what gets installed).
resolved: &mut BTreeSet<String>,
// 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<String>,
) -> 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(())
}