mirror of
https://github.com/mudler/luet.git
synced 2025-09-05 17:20:29 +00:00
Allow to partially uninstall a package graph, make uninstall --full optional
This commit is contained in:
@@ -57,6 +57,8 @@ var uninstallCmd = &cobra.Command{
|
|||||||
attempts := LuetCfg.Viper.GetInt("solver.max_attempts")
|
attempts := LuetCfg.Viper.GetInt("solver.max_attempts")
|
||||||
force := LuetCfg.Viper.GetBool("force")
|
force := LuetCfg.Viper.GetBool("force")
|
||||||
nodeps := LuetCfg.Viper.GetBool("nodeps")
|
nodeps := LuetCfg.Viper.GetBool("nodeps")
|
||||||
|
full, _ := cmd.Flags().GetBool("full")
|
||||||
|
checkconflicts, _ := cmd.Flags().GetBool("conflictscheck")
|
||||||
|
|
||||||
LuetCfg.GetSolverOptions().Type = stype
|
LuetCfg.GetSolverOptions().Type = stype
|
||||||
LuetCfg.GetSolverOptions().LearnRate = float32(rate)
|
LuetCfg.GetSolverOptions().LearnRate = float32(rate)
|
||||||
@@ -66,10 +68,12 @@ var uninstallCmd = &cobra.Command{
|
|||||||
Debug("Solver", LuetCfg.GetSolverOptions().CompactString())
|
Debug("Solver", LuetCfg.GetSolverOptions().CompactString())
|
||||||
|
|
||||||
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{
|
inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{
|
||||||
Concurrency: LuetCfg.GetGeneral().Concurrency,
|
Concurrency: LuetCfg.GetGeneral().Concurrency,
|
||||||
SolverOptions: *LuetCfg.GetSolverOptions(),
|
SolverOptions: *LuetCfg.GetSolverOptions(),
|
||||||
NoDeps: nodeps,
|
NoDeps: nodeps,
|
||||||
Force: force,
|
Force: force,
|
||||||
|
FullUninstall: full,
|
||||||
|
CheckConflicts: checkconflicts,
|
||||||
})
|
})
|
||||||
|
|
||||||
if LuetCfg.GetSystem().DatabaseEngine == "boltdb" {
|
if LuetCfg.GetSystem().DatabaseEngine == "boltdb" {
|
||||||
@@ -98,8 +102,10 @@ func init() {
|
|||||||
uninstallCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")
|
uninstallCmd.Flags().Float32("solver-rate", 0.7, "Solver learning rate")
|
||||||
uninstallCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
|
uninstallCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
|
||||||
uninstallCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
|
uninstallCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
|
||||||
uninstallCmd.Flags().Bool("nodeps", false, "Don't consider package dependencies (harmful!)")
|
uninstallCmd.Flags().Bool("nodeps", false, "Don't consider package dependencies (harmful! overrides checkconflicts and full!)")
|
||||||
uninstallCmd.Flags().Bool("force", false, "Force uninstall")
|
uninstallCmd.Flags().Bool("force", false, "Force uninstall")
|
||||||
|
uninstallCmd.Flags().Bool("full", false, "Attempts to remove as much packages as possible which aren't required (slow)")
|
||||||
|
uninstallCmd.Flags().Bool("conflictscheck", true, "Check if the package marked for deletion is required by other packages")
|
||||||
|
|
||||||
RootCmd.AddCommand(uninstallCmd)
|
RootCmd.AddCommand(uninstallCmd)
|
||||||
}
|
}
|
||||||
|
@@ -41,6 +41,8 @@ type LuetInstallerOptions struct {
|
|||||||
OnlyDeps bool
|
OnlyDeps bool
|
||||||
Force bool
|
Force bool
|
||||||
PreserveSystemEssentialData bool
|
PreserveSystemEssentialData bool
|
||||||
|
FullUninstall bool
|
||||||
|
CheckConflicts bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type LuetInstaller struct {
|
type LuetInstaller struct {
|
||||||
@@ -132,10 +134,8 @@ func (l *LuetInstaller) swap(syncedRepos Repositories, toRemove pkg.Packages, to
|
|||||||
// if the old A results installed in the system. This is due to the fact that
|
// if the old A results installed in the system. This is due to the fact that
|
||||||
// now the solver enforces the constraints and explictly denies two packages
|
// now the solver enforces the constraints and explictly denies two packages
|
||||||
// of the same version installed.
|
// of the same version installed.
|
||||||
forced := false
|
forced := l.Options.Force
|
||||||
if l.Options.Force {
|
|
||||||
forced = true
|
|
||||||
}
|
|
||||||
l.Options.Force = true
|
l.Options.Force = true
|
||||||
|
|
||||||
for _, u := range toRemove {
|
for _, u := range toRemove {
|
||||||
@@ -572,10 +572,13 @@ func (l *LuetInstaller) Uninstall(p pkg.Package, s *System) error {
|
|||||||
// compute uninstall from all world - remove packages in parallel - run uninstall finalizer (in order) TODO - mark the uninstallation in db
|
// compute uninstall from all world - remove packages in parallel - run uninstall finalizer (in order) TODO - mark the uninstallation in db
|
||||||
// Get installed definition
|
// Get installed definition
|
||||||
|
|
||||||
checkConflicts := true
|
checkConflicts := l.Options.CheckConflicts
|
||||||
if l.Options.Force == true {
|
full := l.Options.FullUninstall
|
||||||
|
if l.Options.Force == true { // IF forced, we want to remove the package and all its requires
|
||||||
checkConflicts = false
|
checkConflicts = false
|
||||||
|
full = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a temporary DB with the installed packages
|
// Create a temporary DB with the installed packages
|
||||||
// so the solver is much faster finding the deptree
|
// so the solver is much faster finding the deptree
|
||||||
installedtmp := pkg.NewInMemoryDatabase(false)
|
installedtmp := pkg.NewInMemoryDatabase(false)
|
||||||
@@ -589,7 +592,7 @@ func (l *LuetInstaller) Uninstall(p pkg.Package, s *System) error {
|
|||||||
|
|
||||||
if !l.Options.NoDeps {
|
if !l.Options.NoDeps {
|
||||||
solv := solver.NewResolver(installedtmp, installedtmp, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
|
solv := solver.NewResolver(installedtmp, installedtmp, pkg.NewInMemoryDatabase(false), l.Options.SolverOptions.Resolver())
|
||||||
solution, err := solv.Uninstall(p, checkConflicts)
|
solution, err := solv.Uninstall(p, checkConflicts, full)
|
||||||
if err != nil && !l.Options.Force {
|
if err != nil && !l.Options.Force {
|
||||||
return errors.Wrap(err, "Could not solve the uninstall constraints. Tip: try with --solver-type qlearning or with --force, or by removing packages excluding their dependencies with --nodeps")
|
return errors.Wrap(err, "Could not solve the uninstall constraints. Tip: try with --solver-type qlearning or with --force, or by removing packages excluding their dependencies with --nodeps")
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,8 @@ package solver
|
|||||||
import (
|
import (
|
||||||
|
|
||||||
//. "github.com/mudler/luet/pkg/logger"
|
//. "github.com/mudler/luet/pkg/logger"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/crillab/gophersat/bf"
|
"github.com/crillab/gophersat/bf"
|
||||||
@@ -28,7 +30,7 @@ import (
|
|||||||
type PackageSolver interface {
|
type PackageSolver interface {
|
||||||
SetDefinitionDatabase(pkg.PackageDatabase)
|
SetDefinitionDatabase(pkg.PackageDatabase)
|
||||||
Install(p pkg.Packages) (PackagesAssertions, error)
|
Install(p pkg.Packages) (PackagesAssertions, error)
|
||||||
Uninstall(candidate pkg.Package, checkconflicts bool) (pkg.Packages, error)
|
Uninstall(candidate pkg.Package, checkconflicts, full bool) (pkg.Packages, error)
|
||||||
ConflictsWithInstalled(p pkg.Package) (bool, error)
|
ConflictsWithInstalled(p pkg.Package) (bool, error)
|
||||||
ConflictsWith(p pkg.Package, ls pkg.Packages) (bool, error)
|
ConflictsWith(p pkg.Package, ls pkg.Packages) (bool, error)
|
||||||
Conflicts(pack pkg.Package, lsp pkg.Packages) (bool, error)
|
Conflicts(pack pkg.Package, lsp pkg.Packages) (bool, error)
|
||||||
@@ -175,7 +177,15 @@ func (s *Solver) Conflicts(pack pkg.Package, lsp pkg.Packages) (bool, error) {
|
|||||||
visited := make(map[string]interface{})
|
visited := make(map[string]interface{})
|
||||||
revdeps := p.ExpandedRevdeps(temporarySet, visited)
|
revdeps := p.ExpandedRevdeps(temporarySet, visited)
|
||||||
|
|
||||||
return len(revdeps) != 0, nil
|
var revdepsErr error
|
||||||
|
for _, r := range revdeps {
|
||||||
|
if revdepsErr == nil {
|
||||||
|
revdepsErr = errors.New("")
|
||||||
|
}
|
||||||
|
revdepsErr = errors.New(fmt.Sprintf("%s\n%s", revdepsErr.Error(), r.HumanReadableString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(revdeps) != 0, revdepsErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConflictsWith return true if a package is part of the requirement set of a list of package
|
// ConflictsWith return true if a package is part of the requirement set of a list of package
|
||||||
@@ -271,7 +281,7 @@ func (s *Solver) Upgrade(checkconflicts bool) (pkg.Packages, PackagesAssertions,
|
|||||||
s2.SetResolver(s.Resolver)
|
s2.SetResolver(s.Resolver)
|
||||||
// Then try to uninstall the versions in the system, and store that tree
|
// Then try to uninstall the versions in the system, and store that tree
|
||||||
for _, p := range toUninstall {
|
for _, p := range toUninstall {
|
||||||
r, err := s.Uninstall(p, checkconflicts)
|
r, err := s.Uninstall(p, checkconflicts, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't uninstall selected candidate "+p.GetFingerPrint())
|
return nil, nil, errors.Wrap(err, "Could not compute upgrade - couldn't uninstall selected candidate "+p.GetFingerPrint())
|
||||||
}
|
}
|
||||||
@@ -292,7 +302,7 @@ func (s *Solver) Upgrade(checkconflicts bool) (pkg.Packages, PackagesAssertions,
|
|||||||
|
|
||||||
// Uninstall takes a candidate package and return a list of packages that would be removed
|
// Uninstall takes a candidate package and return a list of packages that would be removed
|
||||||
// in order to purge the candidate. Returns error if unsat.
|
// in order to purge the candidate. Returns error if unsat.
|
||||||
func (s *Solver) Uninstall(c pkg.Package, checkconflicts bool) (pkg.Packages, error) {
|
func (s *Solver) Uninstall(c pkg.Package, checkconflicts, full bool) (pkg.Packages, error) {
|
||||||
var res pkg.Packages
|
var res pkg.Packages
|
||||||
candidate, err := s.InstalledDatabase.FindPackage(c)
|
candidate, err := s.InstalledDatabase.FindPackage(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -311,6 +321,16 @@ func (s *Solver) Uninstall(c pkg.Package, checkconflicts bool) (pkg.Packages, er
|
|||||||
// Build a fake "Installed" - Candidate and its requires tree
|
// Build a fake "Installed" - Candidate and its requires tree
|
||||||
var InstalledMinusCandidate pkg.Packages
|
var InstalledMinusCandidate pkg.Packages
|
||||||
|
|
||||||
|
// We are asked to not perform a full uninstall (checking all the possible requires that could
|
||||||
|
// be removed). Let's only check if we can remove the selected package
|
||||||
|
if !full && checkconflicts {
|
||||||
|
if conflicts, err := s.Conflicts(candidate, s.Installed()); conflicts {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return pkg.Packages{candidate}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Can be optimized
|
// TODO: Can be optimized
|
||||||
for _, i := range s.Installed() {
|
for _, i := range s.Installed() {
|
||||||
if !i.Matches(candidate) {
|
if !i.Matches(candidate) {
|
||||||
|
@@ -547,7 +547,7 @@ var _ = Describe("Solver", func() {
|
|||||||
}
|
}
|
||||||
s = NewSolver(dbInstalled, dbDefinitions, db)
|
s = NewSolver(dbInstalled, dbDefinitions, db)
|
||||||
|
|
||||||
solution, err := s.Uninstall(A, true)
|
solution, err := s.Uninstall(A, true, true)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
Expect(solution).To(ContainElement(A.IsFlagged(false)))
|
Expect(solution).To(ContainElement(A.IsFlagged(false)))
|
||||||
@@ -573,7 +573,7 @@ var _ = Describe("Solver", func() {
|
|||||||
}
|
}
|
||||||
s = NewSolver(dbInstalled, dbDefinitions, db)
|
s = NewSolver(dbInstalled, dbDefinitions, db)
|
||||||
|
|
||||||
solution, err := s.Uninstall(&pkg.DefaultPackage{Name: "A", Version: ">1.0"}, true)
|
solution, err := s.Uninstall(&pkg.DefaultPackage{Name: "A", Version: ">1.0"}, true, true)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
Expect(solution).To(ContainElement(A.IsFlagged(false)))
|
Expect(solution).To(ContainElement(A.IsFlagged(false)))
|
||||||
@@ -692,7 +692,7 @@ var _ = Describe("Solver", func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val, err := s.Conflicts(A, dbInstalled.World())
|
val, err := s.Conflicts(A, dbInstalled.World())
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err.Error()).To(Equal("\n/B-"))
|
||||||
Expect(val).To(BeTrue())
|
Expect(val).To(BeTrue())
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -716,7 +716,7 @@ var _ = Describe("Solver", func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val, err := s.Conflicts(D, dbInstalled.World())
|
val, err := s.Conflicts(D, dbInstalled.World())
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err.Error()).To(Equal("\n/A-\n/B-"))
|
||||||
Expect(val).To(BeTrue())
|
Expect(val).To(BeTrue())
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -781,7 +781,7 @@ var _ = Describe("Solver", func() {
|
|||||||
_, err := dbInstalled.CreatePackage(p)
|
_, err := dbInstalled.CreatePackage(p)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
}
|
||||||
solution, err := s.Uninstall(A, true)
|
solution, err := s.Uninstall(A, true, true)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
Expect(solution).To(ContainElement(A.IsFlagged(false)))
|
Expect(solution).To(ContainElement(A.IsFlagged(false)))
|
||||||
@@ -805,7 +805,7 @@ var _ = Describe("Solver", func() {
|
|||||||
_, err := dbInstalled.CreatePackage(p)
|
_, err := dbInstalled.CreatePackage(p)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
}
|
||||||
solution, err := s.Uninstall(A, true)
|
solution, err := s.Uninstall(A, true, true)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
Expect(solution).To(ContainElement(A.IsFlagged(false)))
|
Expect(solution).To(ContainElement(A.IsFlagged(false)))
|
||||||
@@ -828,7 +828,7 @@ var _ = Describe("Solver", func() {
|
|||||||
_, err := dbInstalled.CreatePackage(p)
|
_, err := dbInstalled.CreatePackage(p)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
}
|
||||||
solution, err := s.Uninstall(A, true)
|
solution, err := s.Uninstall(A, true, true)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
Expect(solution).To(ContainElement(A.IsFlagged(false)))
|
Expect(solution).To(ContainElement(A.IsFlagged(false)))
|
||||||
@@ -853,7 +853,7 @@ var _ = Describe("Solver", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
}
|
||||||
|
|
||||||
solution, err := s.Uninstall(A, true)
|
solution, err := s.Uninstall(A, true, true)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
Expect(solution).To(ContainElement(A.IsFlagged(false)))
|
Expect(solution).To(ContainElement(A.IsFlagged(false)))
|
||||||
@@ -879,7 +879,7 @@ var _ = Describe("Solver", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
}
|
||||||
|
|
||||||
solution, err := s.Uninstall(A, true)
|
solution, err := s.Uninstall(A, true, true)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
Expect(solution).To(ContainElement(A.IsFlagged(false)))
|
Expect(solution).To(ContainElement(A.IsFlagged(false)))
|
||||||
|
Reference in New Issue
Block a user