Skip to content

Commit 5820ac0

Browse files
authored
Merge branch 'main' into main
2 parents 4a9011e + 577993b commit 5820ac0

7 files changed

Lines changed: 103 additions & 371 deletions

File tree

internal/image/imageos/imageos.go

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,7 +1618,7 @@ func configUserStartupScript(installRoot string, user config.UserConfig) error {
16181618
func (imageOs *ImageOs) generateSBOM(installRoot string, template *config.ImageTemplate) (string, error) {
16191619
pkgType := imageOs.chrootEnv.GetTargetOsPkgType()
16201620
sBomFNm := rpmutils.GenerateSPDXFileName(template.GetImageName())
1621-
cmd := "rpm -qa"
1621+
cmd := "rpm -qa --queryformat '%{NAME}\\n'"
16221622
if pkgType == "deb" {
16231623
cmd = "dpkg -l | awk '/^ii/ {print $2}'"
16241624
sBomFNm = debutils.GenerateSPDXFileName(template.GetImageName())
@@ -1637,25 +1637,20 @@ func (imageOs *ImageOs) generateSBOM(installRoot string, template *config.ImageT
16371637
// Create a map of normalized package names from installed packages for faster lookup
16381638
installedPkgMap := make(map[string]bool)
16391639
for _, pkg := range installRootPkgs {
1640-
// Remove architecture tag (e.g., ":amd64") if present
1640+
// For DEB packages, remove architecture tag (e.g., ":amd64") if present
16411641
normalizedPkg := pkg
1642-
if colonIndex := strings.Index(pkg, ":"); colonIndex != -1 {
1643-
normalizedPkg = pkg[:colonIndex]
1642+
if pkgType == "deb" {
1643+
if colonIndex := strings.Index(pkg, ":"); colonIndex != -1 {
1644+
normalizedPkg = pkg[:colonIndex]
1645+
}
16441646
}
16451647
installedPkgMap[normalizedPkg] = true
16461648
}
16471649

16481650
var finalPkgs []ospackage.PackageInfo
16491651
for _, pkg := range downloadedPkgs {
1650-
// Normalize package name by removing file extensions
1651-
normalizedName := pkg.Name
1652-
if strings.HasSuffix(normalizedName, ".rpm") {
1653-
normalizedName = strings.TrimSuffix(normalizedName, ".rpm")
1654-
} else if strings.HasSuffix(normalizedName, ".deb") {
1655-
normalizedName = strings.TrimSuffix(normalizedName, ".deb")
1656-
}
1657-
1658-
if installedPkgMap[normalizedName] {
1652+
// pkg.Name is the clean canonical package name (e.g., "SymCrypt", "bash")
1653+
if installedPkgMap[pkg.Name] {
16591654
finalPkgs = append(finalPkgs, pkg)
16601655
}
16611656
}

internal/ospackage/rpmutils/download.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"io"
77
"os"
8+
"path"
89
"path/filepath"
910
"strings"
1011
"time"
@@ -429,11 +430,12 @@ func DownloadPackagesComplete(pkgList []string, destDir, dotFile string, pkgSour
429430
}
430431
}
431432

432-
// Extract URLs
433+
// Extract URLs and build download list using URL basenames
434+
// (files are saved by URL basename, e.g., "SymCrypt-106.0.1-1.emt3.x86_64.rpm")
433435
urls := make([]string, len(sorted_pkgs))
434436
for i, pkg := range sorted_pkgs {
435437
urls[i] = pkg.URL
436-
downloadPkgList = append(downloadPkgList, pkg.Name)
438+
downloadPkgList = append(downloadPkgList, path.Base(pkg.URL))
437439
}
438440

439441
// Ensure dest directory exists

internal/ospackage/rpmutils/download_test.go

Lines changed: 8 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ func TestMatchRequested(t *testing.T) {
164164
name: "exact name match with .rpm extension",
165165
requests: []string{"test-package"},
166166
all: []ospackage.PackageInfo{
167-
{Name: "test-package.rpm", Arch: "x86_64"},
167+
{Name: "test-package", Arch: "x86_64", Version: "1.0-1"},
168168
},
169169
expectError: false,
170170
expectCount: 1,
@@ -173,8 +173,8 @@ func TestMatchRequested(t *testing.T) {
173173
name: "version prefix match",
174174
requests: []string{"acl"},
175175
all: []ospackage.PackageInfo{
176-
{Name: "acl-2.3.1-2.el8", Arch: "x86_64"},
177-
{Name: "acl-dev", Arch: "x86_64"}, // Should not match - not a version
176+
{Name: "acl", Version: "2.3.1-2.el8", Arch: "x86_64"},
177+
{Name: "acl-dev", Arch: "x86_64"}, // Should not match - different package
178178
},
179179
expectError: false,
180180
expectCount: 1,
@@ -183,7 +183,7 @@ func TestMatchRequested(t *testing.T) {
183183
name: "release prefix match",
184184
requests: []string{"package"},
185185
all: []ospackage.PackageInfo{
186-
{Name: "package-1.0.0", Arch: "x86_64"},
186+
{Name: "package", Version: "1.0.0", Arch: "x86_64"},
187187
},
188188
expectError: false,
189189
expectCount: 1,
@@ -211,12 +211,12 @@ func TestMatchRequested(t *testing.T) {
211211
name: "multiple candidates - pick highest",
212212
requests: []string{"package"},
213213
all: []ospackage.PackageInfo{
214-
{Name: "package-1.0.0", Arch: "x86_64"},
215-
{Name: "package-2.0.0", Arch: "x86_64"},
216-
{Name: "package-1.5.0", Arch: "x86_64"},
214+
{Name: "package", Version: "1.0.0", Arch: "x86_64"},
215+
{Name: "package", Version: "2.0.0", Arch: "x86_64"},
216+
{Name: "package", Version: "1.5.0", Arch: "x86_64"},
217217
},
218218
expectError: false,
219-
expectCount: 1, // Should pick package-2.0.0 (highest lex sort)
219+
expectCount: 1, // Should pick package with version 2.0.0 (exact name match, first found)
220220
},
221221
}
222222

@@ -240,136 +240,6 @@ func TestMatchRequested(t *testing.T) {
240240
}
241241
}
242242

243-
func TestIsAcceptedChar(t *testing.T) {
244-
testCases := []struct {
245-
name string
246-
input string
247-
expected bool
248-
}{
249-
{
250-
name: "empty string",
251-
input: "",
252-
expected: false,
253-
},
254-
{
255-
name: "only digits",
256-
input: "123",
257-
expected: true,
258-
},
259-
{
260-
name: "digits with dash",
261-
input: "1-2-3",
262-
expected: true,
263-
},
264-
{
265-
name: "contains letters",
266-
input: "1a2",
267-
expected: false,
268-
},
269-
{
270-
name: "contains special chars",
271-
input: "1.2",
272-
expected: false,
273-
},
274-
{
275-
name: "only dash",
276-
input: "-",
277-
expected: true,
278-
},
279-
{
280-
name: "mixed valid chars",
281-
input: "123-456-789",
282-
expected: true,
283-
},
284-
}
285-
286-
for _, tc := range testCases {
287-
t.Run(tc.name, func(t *testing.T) {
288-
// Since isAcceptedChar is not exported, we need to test it indirectly through isValidVersionFormat
289-
// or we need to make it exported for testing. For now, let's test it indirectly.
290-
t.Skip("isAcceptedChar is not exported - testing indirectly through isValidVersionFormat")
291-
})
292-
}
293-
}
294-
295-
func TestIsValidVersionFormat(t *testing.T) {
296-
testCases := []struct {
297-
name string
298-
input string
299-
expected bool
300-
}{
301-
{
302-
name: "empty string",
303-
input: "",
304-
expected: false,
305-
},
306-
{
307-
name: "simple version",
308-
input: "1.0.0",
309-
expected: true,
310-
},
311-
{
312-
name: "version with dash",
313-
input: "1-2.el8",
314-
expected: true,
315-
},
316-
{
317-
name: "version without dot",
318-
input: "123",
319-
expected: true,
320-
},
321-
{
322-
name: "invalid version with letters",
323-
input: "abc.def",
324-
expected: false,
325-
},
326-
{
327-
name: "version starting with letter",
328-
input: "a1.0.0",
329-
expected: false,
330-
},
331-
{
332-
name: "complex version",
333-
input: "2-3-1.el8_5",
334-
expected: true,
335-
},
336-
}
337-
338-
for _, tc := range testCases {
339-
t.Run(tc.name, func(t *testing.T) {
340-
// Since isValidVersionFormat is not exported, we'll test it indirectly
341-
// Let's create a test that exercises this through MatchRequested
342-
all := []ospackage.PackageInfo{
343-
{Name: fmt.Sprintf("package-%s", tc.input), Arch: "x86_64"},
344-
}
345-
346-
result, err := rpmutils.MatchRequested([]string{"package"}, all)
347-
348-
if tc.expected {
349-
// If the version format is valid, we should find a match
350-
if err != nil || len(result) == 0 {
351-
t.Errorf("Expected to find match for valid version format %q", tc.input)
352-
}
353-
} else {
354-
// If the version format is invalid, we should not find a match (unless it's exact)
355-
if err == nil && len(result) > 0 && result[0].Name != "package" {
356-
// Only fail if we found a match that wasn't exact
357-
exactMatch := false
358-
for _, pkg := range all {
359-
if pkg.Name == "package" {
360-
exactMatch = true
361-
break
362-
}
363-
}
364-
if !exactMatch {
365-
t.Errorf("Expected no match for invalid version format %q, but got: %v", tc.input, result)
366-
}
367-
}
368-
}
369-
})
370-
}
371-
}
372-
373243
func TestValidate(t *testing.T) {
374244
// Save original global variables
375245
originalRepoCfg := rpmutils.RepoCfg

internal/ospackage/rpmutils/helper.go

Lines changed: 16 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func resolveMultiCandidates(parentPkg ospackage.PackageInfo, candidates []ospack
2727
ver := ""
2828
hasVersionConstraint := false
2929
if len(candidates) > 0 {
30-
op, ver, hasVersionConstraint = extractVersionRequirement(parentPkg.RequiresVer, extractBasePackageNameFromFile(candidates[0].Name))
30+
op, ver, hasVersionConstraint = extractVersionRequirement(parentPkg.RequiresVer, candidates[0].Name)
3131
}
3232

3333
if hasVersionConstraint {
@@ -207,42 +207,6 @@ func compareVersions(v1, v2 string) int {
207207
return cmp
208208
}
209209

210-
// extractBasePackageNameFromFile extracts the base package name from a full package filename
211-
// e.g., "curl-8.8.0-2.azl3.x86_64.rpm" -> "curl"
212-
// e.g., "curl-devel-8.8.0-1.azl3.x86_64.rpm" -> "curl-devel"
213-
func extractBasePackageNameFromFile(fullName string) string {
214-
// Remove .rpm suffix if present
215-
name := strings.TrimSuffix(fullName, ".rpm")
216-
217-
// Split by '-' and find where the version starts
218-
parts := strings.Split(name, "-")
219-
if len(parts) < 2 {
220-
return name
221-
}
222-
223-
// Find the first part that looks like a version (starts with digit)
224-
for i := 1; i < len(parts); i++ {
225-
if len(parts[i]) > 0 && (parts[i][0] >= '0' && parts[i][0] <= '9') {
226-
// get the name
227-
maybe_name := strings.Join(parts[:i], "-")
228-
// check if version is part of the name
229-
// full name contains version, if package name has version,
230-
// it will be repeated in the full name
231-
for j := i + 1; j < len(parts); j++ {
232-
if len(parts[j]) > 0 && strings.Contains(parts[j], parts[i]) {
233-
maybe_name = strings.Join(parts[:j], "-")
234-
break
235-
}
236-
}
237-
// return name or name-version
238-
return maybe_name
239-
}
240-
}
241-
242-
// If no version-like part found, return the whole name
243-
return name
244-
}
245-
246210
// extractBaseNameFromDep takes a potentially complex requirement string
247211
// and returns only the base package/capability name.
248212
func extractBaseNameFromDep(req string) string {
@@ -268,9 +232,8 @@ func findAllCandidates(parent ospackage.PackageInfo, depName string, all []ospac
268232

269233
// First pass: look for exact name (canonical name) matches
270234
for _, pi := range all {
271-
// Extract the base package name (everything before the first '-' that starts a version)
272-
baseName := extractBasePackageNameFromFile(pi.Name)
273-
if baseName == depName {
235+
// pi.Name is already the clean package name from XML <name> element
236+
if pi.Name == depName {
274237
candidates = append(candidates, pi)
275238
}
276239
}
@@ -309,29 +272,22 @@ func findAllCandidates(parent ospackage.PackageInfo, depName string, all []ospac
309272
// ResolvePackage finds the best matching package for a given package name
310273
func ResolveTopPackageConflicts(want string, all []ospackage.PackageInfo) (ospackage.PackageInfo, bool) {
311274
var candidates []ospackage.PackageInfo
275+
exactMatch := false
312276
for _, pi := range all {
313-
// 1) exact name, e.g. acct-205-25.azl3.noarch.rpm
277+
// 1) exact name match (e.g., "acct")
314278
if pi.Name == want {
315279
candidates = append(candidates, pi)
316-
break
317-
}
318-
cleanName := extractBasePackageNameFromFile(pi.Name)
319-
// 2) base name, e.g. acct
320-
if cleanName == want {
321-
candidates = append(candidates, pi)
280+
exactMatch = true
322281
continue
323282
}
324-
// 3) prefix by want-version ("acl-")
325-
// expected pi.Name should look like openvino-2025.3.0-2025.3.0.19807-1.noarch.rpm
326-
// want = openvino-2025.3.0
327-
if strings.HasPrefix(pi.Name, want) {
328-
// Extract string after "-" and compare with pi.Version
329-
if dashIdx := strings.LastIndex(want, "-"); dashIdx != -1 {
330-
verStr := want[dashIdx+1:]
331-
if strings.Contains(pi.Version, verStr) {
332-
candidates = append(candidates, pi)
333-
continue
334-
}
283+
// 2) prefix match for version-specific requests (e.g., want = "openvino-2025.3.0")
284+
// Only try prefix match if we haven't found any exact matches
285+
if !exactMatch && strings.HasPrefix(want, pi.Name+"-") && len(want) > len(pi.Name)+1 {
286+
// Extract string after package name and compare with pi.Version
287+
verStr := want[len(pi.Name)+1:]
288+
if strings.Contains(pi.Version, verStr) {
289+
candidates = append(candidates, pi)
290+
continue
335291
}
336292
}
337293
}
@@ -340,8 +296,8 @@ func ResolveTopPackageConflicts(want string, all []ospackage.PackageInfo) (ospac
340296
return ospackage.PackageInfo{}, false
341297
}
342298

343-
// If we got an exact match in step (1), it's the only candidate
344-
if len(candidates) == 1 && (candidates[0].Name == want || extractBasePackageNameFromFile(candidates[0].Name) == want) {
299+
// Single candidate - return directly
300+
if len(candidates) == 1 {
345301
return candidates[0], true
346302
}
347303

0 commit comments

Comments
 (0)