mirror of
https://github.com/Jguer/yay.git
synced 2025-12-27 10:01:53 -05:00
feat(repo): Sort repository results by pacman.conf repo order (#2740)
* ci(yay): update packages on builder before building release * respect other repos in order * ensure repo ends on top in case of tie * revert dockerfile change
This commit is contained in:
@@ -167,7 +167,8 @@ func (t *DBExecutor) Repos() []string {
|
||||
if t.ReposFn != nil {
|
||||
return t.ReposFn()
|
||||
}
|
||||
panic("implement me")
|
||||
// Tests that don't care about repo ordering shouldn't need to stub this out.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *DBExecutor) SatisfierFromDB(s, s2 string) (IPackage, error) {
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"hash/fnv"
|
||||
"strings"
|
||||
|
||||
"github.com/adrg/strutil"
|
||||
)
|
||||
|
||||
const minVotes = 30
|
||||
const (
|
||||
separateSourceMax = 45.0
|
||||
separateSourceMin = 5.0
|
||||
)
|
||||
|
||||
// TODO: Add support for Popularity and LastModified
|
||||
func (a *abstractResults) aurSortByMetric(pkg *abstractResult) float64 {
|
||||
@@ -58,29 +61,35 @@ func (a *abstractResults) separateSourceScore(source string, score float64) floa
|
||||
return 50
|
||||
}
|
||||
|
||||
switch source {
|
||||
case sourceAUR:
|
||||
return 0
|
||||
case "core":
|
||||
return 40
|
||||
case "extra":
|
||||
return 30
|
||||
case "community":
|
||||
return 20
|
||||
case "multilib":
|
||||
return 10
|
||||
}
|
||||
|
||||
if v, ok := a.separateSourceCache[source]; ok {
|
||||
return v
|
||||
}
|
||||
|
||||
h := fnv.New32a()
|
||||
h.Write([]byte(source))
|
||||
sourceScore := float64(int(h.Sum32())%9 + 2)
|
||||
a.separateSourceCache[source] = sourceScore
|
||||
// AUR is always lowest priority
|
||||
if source == sourceAUR {
|
||||
return 0
|
||||
}
|
||||
|
||||
return sourceScore
|
||||
// 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
|
||||
}
|
||||
|
||||
n := len(a.repoOrder)
|
||||
if n == 1 {
|
||||
a.separateSourceCache[source] = separateSourceMax
|
||||
return separateSourceMax
|
||||
}
|
||||
|
||||
step := (separateSourceMax - separateSourceMin) / float64(n-1)
|
||||
sourceScore := separateSourceMax - (float64(i) * step)
|
||||
a.separateSourceCache[source] = sourceScore
|
||||
return sourceScore
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (a *abstractResults) calculateMetric(pkg *abstractResult) float64 {
|
||||
|
||||
47
pkg/query/metric_test.go
Normal file
47
pkg/query/metric_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package query
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSeparateSourceScore_UsesRepoOrderEvenlyDistributed(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Any non-1.0 score avoids the special-case 50 return.
|
||||
const sim = 0.5
|
||||
const delta = 1e-1
|
||||
|
||||
t.Run("arch repos (core/extra/community/multilib)", func(t *testing.T) {
|
||||
a := &abstractResults{
|
||||
separateSources: true,
|
||||
repoOrder: []string{"core", "extra", "community", "multilib"},
|
||||
separateSourceCache: map[string]float64{},
|
||||
}
|
||||
|
||||
assert.InDelta(t, 45.0, a.separateSourceScore("core", sim), delta)
|
||||
assert.InDelta(t, 31.6, a.separateSourceScore("extra", sim), delta)
|
||||
assert.InDelta(t, 18.3, a.separateSourceScore("community", sim), delta)
|
||||
assert.InDelta(t, 5.0, a.separateSourceScore("multilib", sim), delta)
|
||||
assert.Equal(t, 0.0, a.separateSourceScore(sourceAUR, sim))
|
||||
})
|
||||
|
||||
t.Run("arch arm repos (core/extra/alarm/aur)", func(t *testing.T) {
|
||||
a := &abstractResults{
|
||||
separateSources: true,
|
||||
repoOrder: []string{"core", "extra", "alarm", "aur"},
|
||||
separateSourceCache: map[string]float64{},
|
||||
}
|
||||
|
||||
// Note: AUR is not a sync repository; it is always lowest priority (0) regardless of repo order.
|
||||
assert.InDelta(t, 45.0, a.separateSourceScore("core", sim), delta)
|
||||
assert.InDelta(t, 31.6, a.separateSourceScore("extra", sim), delta)
|
||||
assert.InDelta(t, 18.3, a.separateSourceScore("alarm", sim), delta)
|
||||
assert.InDelta(t, 5.0, a.separateSourceScore("aur", sim), delta)
|
||||
assert.Equal(t, 0.0, a.separateSourceScore(sourceAUR, sim))
|
||||
})
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
)
|
||||
|
||||
const sourceAUR = "aur"
|
||||
const sourceAUR = "AUR"
|
||||
|
||||
type SearchVerbosity int
|
||||
|
||||
@@ -91,6 +91,7 @@ type abstractResults struct {
|
||||
metric strutil.StringMetric
|
||||
separateSources bool
|
||||
sortBy string
|
||||
repoOrder []string
|
||||
|
||||
distanceCache map[string]float64
|
||||
separateSourceCache map[string]float64
|
||||
@@ -143,9 +144,38 @@ func (s *SourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor
|
||||
metric: metric,
|
||||
separateSources: s.separateSources,
|
||||
sortBy: s.sortBy,
|
||||
repoOrder: dbExecutor.Repos(),
|
||||
distanceCache: map[string]float64{},
|
||||
separateSourceCache: map[string]float64{},
|
||||
}
|
||||
var repoResults []alpm.IPackage
|
||||
if s.targetMode.AtLeastRepo() {
|
||||
repoResults = dbExecutor.SyncPackages(pkgS...)
|
||||
|
||||
for i := range repoResults {
|
||||
dbName := repoResults[i].DB().Name()
|
||||
if s.queryMap[dbName] == nil {
|
||||
s.queryMap[dbName] = map[string]any{}
|
||||
}
|
||||
|
||||
s.queryMap[dbName][repoResults[i].Name()] = repoResults[i]
|
||||
|
||||
rawProvides := repoResults[i].Provides().Slice()
|
||||
|
||||
provides := make([]string, len(rawProvides))
|
||||
for j := range rawProvides {
|
||||
provides[j] = rawProvides[j].Name
|
||||
}
|
||||
|
||||
sortableResults.results = append(sortableResults.results, abstractResult{
|
||||
source: repoResults[i].DB().Name(),
|
||||
name: repoResults[i].Name(),
|
||||
description: repoResults[i].Description(),
|
||||
provides: provides,
|
||||
votes: -1,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if s.targetMode.AtLeastAUR() {
|
||||
var aurResults []aur.Pkg
|
||||
@@ -175,35 +205,6 @@ func (s *SourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor
|
||||
}
|
||||
}
|
||||
|
||||
var repoResults []alpm.IPackage
|
||||
if s.targetMode.AtLeastRepo() {
|
||||
repoResults = dbExecutor.SyncPackages(pkgS...)
|
||||
|
||||
for i := range repoResults {
|
||||
dbName := repoResults[i].DB().Name()
|
||||
if s.queryMap[dbName] == nil {
|
||||
s.queryMap[dbName] = map[string]any{}
|
||||
}
|
||||
|
||||
s.queryMap[dbName][repoResults[i].Name()] = repoResults[i]
|
||||
|
||||
rawProvides := repoResults[i].Provides().Slice()
|
||||
|
||||
provides := make([]string, len(rawProvides))
|
||||
for j := range rawProvides {
|
||||
provides[j] = rawProvides[j].Name
|
||||
}
|
||||
|
||||
sortableResults.results = append(sortableResults.results, abstractResult{
|
||||
source: repoResults[i].DB().Name(),
|
||||
name: repoResults[i].Name(),
|
||||
description: repoResults[i].Description(),
|
||||
provides: provides,
|
||||
votes: -1,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(sortableResults)
|
||||
s.results = sortableResults.results
|
||||
|
||||
|
||||
@@ -289,6 +289,10 @@ func TestSourceQueryBuilder(t *testing.T) {
|
||||
}
|
||||
|
||||
mockDB := &mock.DBExecutor{
|
||||
ReposFn: func() []string {
|
||||
// Match pacman.conf parsing order for source separation.
|
||||
return []string{"core"}
|
||||
},
|
||||
SyncPackagesFn: func(pkgs ...string) []mock.IPackage {
|
||||
mockDB := mock.NewDB("core")
|
||||
return []mock.IPackage{
|
||||
|
||||
Reference in New Issue
Block a user