fix(query): keep pacman.conf repo order for exact name matches (#2987)
Motivation With separatesources enabled (the default), a package with the exact searched name that exists in several sync repos can be listed in the wrong order. For example, if [cachyos-extra-v3] comes before [extra] in pacman.conf, extra/chromium can still show up as the top pick instead of cachyos-extra-v3/chromium. pacman prefers the repo listed first. separateSourceScore returned a flat 50 for every exact match, whatever its source. As a result, all exact matches got the same metric (50 + 1.0). The unstable slices.SortFunc then left their relative order unspecified, so the ordering added for non-exact results did not apply to exact ones. Approach Move the repo-order scoring into a repoOrderScore helper (same logic and cache as before). For exact matches, add repoOrderScore/separateSourceMax to the 50: a bonus between 0 and 1. Exact matches still outrank every non-exact result (which scores at most 45 + a metric below 1). Among exact matches, repos earlier in pacman.conf now come first. Exact AUR matches keep 50, so an exact sync-repo match now ranks strictly above an exact AUR match instead of tying with it. Scoring for non-exact results is unchanged, and nothing changes when separatesources is disabled. Validation - go test -race ./pkg/query/... -count=1: passes. - New tests TestSourceQueryBuilderExactMatchSortsByRepoOrder (top-down and bottom-up) and TestSeparateSourceScore_ExactMatchKeepsRepoOrder fail with the previous metric.go and pass with this change. - golangci-lint run ./pkg/query/...: 0 issues; gofmt clean. - go test ./... on macOS has failures that also occur on the unchanged base: Linux-only syscall fields in pkg/settings/exe, and a timezone-dependent test in pkg/text. Not tested against a real pacman setup with custom repos. Report: https://github.com/Jguer/yay/issues/2986 Assisted-by: claude-opus-5-5 (via Claude Code) Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com> Co-authored-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
This commit is contained in:
1 parent
a2a06f4cf9
commit
ce6bcbd775
3 files changed
+109
-3
No files matched your search
+12
-3
@@ -57,10 +57,21 @@ func (a *abstractResults) separateSourceScore(source string, score float64) floa
|
||||
return 0
|
||||
}
|
||||
|
||||
sourceScore := a.repoOrderScore(source)
|
||||
|
||||
// Exact matches always rank above everything else; among them, keep
|
||||
// pacman.conf repo order by adding a bonus in [0, 1].
|
||||
if score == 1.0 {
|
||||
return 50
|
||||
return 50 + sourceScore/separateSourceMax
|
||||
}
|
||||
|
||||
return sourceScore
|
||||
}
|
||||
|
||||
// repoOrderScore scores sync repositories based on pacman.conf order (as reflected by dbExecutor.Repos()).
|
||||
// First repo gets max, last repo gets min, evenly distributed across the range.
|
||||
// AUR and unknown sources get 0.
|
||||
func (a *abstractResults) repoOrderScore(source string) float64 {
|
||||
if v, ok := a.separateSourceCache[source]; ok {
|
||||
return v
|
||||
}
|
||||
@@ -70,8 +81,6 @@ func (a *abstractResults) separateSourceScore(source string, score float64) floa
|
||||
return 0
|
||||
}
|
||||
|
||||
// Score sync repositories based on pacman.conf order (as reflected by dbExecutor.Repos()).
|
||||
// First repo gets max, last repo gets min, evenly distributed across the range.
|
||||
for i, repo := range a.repoOrder {
|
||||
if repo != source {
|
||||
continue
|
||||
|
||||
@@ -43,3 +43,23 @@ func TestSeparateSourceScore_UsesRepoOrderEvenlyDistributed(t *testing.T) {
|
||||
assert.Equal(t, 0.0, a.separateSourceScore("aur", sim))
|
||||
})
|
||||
}
|
||||
|
||||
func TestSeparateSourceScore_ExactMatchKeepsRepoOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
a := &abstractResults{
|
||||
separateSources: true,
|
||||
repoOrder: []string{"cachyos-extra-v3", "core", "extra"},
|
||||
separateSourceCache: map[string]float64{},
|
||||
}
|
||||
|
||||
custom := a.separateSourceScore("cachyos-extra-v3", 1.0)
|
||||
extra := a.separateSourceScore("extra", 1.0)
|
||||
aurScore := a.separateSourceScore("aur", 1.0)
|
||||
|
||||
assert.Greater(t, custom, extra)
|
||||
assert.Greater(t, extra, aurScore)
|
||||
assert.Equal(t, 50.0, aurScore)
|
||||
// Exact matches must still outrank any non-exact match.
|
||||
assert.Greater(t, extra+1.0, a.separateSourceScore("cachyos-extra-v3", 0.99)+0.99)
|
||||
}
|
||||
@@ -445,6 +445,83 @@ func TestSourceQueryBuilderTieSortsByRepoOrder(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceQueryBuilderExactMatchSortsByRepoOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
bottomUp bool
|
||||
repoOrder []string
|
||||
dbOrder []string
|
||||
wantSources []string
|
||||
}{
|
||||
{
|
||||
desc: "topdown custom repo first",
|
||||
repoOrder: []string{"cachyos-extra-v3", "core", "extra"},
|
||||
dbOrder: []string{"extra", "cachyos-extra-v3"},
|
||||
wantSources: []string{"cachyos-extra-v3", "extra", "aur"},
|
||||
},
|
||||
{
|
||||
desc: "topdown extra first",
|
||||
repoOrder: []string{"core", "extra", "cachyos-extra-v3"},
|
||||
dbOrder: []string{"cachyos-extra-v3", "extra"},
|
||||
wantSources: []string{"extra", "cachyos-extra-v3", "aur"},
|
||||
},
|
||||
{
|
||||
desc: "bottomup custom repo first",
|
||||
bottomUp: true,
|
||||
repoOrder: []string{"cachyos-extra-v3", "core", "extra"},
|
||||
dbOrder: []string{"extra", "cachyos-extra-v3"},
|
||||
wantSources: []string{"aur", "extra", "cachyos-extra-v3"},
|
||||
},
|
||||
}
|
||||
|
||||
mockAUR := &mockaur.MockAUR{
|
||||
GetFn: func(ctx context.Context, query *aur.Query) ([]aur.Pkg, error) {
|
||||
return []aur.Pkg{{Name: "chromium", PackageBase: "chromium", Version: "1-1"}}, nil
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mockDB := &mock.DBExecutor{
|
||||
ReposFn: func() []string { return tc.repoOrder },
|
||||
SyncPackagesFn: func(pkgs ...string) []mock.IPackage {
|
||||
res := make([]mock.IPackage, 0, len(tc.dbOrder))
|
||||
for _, dbName := range tc.dbOrder {
|
||||
res = append(res, &mock.Package{
|
||||
PBase: "chromium",
|
||||
PName: "chromium",
|
||||
PVersion: "153.0.8010.52-1",
|
||||
PDescription: "A web browser built for speed, simplicity, and security",
|
||||
PDB: mock.NewDB(dbName),
|
||||
PArchitecture: "x86_64",
|
||||
})
|
||||
}
|
||||
return res
|
||||
},
|
||||
LocalPackageFn: func(string) mock.IPackage { return nil },
|
||||
}
|
||||
|
||||
queryBuilder := NewSourceQueryBuilder(mockAUR,
|
||||
text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), false, "test"),
|
||||
"", parser.ModeAny, "", tc.bottomUp,
|
||||
false, true)
|
||||
|
||||
queryBuilder.Execute(t.Context(), mockDB, []string{"chromium"})
|
||||
|
||||
gotSources := make([]string, len(queryBuilder.results))
|
||||
for i, result := range queryBuilder.results {
|
||||
gotSources[i] = result.source
|
||||
}
|
||||
|
||||
assert.Equal(t, tc.wantSources, gotSources)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceQueryBuilderTieDoesNotSeparateSources(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in new issue
Block a user