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 {
|
if t.ReposFn != nil {
|
||||||
return t.ReposFn()
|
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) {
|
func (t *DBExecutor) SatisfierFromDB(s, s2 string) (IPackage, error) {
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
package query
|
package query
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"hash/fnv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/adrg/strutil"
|
"github.com/adrg/strutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const minVotes = 30
|
const minVotes = 30
|
||||||
|
const (
|
||||||
|
separateSourceMax = 45.0
|
||||||
|
separateSourceMin = 5.0
|
||||||
|
)
|
||||||
|
|
||||||
// TODO: Add support for Popularity and LastModified
|
// TODO: Add support for Popularity and LastModified
|
||||||
func (a *abstractResults) aurSortByMetric(pkg *abstractResult) float64 {
|
func (a *abstractResults) aurSortByMetric(pkg *abstractResult) float64 {
|
||||||
@@ -58,29 +61,35 @@ func (a *abstractResults) separateSourceScore(source string, score float64) floa
|
|||||||
return 50
|
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 {
|
if v, ok := a.separateSourceCache[source]; ok {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
h := fnv.New32a()
|
// AUR is always lowest priority
|
||||||
h.Write([]byte(source))
|
if source == sourceAUR {
|
||||||
sourceScore := float64(int(h.Sum32())%9 + 2)
|
return 0
|
||||||
a.separateSourceCache[source] = sourceScore
|
}
|
||||||
|
|
||||||
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 {
|
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"
|
"github.com/Jguer/yay/v12/pkg/text"
|
||||||
)
|
)
|
||||||
|
|
||||||
const sourceAUR = "aur"
|
const sourceAUR = "AUR"
|
||||||
|
|
||||||
type SearchVerbosity int
|
type SearchVerbosity int
|
||||||
|
|
||||||
@@ -91,6 +91,7 @@ type abstractResults struct {
|
|||||||
metric strutil.StringMetric
|
metric strutil.StringMetric
|
||||||
separateSources bool
|
separateSources bool
|
||||||
sortBy string
|
sortBy string
|
||||||
|
repoOrder []string
|
||||||
|
|
||||||
distanceCache map[string]float64
|
distanceCache map[string]float64
|
||||||
separateSourceCache map[string]float64
|
separateSourceCache map[string]float64
|
||||||
@@ -143,9 +144,38 @@ func (s *SourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor
|
|||||||
metric: metric,
|
metric: metric,
|
||||||
separateSources: s.separateSources,
|
separateSources: s.separateSources,
|
||||||
sortBy: s.sortBy,
|
sortBy: s.sortBy,
|
||||||
|
repoOrder: dbExecutor.Repos(),
|
||||||
distanceCache: map[string]float64{},
|
distanceCache: map[string]float64{},
|
||||||
separateSourceCache: 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() {
|
if s.targetMode.AtLeastAUR() {
|
||||||
var aurResults []aur.Pkg
|
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)
|
sort.Sort(sortableResults)
|
||||||
s.results = sortableResults.results
|
s.results = sortableResults.results
|
||||||
|
|
||||||
|
|||||||
@@ -289,6 +289,10 @@ func TestSourceQueryBuilder(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mockDB := &mock.DBExecutor{
|
mockDB := &mock.DBExecutor{
|
||||||
|
ReposFn: func() []string {
|
||||||
|
// Match pacman.conf parsing order for source separation.
|
||||||
|
return []string{"core"}
|
||||||
|
},
|
||||||
SyncPackagesFn: func(pkgs ...string) []mock.IPackage {
|
SyncPackagesFn: func(pkgs ...string) []mock.IPackage {
|
||||||
mockDB := mock.NewDB("core")
|
mockDB := mock.NewDB("core")
|
||||||
return []mock.IPackage{
|
return []mock.IPackage{
|
||||||
|
|||||||
Reference in New Issue
Block a user