diff --git a/internal/runbits/dependencies/changesummary.go b/internal/runbits/dependencies/changesummary.go index c1e95af3f5..32f50d862a 100644 --- a/internal/runbits/dependencies/changesummary.go +++ b/internal/runbits/dependencies/changesummary.go @@ -33,16 +33,18 @@ func OutputChangeSummary(out output.Outputer, newBuildPlan *buildplan.BuildPlan, v := fmt.Sprintf("%s@%s", a.Name(), a.Version()) addedString = append(addedLocale, v) addedLocale = append(addedLocale, fmt.Sprintf("[ACTIONABLE]%s[/RESET]", v)) - } - for _, i := range a.Ingredients { - dependencies = append(dependencies, i.RuntimeDependencies(true)...) - directDependencies = append(dependencies, i.RuntimeDependencies(false)...) + + for _, i := range a.Ingredients { + dependencies = append(dependencies, i.RuntimeDependencies(true)...) + directDependencies = append(directDependencies, i.RuntimeDependencies(false)...) + } } } dependencies = sliceutils.UniqueByProperty(dependencies, func(i *buildplan.Ingredient) any { return i.IngredientID }) directDependencies = sliceutils.UniqueByProperty(directDependencies, func(i *buildplan.Ingredient) any { return i.IngredientID }) - numIndirect := len(dependencies) - len(directDependencies) + commonDependencies := directDependencies.CommonRuntimeDependencies().ToIDMap() + numIndirect := len(dependencies) - len(directDependencies) - len(commonDependencies) sort.SliceStable(directDependencies, func(i, j int) bool { return directDependencies[i].Name < directDependencies[j].Name @@ -79,8 +81,12 @@ func OutputChangeSummary(out output.Outputer, newBuildPlan *buildplan.BuildPlan, prefix = " └─" } + // Retrieve runtime dependencies, and then filter out any dependencies that are common between all added ingredients. + runtimeDeps := ingredient.RuntimeDependencies(true) + runtimeDeps = runtimeDeps.Filter(func(i *buildplan.Ingredient) bool { _, ok := commonDependencies[i.IngredientID]; return !ok }) + subdependencies := "" - if numSubs := len(ingredient.RuntimeDependencies(true)); numSubs > 0 { + if numSubs := len(runtimeDeps); numSubs > 0 { subdependencies = fmt.Sprintf(" ([ACTIONABLE]%s[/RESET] dependencies)", // intentional leading space strconv.Itoa(numSubs)) } diff --git a/pkg/buildplan/ingredient.go b/pkg/buildplan/ingredient.go index 16672f0fc8..708820dff9 100644 --- a/pkg/buildplan/ingredient.go +++ b/pkg/buildplan/ingredient.go @@ -58,6 +58,31 @@ func (i Ingredients) ToNameMap() IngredientNameMap { return result } +// CommonRuntimeDependencies returns the set of runtime dependencies that are common between all ingredients. +// For example, given a set of python ingredients this will return at the very least the python language ingredient. +func (i Ingredients) CommonRuntimeDependencies() Ingredients { + counts := map[strfmt.UUID]int{} + + for _, ig := range i { + runtimeDeps := ig.RuntimeDependencies(true) + for _, rd := range runtimeDeps { + if _, ok := counts[rd.IngredientID]; !ok { + counts[rd.IngredientID] = 0 + } + counts[rd.IngredientID]++ + } + } + + common := Ingredients{} + for _, ig := range i { + if counts[ig.IngredientID] == len(i) { + common = append(common, ig) + } + } + + return common +} + func (i *Ingredient) RuntimeDependencies(recursive bool) Ingredients { dependencies := i.runtimeDependencies(recursive, make(map[strfmt.UUID]struct{})) return sliceutils.UniqueByProperty(dependencies, func(i *Ingredient) any { return i.IngredientID })