mirror of
https://github.com/EnumeratedDev/bpm.git
synced 2026-09-25 23:16:13 +00:00
Compare commits
7
Commits
4110ad9c5c
..
0.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45c536b326
|
||
|
|
705aec8e6e
|
||
|
|
34fb7ce1d6
|
||
|
|
fee0e6a96f
|
||
|
|
c5f48becf8
|
||
|
|
ac1ba2ada3
|
||
|
|
5fb8211933
|
@@ -12,7 +12,7 @@ ROOT_COMPILATION_GID ?= 65534
|
||||
|
||||
build:
|
||||
mkdir -p build
|
||||
cd src/bpm; $(GO) build -ldflags "-w -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationUID=$(ROOT_COMPILATION_UID)' -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationGID=$(ROOT_COMPILATION_GID)'" -o ../../build/bpm git.enumerated.dev/bubble-package-manager/bpm/src/bpm
|
||||
cd src/bpm; $(GO) build $(GOFLAGS) -ldflags "-w -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationUID=$(ROOT_COMPILATION_UID)' -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationGID=$(ROOT_COMPILATION_GID)'" -o ../../build/bpm git.enumerated.dev/bubble-package-manager/bpm/src/bpm
|
||||
|
||||
install: build/bpm config/
|
||||
# Create directory
|
||||
|
||||
+57
-36
@@ -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:])
|
||||
|
||||
@@ -433,15 +432,12 @@ func installPackages() {
|
||||
|
||||
// Confirmation Prompt
|
||||
if !yesAll {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
if len(operation.Actions) == 1 {
|
||||
fmt.Printf("Do you wish to install this package? [y\\N] ")
|
||||
} else {
|
||||
fmt.Printf("Do you wish to install these %d packages? [y\\N] ", len(operation.Actions))
|
||||
prompt := "Do you wish to install this package?"
|
||||
if len(operation.Actions) != 1 {
|
||||
prompt = fmt.Sprintf("Do you wish to install all %d packages?", len(operation.Actions))
|
||||
}
|
||||
|
||||
text, _ := reader.ReadString('\n')
|
||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
||||
if !showConfirmationPrompt(prompt, false) {
|
||||
fmt.Println("Cancelling package installation...")
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -467,11 +463,7 @@ func installPackages() {
|
||||
|
||||
// Confirmation Prompt
|
||||
if sourcePackagesShown > 0 && !yesAll {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Printf("Are you sure you wish to continue? [y\\N] ")
|
||||
|
||||
text, _ := reader.ReadString('\n')
|
||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
||||
if !showConfirmationPrompt("Do you wish to continue?", false) {
|
||||
fmt.Println("Cancelling package installation...")
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -503,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
|
||||
@@ -534,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
|
||||
@@ -556,10 +555,12 @@ func removePackages() {
|
||||
|
||||
// Confirmation Prompt
|
||||
if !yesAll {
|
||||
fmt.Printf("Are you sure you wish to remove all %d packages? [y\\N] ", len(operation.Actions))
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
text, _ := reader.ReadString('\n')
|
||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
||||
prompt := "Do you wish to remove this package?"
|
||||
if len(operation.Actions) != 1 {
|
||||
prompt = fmt.Sprintf("Do you wish to remove all %d packages?", len(operation.Actions))
|
||||
}
|
||||
|
||||
if !showConfirmationPrompt(prompt, false) {
|
||||
fmt.Println("Cancelling package removal...")
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -667,10 +668,12 @@ func doCleanup() {
|
||||
|
||||
// Confirmation Prompt
|
||||
if !yesAll {
|
||||
fmt.Printf("Are you sure you wish to remove all %d packages? [y\\N] ", len(operation.Actions))
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
text, _ := reader.ReadString('\n')
|
||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
||||
prompt := "Do you wish to remove this package?"
|
||||
if len(operation.Actions) != 1 {
|
||||
prompt = fmt.Sprintf("Do you wish to remove all %d packages?", len(operation.Actions))
|
||||
}
|
||||
|
||||
if !showConfirmationPrompt(prompt, false) {
|
||||
fmt.Println("Cancelling package removal...")
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -720,10 +723,7 @@ func syncDatabases() {
|
||||
|
||||
// Confirmation Prompt
|
||||
if !yesAll {
|
||||
fmt.Printf("Are you sure you wish to sync all databases? [y\\N] ")
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
text, _ := reader.ReadString('\n')
|
||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
||||
if !showConfirmationPrompt("Do you wish to sync all databases?", false) {
|
||||
fmt.Println("Cancelling database synchronization...")
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -797,10 +797,12 @@ func updatePackages() {
|
||||
|
||||
// Confirmation Prompt
|
||||
if !yesAll {
|
||||
fmt.Printf("Are you sure you wish to update all %d packages? [y\\N] ", len(operation.Actions))
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
text, _ := reader.ReadString('\n')
|
||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
||||
prompt := "Do you wish to update this package?"
|
||||
if len(operation.Actions) != 1 {
|
||||
prompt = fmt.Sprintf("Do you wish to update all %d packages?", len(operation.Actions))
|
||||
}
|
||||
|
||||
if !showConfirmationPrompt(prompt, false) {
|
||||
fmt.Println("Cancelling package update...")
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -826,11 +828,7 @@ func updatePackages() {
|
||||
|
||||
// Confirmation Prompt
|
||||
if sourcePackagesShown > 0 && !yesAll {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Printf("Are you sure you wish to continue? [y\\N] ")
|
||||
|
||||
text, _ := reader.ReadString('\n')
|
||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
||||
if !showConfirmationPrompt("Do you wish to continue?", false) {
|
||||
fmt.Println("Cancelling package installation...")
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -1208,3 +1206,26 @@ func isFlagSet(flagSet *flag.FlagSet, name string) bool {
|
||||
})
|
||||
return found
|
||||
}
|
||||
|
||||
func showConfirmationPrompt(prompt string, defaultTo bool) bool {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
if defaultTo {
|
||||
fmt.Printf("%s [Y/n] ", prompt)
|
||||
} else {
|
||||
fmt.Printf("%s [y/N] ", prompt)
|
||||
}
|
||||
|
||||
text, _ := reader.ReadString('\n')
|
||||
text = strings.TrimSpace(text)
|
||||
|
||||
if len(text) > 0 {
|
||||
switch text[0] {
|
||||
case 'y', 'Y':
|
||||
return true
|
||||
case 'n', 'N':
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return defaultTo
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -217,9 +218,8 @@ func FindReplacement(pkg string) *BPMDatabaseEntry {
|
||||
func ResolveVirtualPackage(vpkg string) *BPMDatabaseEntry {
|
||||
for _, db := range BPMDatabases {
|
||||
if v, ok := db.VirtualPackages[vpkg]; ok {
|
||||
for _, pkg := range v {
|
||||
return db.Entries[pkg]
|
||||
}
|
||||
slices.Sort(v)
|
||||
return db.Entries[v[0]]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
+40
-10
@@ -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),
|
||||
@@ -198,27 +199,56 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie
|
||||
// Search for packages
|
||||
for _, pkg := range packages {
|
||||
bpmpkg := GetPackage(pkg, rootDir)
|
||||
if isVirutal, vpkg := IsVirtualPackage(pkg, rootDir); isVirutal {
|
||||
bpmpkg = GetPackage(vpkg, rootDir)
|
||||
}
|
||||
if bpmpkg == nil {
|
||||
continue
|
||||
}
|
||||
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)
|
||||
err := operation.Cleanup(MainBPMConfig.CleanupMakeDependencies)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
+12
-1
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"regexp"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -461,11 +462,21 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure package name is valid
|
||||
if match, _ := regexp.MatchString("^[a-zA-Z0-9._-]+$", pkgInfo.Name); !match {
|
||||
return nil, fmt.Errorf("package name (%s) is invalid", pkgInfo.Name)
|
||||
}
|
||||
|
||||
// Setup split package information
|
||||
for i, splitPkg := range pkgInfo.SplitPackages {
|
||||
// Ensure split package contains a name
|
||||
if splitPkg.Name == "" {
|
||||
return nil, fmt.Errorf("invalid split package name: %s", splitPkg.Name)
|
||||
return nil, fmt.Errorf("package name (%s) is invalid", splitPkg.Name)
|
||||
}
|
||||
|
||||
// Ensure split package name is valid
|
||||
if match, _ := regexp.MatchString("^[a-zA-Z0-9._-]+$", splitPkg.Name); !match {
|
||||
return nil, fmt.Errorf("package name (%s) is invalid", splitPkg.Name)
|
||||
}
|
||||
|
||||
// Turn split package into yaml data
|
||||
|
||||
Reference in New Issue
Block a user