mirror of
https://github.com/chenasraf/sofmani.git
synced 2026-05-17 17:28:04 +00:00
197 lines
5.4 KiB
Go
Executable File
197 lines
5.4 KiB
Go
Executable File
package summary
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/chenasraf/sofmani/logger"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
// Action represents the action taken for an installer.
|
|
type Action int
|
|
|
|
const (
|
|
// ActionSkipped indicates the installer was skipped (platform/machine/filter mismatch).
|
|
ActionSkipped Action = iota
|
|
// ActionUpToDate indicates the software was already installed and up-to-date.
|
|
ActionUpToDate
|
|
// ActionInstalled indicates the software was newly installed.
|
|
ActionInstalled
|
|
// ActionUpgraded indicates the software was upgraded.
|
|
ActionUpgraded
|
|
)
|
|
|
|
// InstallResult represents the result of running an installer.
|
|
type InstallResult struct {
|
|
// Name is the name of the installer.
|
|
Name string
|
|
// Type is the installer type (e.g., "brew", "npm", "group").
|
|
Type string
|
|
// Action is the action that was taken.
|
|
Action Action
|
|
// Children contains results from nested installers (for group/manifest).
|
|
Children []InstallResult
|
|
// SkipSummaryInstall indicates whether to exclude this from install summary.
|
|
SkipSummaryInstall bool
|
|
// SkipSummaryUpdate indicates whether to exclude this from update summary.
|
|
SkipSummaryUpdate bool
|
|
}
|
|
|
|
// Summary collects installation results for final reporting.
|
|
type Summary struct {
|
|
results []InstallResult
|
|
}
|
|
|
|
// NewSummary creates a new Summary instance.
|
|
func NewSummary() *Summary {
|
|
return &Summary{
|
|
results: []InstallResult{},
|
|
}
|
|
}
|
|
|
|
// Add adds an installation result to the summary.
|
|
func (s *Summary) Add(result InstallResult) {
|
|
s.results = append(s.results, result)
|
|
}
|
|
|
|
// Print outputs the summary to the logger.
|
|
func (s *Summary) Print() {
|
|
installed := s.collectByAction(ActionInstalled)
|
|
upgraded := s.collectByAction(ActionUpgraded)
|
|
|
|
hasInstalled := len(installed) > 0
|
|
hasUpgraded := len(upgraded) > 0
|
|
|
|
if !hasInstalled && !hasUpgraded {
|
|
logger.Info("Summary: Nothing new to install or upgrade")
|
|
return
|
|
}
|
|
|
|
logger.Info("Summary:")
|
|
|
|
if hasInstalled {
|
|
logger.Info(" Installed:")
|
|
for _, r := range installed {
|
|
s.printResult(r, 2)
|
|
}
|
|
}
|
|
|
|
if hasUpgraded {
|
|
logger.Info(" Upgraded:")
|
|
for _, r := range upgraded {
|
|
s.printResult(r, 2)
|
|
}
|
|
}
|
|
}
|
|
|
|
// collectByAction returns all results (including nested) that match the given action.
|
|
func (s *Summary) collectByAction(action Action) []InstallResult {
|
|
return lo.FlatMap(s.results, func(r InstallResult, _ int) []InstallResult {
|
|
return collectResultsByAction(r, action)
|
|
})
|
|
}
|
|
|
|
// isContainerType returns true if the installer type is a container (group/manifest).
|
|
func isContainerType(installerType string) bool {
|
|
return installerType == "group" || installerType == "manifest"
|
|
}
|
|
|
|
// shouldSkipSummary checks if a result should be skipped from summary based on action.
|
|
func shouldSkipSummary(r InstallResult, action Action) bool {
|
|
if action == ActionInstalled && r.SkipSummaryInstall {
|
|
return true
|
|
}
|
|
if action == ActionUpgraded && r.SkipSummaryUpdate {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// collectResultsByAction recursively collects results matching the action.
|
|
// For group/manifest installers, it returns the parent with filtered children.
|
|
func collectResultsByAction(r InstallResult, action Action) []InstallResult {
|
|
var results []InstallResult
|
|
|
|
// Check if this result should be skipped from summary
|
|
if shouldSkipSummary(r, action) {
|
|
return results
|
|
}
|
|
|
|
if isContainerType(r.Type) {
|
|
// For container types (groups/manifests), only include if children match
|
|
if hasChildrenWithAction(r.Children, action) {
|
|
filtered := InstallResult{
|
|
Name: r.Name,
|
|
Type: r.Type,
|
|
Action: r.Action,
|
|
Children: filterChildrenByAction(r.Children, action),
|
|
}
|
|
results = append(results, filtered)
|
|
}
|
|
} else {
|
|
// For leaf installers, include if action matches directly
|
|
if r.Action == action {
|
|
results = append(results, InstallResult{
|
|
Name: r.Name,
|
|
Type: r.Type,
|
|
Action: r.Action,
|
|
})
|
|
}
|
|
}
|
|
|
|
return results
|
|
}
|
|
|
|
// filterChildrenByAction filters children to only include those matching the action.
|
|
func filterChildrenByAction(children []InstallResult, action Action) []InstallResult {
|
|
var filtered []InstallResult
|
|
for _, child := range children {
|
|
// Skip if this child should be excluded from summary
|
|
if shouldSkipSummary(child, action) {
|
|
continue
|
|
}
|
|
|
|
if isContainerType(child.Type) {
|
|
// For containers, only include if they have matching children
|
|
if hasChildrenWithAction(child.Children, action) {
|
|
filtered = append(filtered, InstallResult{
|
|
Name: child.Name,
|
|
Type: child.Type,
|
|
Action: child.Action,
|
|
Children: filterChildrenByAction(child.Children, action),
|
|
})
|
|
}
|
|
} else {
|
|
// For leaf installers, include if action matches
|
|
if child.Action == action {
|
|
filtered = append(filtered, InstallResult{
|
|
Name: child.Name,
|
|
Type: child.Type,
|
|
Action: child.Action,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
return filtered
|
|
}
|
|
|
|
// hasChildrenWithAction checks if any children (recursively) match the action.
|
|
func hasChildrenWithAction(children []InstallResult, action Action) bool {
|
|
return lo.SomeBy(children, func(child InstallResult) bool {
|
|
if shouldSkipSummary(child, action) {
|
|
return false
|
|
}
|
|
return child.Action == action || hasChildrenWithAction(child.Children, action)
|
|
})
|
|
}
|
|
|
|
// printResult prints a single result with the given indentation level.
|
|
func (s *Summary) printResult(r InstallResult, indent int) {
|
|
prefix := strings.Repeat(" ", indent)
|
|
logger.Info("%s- %s: %s", prefix, logger.H(r.Type), logger.H(r.Name))
|
|
|
|
for _, child := range r.Children {
|
|
s.printResult(child, indent+1)
|
|
}
|
|
}
|