Skip to content

Commit 38f6fa8

Browse files
committed
ordered output and consistent installation time
1 parent f1b955f commit 38f6fa8

File tree

8 files changed

+38
-20
lines changed

8 files changed

+38
-20
lines changed

comp/softwareinventory/impl/status.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package softwareinventoryimpl
88
import (
99
"embed"
1010
"io"
11+
"sort"
1112
"strings"
1213
"time"
1314

@@ -70,6 +71,8 @@ func formatYYYYMMDD(ts string) (string, error) {
7071
// This method processes the cached inventory data and formats it for display
7172
// in the status output. It handles date formatting, computes statistics by
7273
// software type, and organizes the data by software ID for easy lookup.
74+
// Entries are sorted by Source (software type) and then by DisplayName for
75+
// easier navigation in the GUI and status output.
7376
// Note: Stats are computed from deduplicated entries to ensure consistency
7477
// between the total count and the breakdown by type.
7578
func (is *softwareInventory) populateStatus(status map[string]interface{}) {
@@ -84,21 +87,34 @@ func (is *softwareInventory) populateStatus(status map[string]interface{}) {
8487
data[inventory.GetID()] = inventory
8588
}
8689

90+
// Convert to slice and sort by Source (category), then DisplayName
91+
sortedEntries := make([]software.Entry, 0, len(data))
92+
for _, v := range data {
93+
sortedEntries = append(sortedEntries, v.(software.Entry))
94+
}
95+
sort.Slice(sortedEntries, func(i, j int) bool {
96+
// First sort by Source (software type)
97+
if sortedEntries[i].Source != sortedEntries[j].Source {
98+
return sortedEntries[i].Source < sortedEntries[j].Source
99+
}
100+
// Then sort by DisplayName within each category
101+
return sortedEntries[i].DisplayName < sortedEntries[j].DisplayName
102+
})
103+
87104
// Second pass: compute stats from deduplicated entries
88105
// This ensures stats sum matches the total count
89106
stats := map[string]int{}
90107
brokenCount := 0
91-
for _, v := range data {
92-
inventory := v.(software.Entry)
108+
for _, inventory := range sortedEntries {
93109
stats[inventory.Source]++
94110
if strings.Contains(inventory.Status, "broken") {
95111
brokenCount++
96112
}
97113
}
98114

99-
status["software_inventory_metadata"] = data
115+
status["software_inventory_metadata"] = sortedEntries
100116
status["software_inventory_stats"] = stats
101-
status["software_inventory_total"] = len(data)
117+
status["software_inventory_total"] = len(sortedEntries)
102118
// Only include broken count if there are broken entries
103119
if brokenCount > 0 {
104120
status["software_inventory_broken"] = brokenCount

comp/softwareinventory/impl/status_templates/inventoryHTML.tmpl

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<div class="stat">
22
<span class="stat_title">Software Inventory Metadata</span>
3-
{{- $swMap := index . "software_inventory_metadata" }}
4-
{{- $total := index . "software_inventory_total" }}
5-
{{- $stats := index . "software_inventory_stats" }}
6-
{{- $broken := index . "software_inventory_broken" }}
7-
{{- if index $swMap "error" }}
8-
<div>Error refreshing software inventory: {{ index $swMap "error" }}</div>
3+
{{- $swSlice := .software_inventory_metadata }}
4+
{{- $total := .software_inventory_total }}
5+
{{- $stats := .software_inventory_stats }}
6+
{{- $broken := .software_inventory_broken }}
7+
{{- if .error }}
8+
<div>Error refreshing software inventory: {{ .error }}</div>
99
{{- else }}
1010
<div style="margin-bottom: 1em; padding: 0.5em; background: #f5f5f5; border-radius: 4px;">
1111
<strong>Summary:</strong> {{ $total }} entries{{- if $broken }} (<span style="color: #d00;">{{ $broken }} broken</span>){{- end }}
@@ -28,7 +28,7 @@
2828
<span id="sw-filter-count" style="margin-left: 1em; color: #666;"></span>
2929
</div>
3030
<div id="sw-entries-container" class="stat_data inventory-scrollbox" style="max-height: 400px; overflow-y: auto;">
31-
{{- range $productCode, $meta := $swMap }}
31+
{{- range $meta := $swSlice }}
3232
<details class="sw-entry" data-sw-type="{{ $meta.Source }}">
3333
<summary>
3434
{{- if $meta.DisplayName }}{{ $meta.DisplayName }}{{ if $meta.Version }} {{ $meta.Version }}{{ end }}{{- else }}{{ $meta.ProductCode }}{{- end }}

pkg/inventory/software/collector.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,10 @@ type Entry struct {
6363
Version string `json:"version"`
6464

6565
// InstallDate is the date when the software was installed on the system.
66-
// The format may vary by platform but is typically in ISO 8601 format
67-
// or a platform-specific date format (e.g., "2023-01-15T10:30:00Z").
66+
// The format is RFC3339 (ISO 8601): "2006-01-02T15:04:05Z07:00"
67+
// For example: "2023-01-15T10:30:00Z"
68+
// All timestamps are in UTC (indicated by the Z suffix).
69+
// When displayed in the GUI/status output, it is formatted as "YYYY/MM/DD" (date only).
6870
// This field is optional and may be empty if the installation date
6971
// cannot be determined.
7072
InstallDate string `json:"deployment_time,omitempty"`

pkg/inventory/software/collector_darwin_apps.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func (c *applicationsCollector) Collect() ([]*Entry, []*Warning, error) {
177177
// Get install date from file modification time
178178
var installDate string
179179
if info, err := os.Stat(appPath); err == nil {
180-
installDate = info.ModTime().UTC().Format(time.RFC3339Nano)
180+
installDate = info.ModTime().UTC().Format(time.RFC3339)
181181
}
182182

183183
// Determine the software type and installation source

pkg/inventory/software/collector_darwin_homebrew.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,13 @@ func (c *homebrewCollector) Collect() ([]*Entry, []*Warning, error) {
166166
receiptPath := filepath.Join(versionPath, "INSTALL_RECEIPT.json")
167167
if receipt, err := parseHomebrewReceipt(receiptPath); err == nil {
168168
if receipt.Time > 0 {
169-
installDate = time.Unix(receipt.Time, 0).UTC().Format(time.RFC3339Nano)
169+
installDate = time.Unix(receipt.Time, 0).UTC().Format(time.RFC3339)
170170
}
171171
installedOnRequest = receipt.InstalledOnRequest
172172
} else {
173173
// Fall back to directory modification time
174174
if info, err := os.Stat(versionPath); err == nil {
175-
installDate = info.ModTime().UTC().Format(time.RFC3339Nano)
175+
installDate = info.ModTime().UTC().Format(time.RFC3339)
176176
}
177177
}
178178

@@ -273,7 +273,7 @@ func (c *homebrewCollector) Collect() ([]*Entry, []*Warning, error) {
273273
// Get install date from directory
274274
var installDate string
275275
if info, err := os.Stat(versionPath); err == nil {
276-
installDate = info.ModTime().UTC().Format(time.RFC3339Nano)
276+
installDate = info.ModTime().UTC().Format(time.RFC3339)
277277
}
278278

279279
entry := &Entry{

pkg/inventory/software/collector_darwin_kext.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func (c *kernelExtensionsCollector) Collect() ([]*Entry, []*Warning, error) {
7979
// Get install date from file modification time
8080
var installDate string
8181
if info, err := os.Stat(kextPath); err == nil {
82-
installDate = info.ModTime().UTC().Format(time.RFC3339Nano)
82+
installDate = info.ModTime().UTC().Format(time.RFC3339)
8383
}
8484

8585
// Determine architecture

pkg/inventory/software/collector_darwin_macports.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func (c *macPortsCollector) Collect() ([]*Entry, []*Warning, error) {
113113
// Convert Unix timestamp to RFC3339
114114
var installDateStr string
115115
if installDate > 0 {
116-
installDateStr = time.Unix(installDate, 0).UTC().Format(time.RFC3339Nano)
116+
installDateStr = time.Unix(installDate, 0).UTC().Format(time.RFC3339)
117117
}
118118

119119
// Determine status

pkg/inventory/software/collector_darwin_sysext.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ func (c *systemExtensionsCollector) Collect() ([]*Entry, []*Warning, error) {
193193
var installDate string
194194
if sysExt.OriginPath != "" {
195195
if info, err := os.Stat(sysExt.OriginPath); err == nil {
196-
installDate = info.ModTime().UTC().Format(time.RFC3339Nano)
196+
installDate = info.ModTime().UTC().Format(time.RFC3339)
197197
}
198198
}
199199

0 commit comments

Comments
 (0)