mirror of
https://github.com/EnumeratedDev/bpm.git
synced 2026-09-26 07:26:12 +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:
|
build:
|
||||||
mkdir -p 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/
|
install: build/bpm config/
|
||||||
# Create directory
|
# Create directory
|
||||||
|
|||||||
+57
-36
@@ -92,7 +92,6 @@ func main() {
|
|||||||
currentFlagSet.BoolP("verbose", "v", false, "Show additional information about the current operation")
|
currentFlagSet.BoolP("verbose", "v", false, "Show additional information about the current operation")
|
||||||
currentFlagSet.BoolP("force", "f", false, "Bypass warnings during package removal")
|
currentFlagSet.BoolP("force", "f", false, "Bypass warnings during package removal")
|
||||||
currentFlagSet.BoolP("yes", "y", false, "Enter 'yes' in all prompts")
|
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")
|
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:])
|
setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Remove the specified packages", os.Args[2:])
|
||||||
|
|
||||||
@@ -433,15 +432,12 @@ func installPackages() {
|
|||||||
|
|
||||||
// Confirmation Prompt
|
// Confirmation Prompt
|
||||||
if !yesAll {
|
if !yesAll {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
prompt := "Do you wish to install this package?"
|
||||||
if len(operation.Actions) == 1 {
|
if len(operation.Actions) != 1 {
|
||||||
fmt.Printf("Do you wish to install this package? [y\\N] ")
|
prompt = fmt.Sprintf("Do you wish to install all %d packages?", len(operation.Actions))
|
||||||
} else {
|
|
||||||
fmt.Printf("Do you wish to install these %d packages? [y\\N] ", len(operation.Actions))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
text, _ := reader.ReadString('\n')
|
if !showConfirmationPrompt(prompt, false) {
|
||||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
|
||||||
fmt.Println("Cancelling package installation...")
|
fmt.Println("Cancelling package installation...")
|
||||||
exitCode = 1
|
exitCode = 1
|
||||||
return
|
return
|
||||||
@@ -467,11 +463,7 @@ func installPackages() {
|
|||||||
|
|
||||||
// Confirmation Prompt
|
// Confirmation Prompt
|
||||||
if sourcePackagesShown > 0 && !yesAll {
|
if sourcePackagesShown > 0 && !yesAll {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
if !showConfirmationPrompt("Do you wish to continue?", false) {
|
||||||
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" {
|
|
||||||
fmt.Println("Cancelling package installation...")
|
fmt.Println("Cancelling package installation...")
|
||||||
exitCode = 1
|
exitCode = 1
|
||||||
return
|
return
|
||||||
@@ -503,7 +495,6 @@ func removePackages() {
|
|||||||
verbose, _ := currentFlagSet.GetBool("verbose")
|
verbose, _ := currentFlagSet.GetBool("verbose")
|
||||||
force, _ := currentFlagSet.GetBool("force")
|
force, _ := currentFlagSet.GetBool("force")
|
||||||
yesAll, _ := currentFlagSet.GetBool("yes")
|
yesAll, _ := currentFlagSet.GetBool("yes")
|
||||||
removeUnused, _ := currentFlagSet.GetBool("unused")
|
|
||||||
cleanupPackages, _ := currentFlagSet.GetBool("cleanup")
|
cleanupPackages, _ := currentFlagSet.GetBool("cleanup")
|
||||||
|
|
||||||
// Get packages
|
// Get packages
|
||||||
@@ -534,8 +525,16 @@ func removePackages() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create remove operation
|
// 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{}) {
|
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)
|
log.Printf("Error: %s", err)
|
||||||
exitCode = 1
|
exitCode = 1
|
||||||
return
|
return
|
||||||
@@ -556,10 +555,12 @@ func removePackages() {
|
|||||||
|
|
||||||
// Confirmation Prompt
|
// Confirmation Prompt
|
||||||
if !yesAll {
|
if !yesAll {
|
||||||
fmt.Printf("Are you sure you wish to remove all %d packages? [y\\N] ", len(operation.Actions))
|
prompt := "Do you wish to remove this package?"
|
||||||
reader := bufio.NewReader(os.Stdin)
|
if len(operation.Actions) != 1 {
|
||||||
text, _ := reader.ReadString('\n')
|
prompt = fmt.Sprintf("Do you wish to remove all %d packages?", len(operation.Actions))
|
||||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
}
|
||||||
|
|
||||||
|
if !showConfirmationPrompt(prompt, false) {
|
||||||
fmt.Println("Cancelling package removal...")
|
fmt.Println("Cancelling package removal...")
|
||||||
exitCode = 1
|
exitCode = 1
|
||||||
return
|
return
|
||||||
@@ -667,10 +668,12 @@ func doCleanup() {
|
|||||||
|
|
||||||
// Confirmation Prompt
|
// Confirmation Prompt
|
||||||
if !yesAll {
|
if !yesAll {
|
||||||
fmt.Printf("Are you sure you wish to remove all %d packages? [y\\N] ", len(operation.Actions))
|
prompt := "Do you wish to remove this package?"
|
||||||
reader := bufio.NewReader(os.Stdin)
|
if len(operation.Actions) != 1 {
|
||||||
text, _ := reader.ReadString('\n')
|
prompt = fmt.Sprintf("Do you wish to remove all %d packages?", len(operation.Actions))
|
||||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
}
|
||||||
|
|
||||||
|
if !showConfirmationPrompt(prompt, false) {
|
||||||
fmt.Println("Cancelling package removal...")
|
fmt.Println("Cancelling package removal...")
|
||||||
exitCode = 1
|
exitCode = 1
|
||||||
return
|
return
|
||||||
@@ -720,10 +723,7 @@ func syncDatabases() {
|
|||||||
|
|
||||||
// Confirmation Prompt
|
// Confirmation Prompt
|
||||||
if !yesAll {
|
if !yesAll {
|
||||||
fmt.Printf("Are you sure you wish to sync all databases? [y\\N] ")
|
if !showConfirmationPrompt("Do you wish to sync all databases?", false) {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
|
||||||
text, _ := reader.ReadString('\n')
|
|
||||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
|
||||||
fmt.Println("Cancelling database synchronization...")
|
fmt.Println("Cancelling database synchronization...")
|
||||||
exitCode = 1
|
exitCode = 1
|
||||||
return
|
return
|
||||||
@@ -797,10 +797,12 @@ func updatePackages() {
|
|||||||
|
|
||||||
// Confirmation Prompt
|
// Confirmation Prompt
|
||||||
if !yesAll {
|
if !yesAll {
|
||||||
fmt.Printf("Are you sure you wish to update all %d packages? [y\\N] ", len(operation.Actions))
|
prompt := "Do you wish to update this package?"
|
||||||
reader := bufio.NewReader(os.Stdin)
|
if len(operation.Actions) != 1 {
|
||||||
text, _ := reader.ReadString('\n')
|
prompt = fmt.Sprintf("Do you wish to update all %d packages?", len(operation.Actions))
|
||||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
}
|
||||||
|
|
||||||
|
if !showConfirmationPrompt(prompt, false) {
|
||||||
fmt.Println("Cancelling package update...")
|
fmt.Println("Cancelling package update...")
|
||||||
exitCode = 1
|
exitCode = 1
|
||||||
return
|
return
|
||||||
@@ -826,11 +828,7 @@ func updatePackages() {
|
|||||||
|
|
||||||
// Confirmation Prompt
|
// Confirmation Prompt
|
||||||
if sourcePackagesShown > 0 && !yesAll {
|
if sourcePackagesShown > 0 && !yesAll {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
if !showConfirmationPrompt("Do you wish to continue?", false) {
|
||||||
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" {
|
|
||||||
fmt.Println("Cancelling package installation...")
|
fmt.Println("Cancelling package installation...")
|
||||||
exitCode = 1
|
exitCode = 1
|
||||||
return
|
return
|
||||||
@@ -1208,3 +1206,26 @@ func isFlagSet(flagSet *flag.FlagSet, name string) bool {
|
|||||||
})
|
})
|
||||||
return found
|
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"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
@@ -217,9 +218,8 @@ func FindReplacement(pkg string) *BPMDatabaseEntry {
|
|||||||
func ResolveVirtualPackage(vpkg string) *BPMDatabaseEntry {
|
func ResolveVirtualPackage(vpkg string) *BPMDatabaseEntry {
|
||||||
for _, db := range BPMDatabases {
|
for _, db := range BPMDatabases {
|
||||||
if v, ok := db.VirtualPackages[vpkg]; ok {
|
if v, ok := db.VirtualPackages[vpkg]; ok {
|
||||||
for _, pkg := range v {
|
slices.Sort(v)
|
||||||
return db.Entries[pkg]
|
return db.Entries[v[0]]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -198,12 +198,9 @@ func GetPackageDependants(pkgName string, rootDir string) ([]string, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get installed package dependencies
|
|
||||||
dependencies := installedPkg.PkgInfo.GetDependencies(false, true)
|
|
||||||
|
|
||||||
// Add installed package to list if its dependencies include pkgName
|
// Add installed package to list if its dependencies include pkgName
|
||||||
if slices.ContainsFunc(dependencies, func(p pkgInstallationReason) bool {
|
if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool {
|
||||||
return p.PkgName == pkgName
|
return n == pkgName
|
||||||
}) {
|
}) {
|
||||||
ret = append(ret, installedPkgName)
|
ret = append(ret, installedPkgName)
|
||||||
continue
|
continue
|
||||||
@@ -212,8 +209,8 @@ func GetPackageDependants(pkgName string, rootDir string) ([]string, error) {
|
|||||||
// Loop through each virtual package
|
// Loop through each virtual package
|
||||||
for _, vpkg := range pkg.PkgInfo.Provides {
|
for _, vpkg := range pkg.PkgInfo.Provides {
|
||||||
// Add installed package to list if its dependencies contain a provided virtual package
|
// Add installed package to list if its dependencies contain a provided virtual package
|
||||||
if slices.ContainsFunc(dependencies, func(p pkgInstallationReason) bool {
|
if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool {
|
||||||
return p.PkgName == vpkg
|
return n == vpkg
|
||||||
}) {
|
}) {
|
||||||
ret = append(ret, installedPkgName)
|
ret = append(ret, installedPkgName)
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -40,3 +40,11 @@ type PackageScriptErr struct {
|
|||||||
func (e PackageScriptErr) Error() string {
|
func (e PackageScriptErr) Error() string {
|
||||||
return fmt.Sprintf("could not execute package script (%s) for package (%s): %s", e.packageScript, e.packageName, e.err)
|
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"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"slices"
|
"slices"
|
||||||
@@ -186,7 +187,7 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemovePackages removes the specified packages from the given root directory
|
// 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{
|
operation = &BPMOperation{
|
||||||
Actions: make([]OperationAction, 0),
|
Actions: make([]OperationAction, 0),
|
||||||
UnresolvedDepends: make([]string, 0),
|
UnresolvedDepends: make([]string, 0),
|
||||||
@@ -198,27 +199,56 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie
|
|||||||
// Search for packages
|
// Search for packages
|
||||||
for _, pkg := range packages {
|
for _, pkg := range packages {
|
||||||
bpmpkg := GetPackage(pkg, rootDir)
|
bpmpkg := GetPackage(pkg, rootDir)
|
||||||
|
if isVirutal, vpkg := IsVirtualPackage(pkg, rootDir); isVirutal {
|
||||||
|
bpmpkg = GetPackage(vpkg, rootDir)
|
||||||
|
}
|
||||||
if bpmpkg == nil {
|
if bpmpkg == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
operation.AppendAction(&RemovePackageAction{BpmPackage: bpmpkg})
|
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
|
// Do package cleanup
|
||||||
if cleanupDependencies {
|
if cleanupDependencies {
|
||||||
err := operation.Cleanup(true)
|
err := operation.Cleanup(MainBPMConfig.CleanupMakeDependencies)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not perform cleanup for operation: %s", err)
|
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
|
return operation, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-1
@@ -10,6 +10,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"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
|
// Setup split package information
|
||||||
for i, splitPkg := range pkgInfo.SplitPackages {
|
for i, splitPkg := range pkgInfo.SplitPackages {
|
||||||
// Ensure split package contains a name
|
// Ensure split package contains a name
|
||||||
if splitPkg.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
|
// Turn split package into yaml data
|
||||||
|
|||||||
Reference in New Issue
Block a user