fix(sync): prefer repository replacements over matching AUR upgrades (#2910)

fix upgrade handling for repo replacements
This commit is contained in:
Floze authored and GitHub committed 2026-07-18 19:43:51 +02:00
1 parent 9e47f9496d
commit 32a4c9466d
6 files changed
+122 -8

No files matched your search

+1
View File
@@ -54,6 +54,7 @@ type Executor interface {
PackageGroups(IPackage) []string
PackageOptionalDepends(IPackage) []Depend
PackageProvides(IPackage) []Depend
PackageReplaces(IPackage) []Depend
PackagesFromGroup(string) []IPackage
PackagesFromGroupAndDB(string, string) ([]IPackage, error)
RefreshHandle() error
+8
View File
@@ -415,6 +415,14 @@ func (ae *AlpmExecutor) PackageProvides(pkg alpm.Package) []alpm.Depend {
return pkg.Provides()
}
func (ae *AlpmExecutor) PackageReplaces(pkg alpm.Package) []alpm.Depend {
if pkgWithReplaces, ok := pkg.(interface{ Replaces() []alpm.Depend }); ok {
return pkgWithReplaces.Replaces()
}
return nil
}
func (ae *AlpmExecutor) PackageGroups(pkg alpm.Package) []string {
return pkg.Groups()
}
+12
View File
@@ -27,6 +27,7 @@ type DBExecutor struct {
PackageDependsFn func(IPackage) []Depend
PackageOptionalDependsFn func(alpm.Package) []alpm.Depend
PackageProvidesFn func(IPackage) []Depend
PackageReplacesFn func(IPackage) []Depend
PackagesFromGroupFn func(string) []IPackage
PackagesFromGroupAndDBFn func(string, string) ([]IPackage, error)
RefreshHandleFn func() error
@@ -134,6 +135,17 @@ func (t *DBExecutor) PackageProvides(iPackage IPackage) []Depend {
panic("implement me")
}
func (t *DBExecutor) PackageReplaces(iPackage IPackage) []Depend {
if t.PackageReplacesFn != nil {
return t.PackageReplacesFn(iPackage)
}
if pkg, ok := iPackage.(interface{ Replaces() []alpm.Depend }); ok {
return pkg.Replaces()
}
return nil
}
func (t *DBExecutor) PackagesFromGroup(s string) []IPackage {
if t.PackagesFromGroupFn != nil {
return t.PackagesFromGroupFn(s)
+2 -1
View File
@@ -25,6 +25,7 @@ type Package struct {
PReason alpm.PkgReason
PDepends DependList
PProvides DependList
PReplaces DependList
PArchitecture string
}
@@ -164,7 +165,7 @@ func (p *Package) Origin() alpm.PkgFrom {
// Replaces returns a DependList with the packages this package replaces.
func (p *Package) Replaces() []alpm.Depend {
panic("not implemented")
return p.PReplaces.Depends
}
// URL returns the upstream URL of the package.
+35 -7
View File
@@ -64,15 +64,35 @@ func (u *UpgradeService) upGraph(ctx context.Context, graph *topo.Graph[string,
filter Filter,
) (err error) {
var (
develUp UpSlice
errs []error
aurdata = make(map[string]*aur.Pkg)
aurUp UpSlice
develUp UpSlice
errs []error
aurdata = make(map[string]*aur.Pkg)
aurUp UpSlice
syncUpgrades map[string]db.SyncUpgrade
)
remote := u.dbExecutor.InstalledRemotePackages()
remoteNames := u.dbExecutor.InstalledRemotePackageNames()
if u.cfg.Mode.AtLeastRepo() {
syncUpgrades, err = u.dbExecutor.SyncUpgrades(enableDowngrade)
errs = append(errs, err)
replaced := u.syncReplacedPackageNames(syncUpgrades)
if replaced.Cardinality() > 0 {
filteredRemote := make(map[string]db.IPackage, len(remote))
for name, pkg := range remote {
if !replaced.Contains(name) {
filteredRemote[name] = pkg
}
}
remote = filteredRemote
remoteNames = slices.DeleteFunc(slices.Clone(remoteNames), func(name string) bool {
return replaced.Contains(name)
})
}
}
if u.cfg.Mode.AtLeastAUR() {
u.log.OperationInfoln(gotext.Get("Searching AUR for updates..."))
@@ -168,7 +188,6 @@ func (u *UpgradeService) upGraph(ctx context.Context, graph *topo.Graph[string,
if u.cfg.Mode.AtLeastRepo() {
u.log.OperationInfoln(gotext.Get("Searching databases for updates..."))
syncUpgrades, err := u.dbExecutor.SyncUpgrades(enableDowngrade)
for _, up := range syncUpgrades {
if filter != nil && !filter(&db.Upgrade{
Name: up.Package.Name(),
@@ -184,13 +203,22 @@ func (u *UpgradeService) upGraph(ctx context.Context, graph *topo.Graph[string,
upgradeInfo := up
graph = u.grapher.GraphSyncPkg(ctx, graph, up.Package, &upgradeInfo)
}
errs = append(errs, err)
}
return errors.Join(errs...)
}
func (u *UpgradeService) syncReplacedPackageNames(syncUpgrades map[string]db.SyncUpgrade) mapset.Set[string] {
replaced := mapset.NewThreadUnsafeSet[string]()
for _, up := range syncUpgrades {
for _, replacement := range u.dbExecutor.PackageReplaces(up.Package) {
replaced.Add(replacement.Name)
}
}
return replaced
}
func (u *UpgradeService) graphToUpSlice(graph *topo.Graph[string, *dep.InstallInfo]) (aurUp, repoUp UpSlice) {
aurUp = UpSlice{Up: make([]Upgrade, 0, graph.Len())}
repoUp = UpSlice{Up: make([]Upgrade, 0, graph.Len()), Repos: u.dbExecutor.Repos()}
+64
View File
@@ -759,6 +759,70 @@ func TestUpgradeService_GraphUpgradesNoUpdates(t *testing.T) {
}
}
func TestUpgradeService_GraphUpgradesPrefersSyncReplacementOverAUR(t *testing.T) {
t.Parallel()
coreDB := mock.NewDB("extra")
dbExe := &mock.DBExecutor{
InstalledRemotePackageNamesFn: func() []string {
return []string{"sdl2"}
},
InstalledRemotePackagesFn: func() map[string]mock.IPackage {
return map[string]mock.IPackage{
"sdl2": &mock.Package{
PName: "sdl2",
PBase: "sdl2",
PVersion: "2.30.11-1",
PReason: alpm.PkgReasonDepend,
},
}
},
SyncUpgradesFn: func(bool) (map[string]db.SyncUpgrade, error) {
return map[string]db.SyncUpgrade{
"sdl2-compat": {
Package: &mock.Package{
PName: "sdl2-compat",
PVersion: "2.32.50-1",
PReason: alpm.PkgReasonDepend,
PDB: coreDB,
PReplaces: mock.DependList{Depends: []alpm.Depend{
{Name: "sdl2"},
}},
},
LocalVersion: "-",
Reason: alpm.PkgReasonDepend,
},
}, nil
},
ReposFn: func() []string { return []string{"extra"} },
}
mockAUR := &mockaur.MockAUR{
GetFn: func(_ context.Context, query *aur.Query) ([]aur.Pkg, error) {
assert.Empty(t, query.Needles)
return []aur.Pkg{}, nil
},
}
logger := text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
grapher := dep.NewGrapher(dbExe, mockAUR, false, true, false, false, false, logger)
service := &UpgradeService{
log: logger,
grapher: grapher,
aurCache: mockAUR,
dbExecutor: dbExe,
vcsStore: &vcs.Mock{},
cfg: &settings.Configuration{Mode: parser.ModeAny},
AURWarnings: query.NewWarnings(logger),
}
graph, err := service.GraphUpgrades(t.Context(), nil, false, func(*Upgrade) bool { return true })
require.NoError(t, err)
assert.False(t, graph.Exists("sdl2"))
require.True(t, graph.Exists("sdl2-compat"))
assert.Equal(t, dep.Sync, graph.GetNodeInfo("sdl2-compat").Value.Source)
}
func TestUpgradeService_UserExcludeUpgradesWithoutLuaHookUsesNativeMenu(t *testing.T) {
t.Parallel()
graph := newUpgradeSelectTestGraph(t)