mirror of
https://github.com/EnumeratedDev/bpm.git
synced 2026-09-16 02:26:12 +00:00
Change remove subcommand behaviour to show error when breaking installed package dependencies
This commit is contained in:
+9
-3
@@ -92,7 +92,6 @@ func main() {
|
||||
currentFlagSet.BoolP("verbose", "v", false, "Show additional information about the current operation")
|
||||
currentFlagSet.BoolP("force", "f", false, "Bypass warnings during package removal")
|
||||
currentFlagSet.BoolP("yes", "y", false, "Enter 'yes' in all prompts")
|
||||
currentFlagSet.BoolP("unused", "u", false, "Remove packages only if they are not required as dependencies")
|
||||
currentFlagSet.BoolP("cleanup", "n", false, "Additionally remove all unused dependencies")
|
||||
setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Remove the specified packages", os.Args[2:])
|
||||
|
||||
@@ -496,7 +495,6 @@ func removePackages() {
|
||||
verbose, _ := currentFlagSet.GetBool("verbose")
|
||||
force, _ := currentFlagSet.GetBool("force")
|
||||
yesAll, _ := currentFlagSet.GetBool("yes")
|
||||
removeUnused, _ := currentFlagSet.GetBool("unused")
|
||||
cleanupPackages, _ := currentFlagSet.GetBool("cleanup")
|
||||
|
||||
// Get packages
|
||||
@@ -527,8 +525,16 @@ func removePackages() {
|
||||
}
|
||||
|
||||
// Create remove operation
|
||||
operation, err := bpmlib.RemovePackages(rootDir, removeUnused, cleanupPackages, packages...)
|
||||
operation, err := bpmlib.RemovePackages(rootDir, force, cleanupPackages, packages...)
|
||||
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
|
||||
log.Printf("Error: %s", err)
|
||||
exitCode = 1
|
||||
return
|
||||
} else if errors.As(err, &bpmlib.PackageRemovalDependencyErr{}) {
|
||||
for pkg, dependants := range err.(bpmlib.PackageRemovalDependencyErr).RequiredPackages {
|
||||
fmt.Printf("The following packages depend on package (%s): %s\n", pkg, strings.Join(dependants, ", "))
|
||||
}
|
||||
|
||||
log.Printf("Error: %s", err)
|
||||
exitCode = 1
|
||||
return
|
||||
|
||||
@@ -198,12 +198,9 @@ func GetPackageDependants(pkgName string, rootDir string) ([]string, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get installed package dependencies
|
||||
dependencies := installedPkg.PkgInfo.GetDependencies(false, true)
|
||||
|
||||
// Add installed package to list if its dependencies include pkgName
|
||||
if slices.ContainsFunc(dependencies, func(p pkgInstallationReason) bool {
|
||||
return p.PkgName == pkgName
|
||||
if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool {
|
||||
return n == pkgName
|
||||
}) {
|
||||
ret = append(ret, installedPkgName)
|
||||
continue
|
||||
@@ -212,8 +209,8 @@ func GetPackageDependants(pkgName string, rootDir string) ([]string, error) {
|
||||
// Loop through each virtual package
|
||||
for _, vpkg := range pkg.PkgInfo.Provides {
|
||||
// Add installed package to list if its dependencies contain a provided virtual package
|
||||
if slices.ContainsFunc(dependencies, func(p pkgInstallationReason) bool {
|
||||
return p.PkgName == vpkg
|
||||
if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool {
|
||||
return n == vpkg
|
||||
}) {
|
||||
ret = append(ret, installedPkgName)
|
||||
break
|
||||
|
||||
@@ -40,3 +40,11 @@ type PackageScriptErr struct {
|
||||
func (e PackageScriptErr) Error() string {
|
||||
return fmt.Sprintf("could not execute package script (%s) for package (%s): %s", e.packageScript, e.packageName, e.err)
|
||||
}
|
||||
|
||||
type PackageRemovalDependencyErr struct {
|
||||
RequiredPackages map[string][]string
|
||||
}
|
||||
|
||||
func (e PackageRemovalDependencyErr) Error() string {
|
||||
return "removing these package would break other installed packages"
|
||||
}
|
||||
|
||||
+36
-9
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"maps"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
@@ -186,7 +187,7 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
|
||||
}
|
||||
|
||||
// RemovePackages removes the specified packages from the given root directory
|
||||
func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencies bool, packages ...string) (operation *BPMOperation, err error) {
|
||||
func RemovePackages(rootDir string, force, cleanupDependencies bool, packages ...string) (operation *BPMOperation, err error) {
|
||||
operation = &BPMOperation{
|
||||
Actions: make([]OperationAction, 0),
|
||||
UnresolvedDepends: make([]string, 0),
|
||||
@@ -204,14 +205,6 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie
|
||||
operation.AppendAction(&RemovePackageAction{BpmPackage: bpmpkg})
|
||||
}
|
||||
|
||||
// Do not remove packages which other packages depend on
|
||||
if removeUnusedPackagesOnly {
|
||||
err := operation.RemoveNeededPackages()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not skip needed packages: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Do package cleanup
|
||||
if cleanupDependencies {
|
||||
err := operation.Cleanup(true)
|
||||
@@ -219,6 +212,40 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie
|
||||
return nil, fmt.Errorf("could not perform cleanup for operation: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Return error if other packages depend on removed ones
|
||||
if !force {
|
||||
// Get packages and their dependants
|
||||
packageDepndants := make(map[string][]string, 0)
|
||||
for _, action := range operation.Actions {
|
||||
dependants, err := GetPackageDependants(action.(*RemovePackageAction).BpmPackage.PkgInfo.Name, rootDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get package dependants: %s", err)
|
||||
}
|
||||
|
||||
packageDepndants[action.(*RemovePackageAction).BpmPackage.PkgInfo.Name] = dependants
|
||||
}
|
||||
|
||||
// Remove dependant packages from map if they are to be removed by this operation
|
||||
for pkg, required := range packageDepndants {
|
||||
required = slices.DeleteFunc(required, func(pkgName string) bool {
|
||||
_, ok := packageDepndants[pkgName]
|
||||
return ok
|
||||
})
|
||||
packageDepndants[pkg] = required
|
||||
}
|
||||
|
||||
// Remove empty keys from map
|
||||
maps.DeleteFunc(packageDepndants, func(pkg string, required []string) bool {
|
||||
return len(required) == 0
|
||||
})
|
||||
|
||||
// Return error
|
||||
if len(packageDepndants) != 0 {
|
||||
return nil, PackageRemovalDependencyErr{RequiredPackages: packageDepndants}
|
||||
}
|
||||
}
|
||||
|
||||
return operation, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user