16 Commits
Author SHA1 Message Date
EnumDev f23537ba7d Fix typo 2026-01-03 13:46:43 +02:00
EnumDev 6e9f16837c Fix split packages with the same name as the main package being removed from the database 2026-01-02 20:08:10 +02:00
EnumDev 271e87cb77 Exit with exit code 1 if path is not owned by any package 2026-01-02 18:54:09 +02:00
EnumDev 25ccc208d7 Change module names 2026-01-01 10:20:33 +02:00
EnumDev 9c7e20d27d Remove need for config module 2026-01-01 10:15:23 +02:00
EnumDev 6206fdbf43 Add 'BPM_PKG_URL' variable in package compilation 2025-12-31 09:10:36 +02:00
EnumDev 9e48d97dc8 Add 'allow-downgrades' flag to 'update' subcommand 2025-12-28 18:30:17 +02:00
EnumDev 08a4e24889 Add confirmation prompt for syncing in 'update' subcomamnd 2025-12-26 13:05:25 +02:00
EnumDev 4a32df6335 Fix possible panic when updating packages 2025-12-26 13:02:58 +02:00
EnumDev 548b2476bd Reduce memory usage for the 'list' subcommand 2025-12-26 12:34:08 +02:00
EnumDev a660039be1 Use package info instead of BPMPackage for the local package information map 2025-12-26 11:41:07 +02:00
EnumDev 44c2678ef9 Show optional dependencies after install or update operation 2025-12-17 15:36:08 +02:00
EnumDev 2a7c6ce5d9 Count size in signed 64-bit integers 2025-12-17 13:43:23 +02:00
EnumDev f98760d1dd Rearrange function order in utils.go 2025-12-17 12:18:07 +02:00
EnumDev 2ba78f16db Remove duplicate strings in packages slice from install and remove operations 2025-12-17 12:16:45 +02:00
EnumDev 04a71b573b Improve conflict checking 2025-12-17 12:07:44 +02:00
13 changed files with 360 additions and 231 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ ROOT_COMPILATION_GID ?= 65534
build: build:
mkdir -p build mkdir -p build
cd src/bpm; $(GO) build $(GOFLAGS) -ldflags "-w -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpm/config.BpmVersion=$(VERSION)' -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 'main.BpmVersion=$(VERSION)' -X 'github.com/EnumeratedDev/bpm/src/bpmlib.rootCompilationUID=$(ROOT_COMPILATION_UID)' -X 'github.com/EnumeratedDev/bpm/src/bpmlib.rootCompilationGID=$(ROOT_COMPILATION_GID)'" -o ../../build/bpm bpm
install: build/bpm config/ install: build/bpm config/
# Create directory # Create directory
-3
View File
@@ -1,3 +0,0 @@
package config
var BpmVersion = "dev"
+3 -3
View File
@@ -1,14 +1,14 @@
module git.enumerated.dev/bubble-package-manager/bpm/src/bpm module bpm
go 1.24.0 go 1.24.0
require ( require (
git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib v0.5.0 github.com/EnumeratedDev/bpm/src/bpmlib v0.0.0
github.com/lithammer/fuzzysearch v1.1.8 github.com/lithammer/fuzzysearch v1.1.8
github.com/spf13/pflag v1.0.10 github.com/spf13/pflag v1.0.10
) )
replace git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib => ../bpmlib replace github.com/EnumeratedDev/bpm/src/bpmlib => ../bpmlib
require ( require (
github.com/drone/envsubst v1.0.3 // indirect github.com/drone/envsubst v1.0.3 // indirect
+90 -19
View File
@@ -14,8 +14,7 @@ import (
"sort" "sort"
"strings" "strings"
"git.enumerated.dev/bubble-package-manager/bpm/src/bpm/config" "github.com/EnumeratedDev/bpm/src/bpmlib"
"git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib"
"github.com/lithammer/fuzzysearch/fuzzy" "github.com/lithammer/fuzzysearch/fuzzy"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
@@ -26,6 +25,8 @@ import (
/* A simple-to-use package manager */ /* A simple-to-use package manager */
/* ------------------------------------------------------- */ /* ------------------------------------------------------- */
var BpmVersion = "dev"
var currentFlagSet *flag.FlagSet var currentFlagSet *flag.FlagSet
var exitCode = 0 var exitCode = 0
@@ -47,7 +48,7 @@ func main() {
switch subcommand { switch subcommand {
case "v", "version": case "v", "version":
fmt.Println("Bubble Package Manager (BPM)") fmt.Println("Bubble Package Manager (BPM)")
fmt.Println("Version: " + config.BpmVersion) fmt.Println("Version: " + BpmVersion)
case "q", "query": case "q", "query":
// Setup flags and help // Setup flags and help
currentFlagSet = flag.NewFlagSet("query", flag.ExitOnError) currentFlagSet = flag.NewFlagSet("query", flag.ExitOnError)
@@ -137,6 +138,7 @@ func main() {
currentFlagSet.BoolP("force", "f", false, "Bypass warnings during package update") currentFlagSet.BoolP("force", "f", false, "Bypass warnings during package update")
currentFlagSet.BoolP("yes", "y", false, "Enter 'yes' in all prompts") currentFlagSet.BoolP("yes", "y", false, "Enter 'yes' in all prompts")
currentFlagSet.BoolP("no-sync", "n", false, "Do not sync databases") currentFlagSet.BoolP("no-sync", "n", false, "Do not sync databases")
currentFlagSet.Bool("allow-downgrades", false, "Allow package downgrades")
currentFlagSet.BoolP("optional", "o", false, "Install all optional dependencies") currentFlagSet.BoolP("optional", "o", false, "Install all optional dependencies")
setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Update installed packages", os.Args[2:]) setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Update installed packages", os.Args[2:])
@@ -254,7 +256,14 @@ func showPackageInfo() {
} }
fmt.Println("File: " + abs) fmt.Println("File: " + abs)
} }
fmt.Println(bpmpkg.CreateReadableInfo(rootDir, showHumanReadableSize)) fmt.Println(bpmpkg.PkgInfo.CreateReadableInfo(rootDir))
if bpmpkg.PkgInfo.Type == "binary" {
if !showHumanReadableSize {
fmt.Printf("Installed size: %d\n", bpmpkg.GetInstalledSize())
} else {
fmt.Printf("Installed size: %s\n", bpmlib.BytesToHumanReadable(bpmpkg.GetInstalledSize()))
}
}
} }
} }
@@ -292,9 +301,21 @@ func showPackageList() {
return return
} }
installedPackages := make([]*bpmlib.BPMPackage, len(installedPackageNames)) installedPackages := make([]struct {
pkgInfo bpmlib.PackageInfo
installedSize int64
}, len(installedPackageNames))
for i, pkgName := range installedPackageNames { for i, pkgName := range installedPackageNames {
installedPackages[i] = bpmlib.GetPackage(pkgName, rootDir) pkgInfo := *bpmlib.GetPackageInfo(pkgName, rootDir)
installedSize := bpmlib.GetPackage(pkgName, rootDir).GetInstalledSize()
installedPackages[i] = struct {
pkgInfo bpmlib.PackageInfo
installedSize int64
}{
pkgInfo: pkgInfo,
installedSize: installedSize,
}
} }
databaseEntries := make([]*bpmlib.BPMDatabaseEntry, 0) databaseEntries := make([]*bpmlib.BPMDatabaseEntry, 0)
@@ -305,8 +326,11 @@ func showPackageList() {
switch sortPackages { switch sortPackages {
case "", "name": case "", "name":
case "size": case "size":
slices.SortFunc(installedPackages, func(a, b *bpmlib.BPMPackage) int { slices.SortFunc(installedPackages, func(a, b struct {
return int(b.GetInstalledSize() - a.GetInstalledSize()) pkgInfo bpmlib.PackageInfo
installedSize int64
}) int {
return int(b.installedSize - a.installedSize)
}) })
slices.SortFunc(databaseEntries, func(a, b *bpmlib.BPMDatabaseEntry) int { slices.SortFunc(databaseEntries, func(a, b *bpmlib.BPMDatabaseEntry) int {
return int(b.InstalledSize - a.InstalledSize) return int(b.InstalledSize - a.InstalledSize)
@@ -335,7 +359,7 @@ func showPackageList() {
} }
} else { } else {
for _, pkg := range installedPackages { for _, pkg := range installedPackages {
installationReason := bpmlib.GetInstallationReason(pkg.PkgInfo.Name, rootDir) installationReason := bpmlib.GetInstallationReason(pkg.pkgInfo.Name, rootDir)
if installationReason == bpmlib.InstallationReasonManual && !showManual { if installationReason == bpmlib.InstallationReasonManual && !showManual {
continue continue
} else if installationReason == bpmlib.InstallationReasonDependency && !showDepends { } else if installationReason == bpmlib.InstallationReasonDependency && !showDepends {
@@ -346,7 +370,7 @@ func showPackageList() {
continue continue
} }
fmt.Println(pkg.PkgInfo.Name) fmt.Println(pkg.pkgInfo.Name)
} }
} }
} else { } else {
@@ -366,13 +390,8 @@ func showPackageList() {
fmt.Println("No packages have been installed") fmt.Println("No packages have been installed")
return return
} }
for n, bpmpkg := range installedPackages { for n, pkg := range installedPackages {
if bpmpkg == nil { installationReason := bpmlib.GetInstallationReason(pkg.pkgInfo.Name, rootDir)
fmt.Printf("Package (%s) could not be found\n", installedPackageNames[n])
continue
}
installationReason := bpmlib.GetInstallationReason(bpmpkg.PkgInfo.Name, rootDir)
if installationReason == bpmlib.InstallationReasonManual && !showManual { if installationReason == bpmlib.InstallationReasonManual && !showManual {
continue continue
} else if installationReason == bpmlib.InstallationReasonDependency && !showDepends { } else if installationReason == bpmlib.InstallationReasonDependency && !showDepends {
@@ -386,7 +405,15 @@ func showPackageList() {
if n != 0 { if n != 0 {
fmt.Println() fmt.Println()
} }
fmt.Println(bpmpkg.CreateReadableInfo(rootDir, showHumanReadableSize))
fmt.Println(pkg.pkgInfo.CreateReadableInfo(rootDir))
if pkg.pkgInfo.Type == "binary" {
if !showHumanReadableSize {
fmt.Printf("Installed size: %d\n", pkg.installedSize)
} else {
fmt.Printf("Installed size: %s\n", bpmlib.BytesToHumanReadable(pkg.installedSize))
}
}
} }
} }
} }
@@ -577,6 +604,9 @@ func installPackages() {
} }
} }
// Get optional dependencies
optionalDepends := operation.GetOptionalDependencies()
// Execute operation // Execute operation
err = operation.Execute(verbose, force) err = operation.Execute(verbose, force)
if err != nil { if err != nil {
@@ -593,6 +623,18 @@ func installPackages() {
exitCode = 1 exitCode = 1
return return
} }
// Show optional dependencies
if !installOptional && len(optionalDepends) != 0 {
// List optional dependencies
fmt.Println("The following optional dependenices have been discovered:")
for dependant, depends := range optionalDepends {
fmt.Printf("%s: \n", dependant)
for _, depend := range depends {
fmt.Printf(" - %s\n", depend)
}
}
}
} }
func removePackages() { func removePackages() {
@@ -854,6 +896,7 @@ func updatePackages() {
force, _ := currentFlagSet.GetBool("force") force, _ := currentFlagSet.GetBool("force")
yesAll, _ := currentFlagSet.GetBool("yes") yesAll, _ := currentFlagSet.GetBool("yes")
noSync, _ := currentFlagSet.GetBool("no-sync") noSync, _ := currentFlagSet.GetBool("no-sync")
allowDowngrades, _ := currentFlagSet.GetBool("allow-downgrades")
installOptional, _ := currentFlagSet.GetBool("optional") installOptional, _ := currentFlagSet.GetBool("optional")
// Check for required permissions // Check for required permissions
@@ -882,14 +925,25 @@ func updatePackages() {
} }
} }
// Confirmation Prompt
if !noSync && !yesAll {
if !showConfirmationPrompt("Do you wish to sync all databases?", false) {
fmt.Println("Cancelling package update...")
exitCode = 1
return
}
}
// Create update operation // Create update operation
operation, err := bpmlib.UpdatePackages(rootDir, !noSync, installOptional, force, verbose) operation, err := bpmlib.UpdatePackages(rootDir, !noSync, allowDowngrades, installOptional, force, verbose)
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) log.Printf("Error: %s", err)
exitCode = 1 exitCode = 1
return return
} else if err != nil { } else if err != nil {
log.Printf("Error: could not setup operation: %s\n", err) log.Printf("Error: could not setup operation: %s\n", err)
exitCode = 1
return
} }
// Exit if operation contains no actions // Exit if operation contains no actions
@@ -942,6 +996,9 @@ func updatePackages() {
} }
} }
// Get optional dependencies
optionalDepends := operation.GetOptionalDependencies()
// Execute operation // Execute operation
err = operation.Execute(verbose, force) err = operation.Execute(verbose, force)
if err != nil { if err != nil {
@@ -958,6 +1015,18 @@ func updatePackages() {
exitCode = 1 exitCode = 1
return return
} }
// Show optional dependencies
if !installOptional && len(optionalDepends) != 0 {
// List optional dependencies
fmt.Println("The following optional dependenices have been discovered:")
for dependant, depends := range optionalDepends {
fmt.Printf("%s: \n", dependant)
for _, depend := range depends {
fmt.Printf(" - %s\n", depend)
}
}
}
} }
func getFileOwner() { func getFileOwner() {
@@ -1034,6 +1103,8 @@ func getFileOwner() {
// Print packages // Print packages
if len(pkgList) == 0 { if len(pkgList) == 0 {
fmt.Printf("%s (%s) is not owned by any packages!\n", absPath, pathType) fmt.Printf("%s (%s) is not owned by any packages!\n", absPath, pathType)
exitCode = 1
return
} else { } else {
fmt.Printf("%s (%s) is owned by the following packages:\n", absPath, pathType) fmt.Printf("%s (%s) is owned by the following packages:\n", absPath, pathType)
for _, pkg := range pkgList { for _, pkg := range pkgList {
+9 -4
View File
@@ -137,6 +137,7 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks, k
env = append(env, "BPM_PKG_NAME="+bpmpkg.PkgInfo.Name) env = append(env, "BPM_PKG_NAME="+bpmpkg.PkgInfo.Name)
env = append(env, "BPM_PKG_VERSION="+bpmpkg.PkgInfo.Version) env = append(env, "BPM_PKG_VERSION="+bpmpkg.PkgInfo.Version)
env = append(env, "BPM_PKG_REVISION="+strconv.Itoa(bpmpkg.PkgInfo.Revision)) env = append(env, "BPM_PKG_REVISION="+strconv.Itoa(bpmpkg.PkgInfo.Revision))
env = append(env, "BPM_PKG_URL="+bpmpkg.PkgInfo.Url)
env = append(env, "BPM_PKG_ARCH="+bpmpkg.PkgInfo.OutputArch) env = append(env, "BPM_PKG_ARCH="+bpmpkg.PkgInfo.OutputArch)
env = append(env, CompilationBPMConfig.CompilationEnvironment...) env = append(env, CompilationBPMConfig.CompilationEnvironment...)
@@ -434,10 +435,12 @@ func downloadPackageFiles(pkgInfo *PackageInfo, tempDirectory string, verbose bo
// Replace variables // Replace variables
replaceVars := func(s string) string { replaceVars := func(s string) string {
switch s { switch s {
case "BPM_PKG_VERSION":
return pkgInfo.Version
case "BPM_PKG_NAME": case "BPM_PKG_NAME":
return pkgInfo.Name return pkgInfo.Name
case "BPM_PKG_VERSION":
return pkgInfo.Version
case "BPM_PKG_URL":
return pkgInfo.Url
case "BPM_SOURCE": case "BPM_SOURCE":
return path.Join(tempDirectory, "source/") return path.Join(tempDirectory, "source/")
default: default:
@@ -557,10 +560,12 @@ func downloadPackageFiles(pkgInfo *PackageInfo, tempDirectory string, verbose bo
gitBranch := download.GitBranch gitBranch := download.GitBranch
gitBranch, err := envsubst.Eval(gitBranch, func(s string) string { gitBranch, err := envsubst.Eval(gitBranch, func(s string) string {
switch s { switch s {
case "BPM_PKG_VERSION":
return pkgInfo.Version
case "BPM_PKG_NAME": case "BPM_PKG_NAME":
return pkgInfo.Name return pkgInfo.Name
case "BPM_PKG_VERSION":
return pkgInfo.Version
case "BPM_PKG_URL":
return pkgInfo.Url
default: default:
return "" return ""
} }
+6 -13
View File
@@ -28,8 +28,8 @@ type BPMDatabase struct {
type BPMDatabaseEntry struct { type BPMDatabaseEntry struct {
Info *PackageInfo `yaml:"info"` Info *PackageInfo `yaml:"info"`
Filepath string `yaml:"filepath"` Filepath string `yaml:"filepath"`
DownloadSize uint64 `yaml:"download_size"` DownloadSize int64 `yaml:"download_size"`
InstalledSize uint64 `yaml:"installed_size"` InstalledSize int64 `yaml:"installed_size"`
Database *BPMDatabase Database *BPMDatabase
} }
@@ -63,11 +63,12 @@ func (db *configDatabase) ReadLocalDatabase() error {
database.Name = db.Name database.Name = db.Name
database.Source = db.Source database.Source = db.Source
entriesToRemove := make([]string, 0)
for entryName, entry := range database.Entries { for entryName, entry := range database.Entries {
entry.Database = database entry.Database = database
if entry.Info.IsSplitPackage() { if entry.Info.IsSplitPackage() {
delete(database.Entries, entryName)
// Handle split packages // Handle split packages
for _, splitPkg := range entry.Info.SplitPackages { for _, splitPkg := range entry.Info.SplitPackages {
// Turn split package into json data // Turn split package into json data
@@ -106,9 +107,6 @@ func (db *configDatabase) ReadLocalDatabase() error {
for _, p := range splitPkg.Provides { for _, p := range splitPkg.Provides {
database.VirtualPackages[p] = append(database.VirtualPackages[p], splitPkg.Name) database.VirtualPackages[p] = append(database.VirtualPackages[p], splitPkg.Name)
} }
// Add current entry to list for removal
entriesToRemove = append(entriesToRemove, entryName)
} }
} else { } else {
// Add virtual packages to database // Add virtual packages to database
@@ -118,11 +116,6 @@ func (db *configDatabase) ReadLocalDatabase() error {
} }
} }
// Remove entries
for _, entryName := range entriesToRemove {
delete(database.Entries, entryName)
}
BPMDatabases[db.Name] = database BPMDatabases[db.Name] = database
return nil return nil
@@ -400,10 +393,10 @@ func (entry *BPMDatabaseEntry) CreateReadableInfo(rootDir string, humanReadableS
ret = append(ret, "Installation Reason: "+installationReasonString) ret = append(ret, "Installation Reason: "+installationReasonString)
} }
if entry.Info.Type == "binary" { if entry.Info.Type == "binary" {
installedSize := int64(entry.InstalledSize) installedSize := entry.InstalledSize
var installedSizeStr string var installedSizeStr string
if humanReadableSize { if humanReadableSize {
installedSizeStr = bytesToHumanReadable(installedSize) installedSizeStr = BytesToHumanReadable(installedSize)
} else { } else {
installedSizeStr = strconv.FormatInt(installedSize, 10) installedSizeStr = strconv.FormatInt(installedSize, 10)
} }
+16 -28
View File
@@ -177,39 +177,33 @@ func resolvePackageDependenciesFromDatabase(resolved *[]pkgInstallationReason, u
func (pkgInfo *PackageInfo) GetPackageDependants(rootDir string) (dependants []string) { func (pkgInfo *PackageInfo) GetPackageDependants(rootDir string) (dependants []string) {
// Get installed package names // Get installed package names
pkgs, err := GetInstalledPackages(rootDir) pkgs, ok := localPackageInformation[rootDir]
if err != nil { if !ok {
return nil return nil
} }
// Loop through all installed packages // Loop through all installed packages
for _, installedPkgName := range pkgs { for _, installedPkg := range pkgs {
// Get installed BPM package
installedPkg := GetPackage(installedPkgName, rootDir)
if installedPkg == nil {
return nil
}
// Skip iteration if comparing the same packages // Skip iteration if comparing the same packages
if installedPkg.PkgInfo.Name == pkgInfo.Name { if installedPkg.Name == pkgInfo.Name {
continue continue
} }
// Add installed package to list if its dependencies include pkgName // Add installed package to list if its dependencies include pkgName
if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool { if slices.ContainsFunc(installedPkg.Depends, func(n string) bool {
return n == pkgInfo.Name return n == pkgInfo.Name
}) { }) {
dependants = append(dependants, installedPkgName) dependants = append(dependants, installedPkg.Name)
continue continue
} }
// Loop through each virtual package // Loop through each virtual package
for _, vpkg := range pkgInfo.Provides { for _, vpkg := range 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(installedPkg.PkgInfo.Depends, func(n string) bool { if slices.ContainsFunc(installedPkg.Depends, func(n string) bool {
return n == vpkg return n == vpkg
}) { }) {
dependants = append(dependants, installedPkgName) dependants = append(dependants, installedPkg.Name)
break break
} }
} }
@@ -220,39 +214,33 @@ func (pkgInfo *PackageInfo) GetPackageDependants(rootDir string) (dependants []s
func (pkgInfo *PackageInfo) GetPackageOptionalDependants(rootDir string) (dependants []string) { func (pkgInfo *PackageInfo) GetPackageOptionalDependants(rootDir string) (dependants []string) {
// Get installed package names // Get installed package names
pkgs, err := GetInstalledPackages(rootDir) pkgs, ok := localPackageInformation[rootDir]
if err != nil { if !ok {
return nil return nil
} }
// Loop through all installed packages // Loop through all installed packages
for _, installedPkgName := range pkgs { for _, installedPkg := range pkgs {
// Get installed BPM package
installedPkg := GetPackage(installedPkgName, rootDir)
if installedPkg == nil {
return nil
}
// Skip iteration if comparing the same packages // Skip iteration if comparing the same packages
if installedPkg.PkgInfo.Name == pkgInfo.Name { if installedPkg.Name == pkgInfo.Name {
continue continue
} }
// Add installed package to list if its optional dependencies include pkgName // Add installed package to list if its optional dependencies include pkgName
if slices.ContainsFunc(installedPkg.PkgInfo.OptionalDepends, func(n string) bool { if slices.ContainsFunc(installedPkg.OptionalDepends, func(n string) bool {
return n == pkgInfo.Name return n == pkgInfo.Name
}) { }) {
dependants = append(dependants, installedPkgName) dependants = append(dependants, installedPkg.Name)
continue continue
} }
// Loop through each virtual package // Loop through each virtual package
for _, vpkg := range pkgInfo.Provides { for _, vpkg := range pkgInfo.Provides {
// Add installed package to list if its optional dependencies contain a provided virtual package // Add installed package to list if its optional dependencies contain a provided virtual package
if slices.ContainsFunc(installedPkg.PkgInfo.OptionalDepends, func(n string) bool { if slices.ContainsFunc(installedPkg.OptionalDepends, func(n string) bool {
return n == vpkg return n == vpkg
}) { }) {
dependants = append(dependants, installedPkgName) dependants = append(dependants, installedPkg.Name)
break break
} }
} }
+25 -8
View File
@@ -21,7 +21,6 @@ const (
// InstallPackages installs the specified packages into the given root directory by fetching them from databases or directly from local bpm archives // InstallPackages installs the specified packages into the given root directory by fetching them from databases or directly from local bpm archives
func InstallPackages(rootDir string, forceInstallationReason InstallationReason, reinstallMethod ReinstallMethod, installOptionalDependencies, forceInstallation, verbose bool, packages ...string) (operation *BPMOperation, err error) { func InstallPackages(rootDir string, forceInstallationReason InstallationReason, reinstallMethod ReinstallMethod, installOptionalDependencies, forceInstallation, verbose bool, packages ...string) (operation *BPMOperation, err error) {
// Setup operation struct // Setup operation struct
operation = &BPMOperation{ operation = &BPMOperation{
Actions: make([]OperationAction, 0), Actions: make([]OperationAction, 0),
@@ -31,6 +30,9 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
compiledPackages: make(map[string]string), compiledPackages: make(map[string]string),
} }
// Remove duplicates from packages
packages = removeDuplicates(packages)
// Resolve packages // Resolve packages
pkgsNotFound := make([]string, 0) pkgsNotFound := make([]string, 0)
for _, pkg := range packages { for _, pkg := range packages {
@@ -145,12 +147,9 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
operation.ReplaceObsoletePackages() operation.ReplaceObsoletePackages()
// Check for conflicts // Check for conflicts
conflicts, err := operation.CheckForConflicts() conflicts := operation.CheckForConflicts()
if err != nil {
return nil, fmt.Errorf("could not complete package conflict check: %s", err)
}
if len(conflicts) > 0 { if len(conflicts) > 0 {
err = nil err = fmt.Errorf("conflicts detected")
for pkg, conflict := range conflicts { for pkg, conflict := range conflicts {
err = errors.Join(err, PackageConflictErr{pkg, conflict}) err = errors.Join(err, PackageConflictErr{pkg, conflict})
} }
@@ -196,6 +195,9 @@ func RemovePackages(rootDir string, force, cleanupDependencies bool, packages ..
compiledPackages: make(map[string]string), compiledPackages: make(map[string]string),
} }
// Remove duplicates from packages
packages = removeDuplicates(packages)
// Search for packages // Search for packages
for _, pkg := range packages { for _, pkg := range packages {
bpmpkg := GetPackage(pkg, rootDir) bpmpkg := GetPackage(pkg, rootDir)
@@ -363,7 +365,7 @@ func CleanupCache(rootDir string, cleanupCompilationFiles, cleanupCompiledPackag
} }
// UpdatePackages fetches the newest versions of all installed packages from // UpdatePackages fetches the newest versions of all installed packages from
func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependencies, forceInstallation, verbose bool) (operation *BPMOperation, err error) { func UpdatePackages(rootDir string, syncDatabase bool, allowDowngrades bool, installOptionalDependencies, forceInstallation, verbose bool) (operation *BPMOperation, err error) {
// Sync databases // Sync databases
if syncDatabase { if syncDatabase {
err := SyncDatabase(verbose) err := SyncDatabase(verbose)
@@ -417,7 +419,7 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci
return nil, fmt.Errorf("could not get package info for package (%s)", pkg) return nil, fmt.Errorf("could not get package info for package (%s)", pkg)
} else { } else {
comparison := CompareVersions(entry.Info.GetFullVersion(), installedInfo.GetFullVersion()) comparison := CompareVersions(entry.Info.GetFullVersion(), installedInfo.GetFullVersion())
if comparison > 0 { if (!allowDowngrades && comparison > 0) || (allowDowngrades && comparison != 0) {
operation.AppendAction(&FetchPackageAction{ operation.AppendAction(&FetchPackageAction{
InstallationReason: GetInstallationReason(pkg, rootDir), InstallationReason: GetInstallationReason(pkg, rootDir),
DatabaseEntry: entry, DatabaseEntry: entry,
@@ -441,6 +443,21 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci
// Replace obsolete packages // Replace obsolete packages
operation.ReplaceObsoletePackages() operation.ReplaceObsoletePackages()
// Check for conflicts
conflicts := operation.CheckForConflicts()
if len(conflicts) > 0 {
err = fmt.Errorf("conflicts detected")
for pkg, conflict := range conflicts {
err = errors.Join(err, PackageConflictErr{pkg, conflict})
}
if !forceInstallation {
return nil, err
} else {
log.Printf("Warning: %s", err)
}
}
return operation, nil return operation, nil
} }
+1 -1
View File
@@ -1,4 +1,4 @@
module git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib module github.com/EnumeratedDev/bpm/src/bpmlib
go 1.24.0 go 1.24.0
+22 -28
View File
@@ -9,7 +9,7 @@ import (
"strings" "strings"
) )
var localPackageInformation map[string]map[string]*BPMPackage = make(map[string]map[string]*BPMPackage) var localPackageInformation map[string]map[string]*PackageInfo = make(map[string]map[string]*PackageInfo)
func initializeLocalPackageInformation(rootDir string) (err error) { func initializeLocalPackageInformation(rootDir string) (err error) {
// Return if information is already initialized // Return if information is already initialized
@@ -17,7 +17,7 @@ func initializeLocalPackageInformation(rootDir string) (err error) {
return nil return nil
} }
tempPackageInformation := make(map[string]*BPMPackage) tempPackageInformation := make(map[string]*PackageInfo)
// Get path to installed package information directory // Get path to installed package information directory
installedDir := path.Join(rootDir, "var/lib/bpm/installed/") installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
@@ -25,7 +25,7 @@ func initializeLocalPackageInformation(rootDir string) (err error) {
// Get directory content // Get directory content
items, err := os.ReadDir(installedDir) items, err := os.ReadDir(installedDir)
if os.IsNotExist(err) { if os.IsNotExist(err) {
localPackageInformation[rootDir] = make(map[string]*BPMPackage) localPackageInformation[rootDir] = make(map[string]*PackageInfo)
return nil return nil
} }
if err != nil { if err != nil {
@@ -49,14 +49,8 @@ func initializeLocalPackageInformation(rootDir string) (err error) {
return err return err
} }
// Read package files
files := getPackageFiles(info.Name, rootDir)
// Add package to slice // Add package to slice
tempPackageInformation[info.Name] = &BPMPackage{ tempPackageInformation[info.Name] = info
PkgInfo: info,
PkgFiles: files,
}
} }
localPackageInformation[rootDir] = tempPackageInformation localPackageInformation[rootDir] = tempPackageInformation
@@ -71,8 +65,8 @@ func GetInstalledPackages(rootDir string) (ret []string, err error) {
} }
// Loop through each package and add it to slice // Loop through each package and add it to slice
for _, bpmpkg := range localPackageInformation[rootDir] { for _, pkgInfo := range localPackageInformation[rootDir] {
ret = append(ret, bpmpkg.PkgInfo.Name) ret = append(ret, pkgInfo.Name)
} }
// Sort packages // Sort packages
@@ -94,18 +88,6 @@ func IsPackageInstalled(pkg, rootDir string) bool {
return true return true
} }
func GetPackageInfo(pkg string, rootDir string) *PackageInfo {
// Get BPM package
bpmpkg := GetPackage(pkg, rootDir)
// Return nil if not found
if bpmpkg == nil {
return nil
}
return bpmpkg.PkgInfo
}
func IsVirtualPackage(pkg, rootDir string) (bool, string) { func IsVirtualPackage(pkg, rootDir string) (bool, string) {
pkgs, err := GetInstalledPackages(rootDir) pkgs, err := GetInstalledPackages(rootDir)
if err != nil { if err != nil {
@@ -146,15 +128,27 @@ func IsPackageProvided(pkg, rootDir string) bool {
return false return false
} }
func GetPackage(pkg, rootDir string) *BPMPackage { func GetPackageInfo(pkg string, rootDir string) *PackageInfo {
err := initializeLocalPackageInformation(rootDir) err := initializeLocalPackageInformation(rootDir)
if err != nil { if err != nil {
return nil return nil
} }
bpmpkg := localPackageInformation[rootDir][pkg] return localPackageInformation[rootDir][pkg]
}
return bpmpkg func GetPackage(pkg, rootDir string) *BPMPackage {
pkgInfo := GetPackageInfo(pkg, rootDir)
if pkgInfo == nil {
return nil
}
files := getPackageFiles(pkgInfo.Name, rootDir)
return &BPMPackage{
PkgInfo: pkgInfo,
PkgFiles: files,
}
} }
func GetAllPackageFiles(rootDir string, excludePackages ...string) (map[string][]*BPMPackage, error) { func GetAllPackageFiles(rootDir string, excludePackages ...string) (map[string][]*BPMPackage, error) {
@@ -225,7 +219,7 @@ func getPackageFiles(pkg, rootDir string) []*PackageFileEntry {
if err != nil { if err != nil {
return nil return nil
} }
size, err := strconv.ParseUint(stringEntry[len(stringEntry)-1], 0, 64) size, err := strconv.ParseInt(stringEntry[len(stringEntry)-1], 0, 64)
if err != nil { if err != nil {
return nil return nil
} }
+124 -49
View File
@@ -87,8 +87,8 @@ func (operation *BPMOperation) RemoveAction(pkg, actionType string) {
}) })
} }
func (operation *BPMOperation) GetTotalDownloadSize() uint64 { func (operation *BPMOperation) GetTotalDownloadSize() int64 {
var ret uint64 = 0 var ret int64 = 0
for _, action := range operation.Actions { for _, action := range operation.Actions {
if action.GetActionType() == "fetch" { if action.GetActionType() == "fetch" {
ret += action.(*FetchPackageAction).DatabaseEntry.DownloadSize ret += action.(*FetchPackageAction).DatabaseEntry.DownloadSize
@@ -97,8 +97,8 @@ func (operation *BPMOperation) GetTotalDownloadSize() uint64 {
return ret return ret
} }
func (operation *BPMOperation) GetTotalInstalledSize() uint64 { func (operation *BPMOperation) GetTotalInstalledSize() int64 {
var ret uint64 = 0 var ret int64 = 0
for _, action := range operation.Actions { for _, action := range operation.Actions {
if action.GetActionType() == "install" { if action.GetActionType() == "install" {
ret += action.(*InstallPackageAction).BpmPackage.GetInstalledSize() ret += action.(*InstallPackageAction).BpmPackage.GetInstalledSize()
@@ -113,14 +113,17 @@ func (operation *BPMOperation) GetFinalActionSize(rootDir string) int64 {
var ret int64 = 0 var ret int64 = 0
for _, action := range operation.Actions { for _, action := range operation.Actions {
if action.GetActionType() == "install" { if action.GetActionType() == "install" {
ret += int64(action.(*InstallPackageAction).BpmPackage.GetInstalledSize()) ret += action.(*InstallPackageAction).BpmPackage.GetInstalledSize()
if IsPackageInstalled(action.(*InstallPackageAction).BpmPackage.PkgInfo.Name, rootDir) { if IsPackageInstalled(action.(*InstallPackageAction).BpmPackage.PkgInfo.Name, rootDir) {
ret -= int64(GetPackage(action.(*InstallPackageAction).BpmPackage.PkgInfo.Name, rootDir).GetInstalledSize()) ret -= GetPackage(action.(*InstallPackageAction).BpmPackage.PkgInfo.Name, rootDir).GetInstalledSize()
} }
} else if action.GetActionType() == "fetch" { } else if action.GetActionType() == "fetch" {
ret += int64(action.(*FetchPackageAction).DatabaseEntry.InstalledSize) ret += action.(*FetchPackageAction).DatabaseEntry.InstalledSize
if IsPackageInstalled(action.(*FetchPackageAction).DatabaseEntry.Info.Name, rootDir) {
ret -= action.(*FetchPackageAction).DatabaseEntry.InstalledSize
}
} else if action.GetActionType() == "remove" { } else if action.GetActionType() == "remove" {
ret -= int64(action.(*RemovePackageAction).BpmPackage.GetInstalledSize()) ret -= action.(*RemovePackageAction).BpmPackage.GetInstalledSize()
} }
} }
return ret return ret
@@ -277,54 +280,92 @@ func (operation *BPMOperation) ReplaceObsoletePackages() {
} }
} }
func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error) { func (operation *BPMOperation) CheckForConflicts() map[string][]string {
conflicts := make(map[string][]string) conflicts := make(map[string][]string)
installedPackages, err := GetInstalledPackages(operation.RootDir)
if err != nil {
return nil, err
}
allPackages := make([]*PackageInfo, len(installedPackages))
for i, value := range installedPackages {
bpmpkg := GetPackage(value, operation.RootDir)
if bpmpkg == nil {
return nil, fmt.Errorf("could not find installed package (%s)", value)
}
allPackages[i] = bpmpkg.PkgInfo
}
// Add all new packages to the allPackages slice // Get installed packages
installedPackages := localPackageInformation[operation.RootDir]
// Get packages to be removed
removedPackages := make([]string, 0)
for _, value := range slices.Clone(operation.Actions) { for _, value := range slices.Clone(operation.Actions) {
if value.GetActionType() != "remove" {
continue
}
removedPackages = append(removedPackages, value.(*RemovePackageAction).BpmPackage.PkgInfo.Name)
}
// Check for conflicts
for _, value := range slices.Clone(operation.Actions) {
var pkgInfo *PackageInfo
if value.GetActionType() == "install" { if value.GetActionType() == "install" {
action := value.(*InstallPackageAction) pkgInfo = value.(*InstallPackageAction).BpmPackage.PkgInfo
pkgInfo := action.BpmPackage.PkgInfo
allPackages = append(allPackages, pkgInfo)
} else if value.GetActionType() == "fetch" { } else if value.GetActionType() == "fetch" {
action := value.(*FetchPackageAction) pkgInfo = value.(*FetchPackageAction).DatabaseEntry.Info
pkgInfo := action.DatabaseEntry.Info } else {
allPackages = append(allPackages, pkgInfo) continue
} else if value.GetActionType() == "remove" { }
action := value.(*RemovePackageAction)
pkgInfo := action.BpmPackage.PkgInfo // Check for conflicts with installed packages
for i := len(allPackages) - 1; i >= 0; i-- { for _, installedPkg := range installedPackages {
info := allPackages[i] // Skip if package is to be removed
if info.Name == pkgInfo.Name { if slices.Contains(removedPackages, installedPkg.Name) {
allPackages = append(allPackages[:i], allPackages[i+1:]...) continue
}
// Skip if same package
if pkgInfo.Name == installedPkg.Name {
continue
}
// Check for new package conflicts
if slices.Contains(pkgInfo.Conflicts, installedPkg.Name) {
conflicts[pkgInfo.Name] = append(conflicts[pkgInfo.Name], installedPkg.Name)
}
for _, vpkg := range installedPkg.Provides {
if slices.Contains(pkgInfo.Conflicts, vpkg) {
conflicts[pkgInfo.Name] = append(conflicts[pkgInfo.Name], vpkg+" ("+installedPkg.Name+")")
}
}
// Check for installed package conflicts
for _, vpkg := range pkgInfo.Provides {
if slices.Contains(installedPkg.Conflicts, vpkg) {
conflicts[installedPkg.Name] = append(conflicts[installedPkg.Name], vpkg+" ("+pkgInfo.Name+")")
}
}
}
// Check for conflicts with other new packages
for _, value := range slices.Clone(operation.Actions) {
var pkgInfo2 *PackageInfo
if value.GetActionType() == "install" {
pkgInfo2 = value.(*InstallPackageAction).BpmPackage.PkgInfo
} else if value.GetActionType() == "fetch" {
pkgInfo2 = value.(*FetchPackageAction).DatabaseEntry.Info
} else {
continue
}
// Skip if same package
if pkgInfo.Name == pkgInfo2.Name {
continue
}
// Check for other package conflicts
if slices.Contains(pkgInfo.Conflicts, pkgInfo2.Name) {
conflicts[pkgInfo.Name] = append(conflicts[pkgInfo.Name], pkgInfo2.Name)
}
for _, vpkg := range pkgInfo2.Provides {
if slices.Contains(pkgInfo.Conflicts, vpkg) {
conflicts[pkgInfo.Name] = append(conflicts[pkgInfo.Name], vpkg+" ("+pkgInfo2.Name+")")
} }
} }
} }
} }
for _, value := range allPackages { return conflicts
for _, conflict := range value.Conflicts {
if slices.ContainsFunc(allPackages, func(info *PackageInfo) bool {
return info.Name == conflict
}) {
conflicts[value.Name] = append(conflicts[value.Name], conflict)
}
}
}
return conflicts, nil
} }
func (operation *BPMOperation) ShowOperationSummary() { func (operation *BPMOperation) ShowOperationSummary() {
@@ -388,12 +429,12 @@ func (operation *BPMOperation) ShowOperationSummary() {
fmt.Println("Warning: Operating in " + operation.RootDir) fmt.Println("Warning: Operating in " + operation.RootDir)
} }
if operation.GetTotalDownloadSize() > 0 { if operation.GetTotalDownloadSize() > 0 {
fmt.Printf("%s will be downloaded to complete this operation\n", unsignedBytesToHumanReadable(operation.GetTotalDownloadSize())) fmt.Printf("%s will be downloaded to complete this operation\n", BytesToHumanReadable(operation.GetTotalDownloadSize()))
} }
if operation.GetFinalActionSize(operation.RootDir) > 0 { if operation.GetFinalActionSize(operation.RootDir) > 0 {
fmt.Printf("A total of %s will be installed after the operation finishes\n", bytesToHumanReadable(operation.GetFinalActionSize(operation.RootDir))) fmt.Printf("A total of %s will be installed after the operation finishes\n", BytesToHumanReadable(operation.GetFinalActionSize(operation.RootDir)))
} else if operation.GetFinalActionSize(operation.RootDir) < 0 { } else if operation.GetFinalActionSize(operation.RootDir) < 0 {
fmt.Printf("A total of %s will be freed after the operation finishes\n", strings.TrimPrefix(bytesToHumanReadable(operation.GetFinalActionSize(operation.RootDir)), "-")) fmt.Printf("A total of %s will be freed after the operation finishes\n", strings.TrimPrefix(BytesToHumanReadable(operation.GetFinalActionSize(operation.RootDir)), "-"))
} }
} }
@@ -425,6 +466,40 @@ func (operation *BPMOperation) ShowSourcePackageContent() (sourcePackagesShown i
return sourcePackagesShown, nil return sourcePackagesShown, nil
} }
func (operation *BPMOperation) GetOptionalDependencies() (optionalDepends map[string][]string) {
optionalDepends = make(map[string][]string)
// Find all optional dependencies
for _, value := range slices.Clone(operation.Actions) {
var pkgInfo *PackageInfo
if value.GetActionType() == "install" {
action := value.(*InstallPackageAction)
pkgInfo = action.BpmPackage.PkgInfo
} else if value.GetActionType() == "fetch" {
action := value.(*FetchPackageAction)
pkgInfo = action.DatabaseEntry.Info
} else {
continue
}
for _, depend := range pkgInfo.OptionalDepends {
// Skip if dependency is already installed
if IsPackageInstalled(depend, operation.RootDir) {
continue
}
// Skip if not a new dependency of the package
if installedPkg := GetPackage(pkgInfo.Name, operation.RootDir); installedPkg != nil && slices.Contains(installedPkg.PkgInfo.OptionalDepends, depend) {
continue
}
optionalDepends[pkgInfo.Name] = append(optionalDepends[pkgInfo.Name], depend)
}
}
return
}
func (operation *BPMOperation) RunHooks(verbose bool) error { func (operation *BPMOperation) RunHooks(verbose bool) error {
// Return if hooks directory does not exist // Return if hooks directory does not exist
if stat, err := os.Stat(path.Join(operation.RootDir, "var/lib/bpm/hooks")); err != nil || !stat.IsDir() { if stat, err := os.Stat(path.Join(operation.RootDir, "var/lib/bpm/hooks")); err != nil || !stat.IsDir() {
+35 -47
View File
@@ -69,11 +69,11 @@ type PackageFileEntry struct {
OctalPerms uint32 OctalPerms uint32
UserID int UserID int
GroupID int GroupID int
SizeInBytes uint64 SizeInBytes int64
} }
func (pkg *BPMPackage) GetInstalledSize() uint64 { func (pkg *BPMPackage) GetInstalledSize() int64 {
var totalSize uint64 = 0 var totalSize int64 = 0
for _, entry := range pkg.PkgFiles { for _, entry := range pkg.PkgFiles {
totalSize += entry.SizeInBytes totalSize += entry.SizeInBytes
} }
@@ -232,7 +232,7 @@ func ReadPackage(filename string) (*BPMPackage, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
size, err := strconv.ParseUint(stringEntry[len(stringEntry)-1], 0, 64) size, err := strconv.ParseInt(stringEntry[len(stringEntry)-1], 0, 64)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -536,60 +536,57 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
return pkgInfo, nil return pkgInfo, nil
} }
func (bpmpkg *BPMPackage) CreateReadableInfo(rootDir string, humanReadableSize bool) string { func (pkgInfo *PackageInfo) CreateReadableInfo(rootDir string) string {
ret := make([]string, 0) ret := make([]string, 0)
appendArray := func(label string, array []string) { appendArray := func(label string, array []string) {
if len(array) == 0 { if len(array) == 0 {
return return
} }
// Sort array
slices.Sort(array)
ret = append(ret, fmt.Sprintf("%s: %s", label, strings.Join(array, ", "))) ret = append(ret, fmt.Sprintf("%s: %s", label, strings.Join(array, ", ")))
} }
ret = append(ret, "Name: "+bpmpkg.PkgInfo.Name) ret = append(ret, "Name: "+pkgInfo.Name)
ret = append(ret, "Description: "+bpmpkg.PkgInfo.Description) ret = append(ret, "Description: "+pkgInfo.Description)
ret = append(ret, "Version: "+bpmpkg.PkgInfo.GetFullVersion()) ret = append(ret, "Version: "+pkgInfo.GetFullVersion())
if bpmpkg.PkgInfo.Url != "" { if pkgInfo.Url != "" {
ret = append(ret, "URL: "+bpmpkg.PkgInfo.Url) ret = append(ret, "URL: "+pkgInfo.Url)
} }
if bpmpkg.PkgInfo.License != "" { if pkgInfo.License != "" {
ret = append(ret, "License: "+bpmpkg.PkgInfo.License) ret = append(ret, "License: "+pkgInfo.License)
} }
ret = append(ret, "Architecture: "+bpmpkg.PkgInfo.Arch) ret = append(ret, "Architecture: "+pkgInfo.Arch)
if bpmpkg.PkgInfo.Type == "source" && bpmpkg.PkgInfo.OutputArch != "" && bpmpkg.PkgInfo.OutputArch != GetArch() { if pkgInfo.Type == "source" && pkgInfo.OutputArch != "" && pkgInfo.OutputArch != GetArch() {
ret = append(ret, "Output architecture: "+bpmpkg.PkgInfo.Arch) ret = append(ret, "Output architecture: "+pkgInfo.Arch)
} }
ret = append(ret, "Type: "+bpmpkg.PkgInfo.Type) ret = append(ret, "Type: "+pkgInfo.Type)
appendArray("Dependencies", bpmpkg.PkgInfo.Depends) appendArray("Dependencies", pkgInfo.Depends)
if bpmpkg.PkgInfo.Type == "source" { if pkgInfo.Type == "source" {
appendArray("Make Dependencies", bpmpkg.PkgInfo.MakeDepends) appendArray("Make Dependencies", pkgInfo.MakeDepends)
} }
appendArray("Optional dependencies", bpmpkg.PkgInfo.OptionalDepends) appendArray("Optional dependencies", pkgInfo.OptionalDepends)
dependants := bpmpkg.PkgInfo.GetPackageDependants(rootDir) dependants := pkgInfo.GetPackageDependants(rootDir)
if len(dependants) > 0 { if len(dependants) > 0 {
appendArray("Dependant packages", dependants) appendArray("Dependant packages", dependants)
} }
optionalDependants := bpmpkg.PkgInfo.GetPackageOptionalDependants(rootDir) optionalDependants := pkgInfo.GetPackageOptionalDependants(rootDir)
if len(optionalDependants) > 0 { if len(optionalDependants) > 0 {
appendArray("Optionally dependant packages", optionalDependants) appendArray("Optionally dependant packages", optionalDependants)
} }
appendArray("Conflicting packages", bpmpkg.PkgInfo.Conflicts) appendArray("Conflicting packages", pkgInfo.Conflicts)
appendArray("Provided packages", bpmpkg.PkgInfo.Provides) appendArray("Provided packages", pkgInfo.Provides)
appendArray("Replaces packages", bpmpkg.PkgInfo.Replaces) appendArray("Replaces packages", pkgInfo.Replaces)
if bpmpkg.PkgInfo.Type == "source" && len(bpmpkg.PkgInfo.SplitPackages) != 0 { if pkgInfo.Type == "source" && len(pkgInfo.SplitPackages) != 0 {
splitPkgs := make([]string, len(bpmpkg.PkgInfo.SplitPackages)) splitPkgs := make([]string, len(pkgInfo.SplitPackages))
for i, splitPkgInfo := range bpmpkg.PkgInfo.SplitPackages { for i, splitPkgInfo := range pkgInfo.SplitPackages {
splitPkgs[i] = splitPkgInfo.Name splitPkgs[i] = splitPkgInfo.Name
} }
appendArray("Split Packages", splitPkgs) appendArray("Split Packages", splitPkgs)
} }
if rootDir != "" && IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { if rootDir != "" && IsPackageInstalled(pkgInfo.Name, rootDir) {
installationReason := GetInstallationReason(bpmpkg.PkgInfo.Name, rootDir) installationReason := GetInstallationReason(pkgInfo.Name, rootDir)
var installationReasonString string var installationReasonString string
switch installationReason { switch installationReason {
case InstallationReasonManual: case InstallationReasonManual:
@@ -603,16 +600,7 @@ func (bpmpkg *BPMPackage) CreateReadableInfo(rootDir string, humanReadableSize b
} }
ret = append(ret, "Installation Reason: "+installationReasonString) ret = append(ret, "Installation Reason: "+installationReasonString)
} }
if bpmpkg.PkgInfo.Type == "binary" {
installedSize := int64(bpmpkg.GetInstalledSize())
var installedSizeStr string
if humanReadableSize {
installedSizeStr = bytesToHumanReadable(installedSize)
} else {
installedSizeStr = strconv.FormatInt(installedSize, 10)
}
ret = append(ret, "Installed size: "+installedSizeStr)
}
return strings.Join(ret, "\n") return strings.Join(ret, "\n")
} }
@@ -624,7 +612,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
} }
// Initialize progress bar // Initialize progress bar
bar := createProgressBar(int64(bpmpkg.GetInstalledSize()), "Installing "+bpmpkg.PkgInfo.Name, verbose) bar := createProgressBar(bpmpkg.GetInstalledSize(), "Installing "+bpmpkg.PkgInfo.Name, verbose)
defer bar.Close() defer bar.Close()
tarballFile, err := readTarballFile(filename, "files.tar.gz") tarballFile, err := readTarballFile(filename, "files.tar.gz")
@@ -1007,7 +995,7 @@ func installPackage(filename, rootDir string, verbose, force bool) error {
} }
// Add or update package information for rootDir // Add or update package information for rootDir
localPackageInformation[rootDir][bpmpkg.PkgInfo.Name] = bpmpkg localPackageInformation[rootDir][bpmpkg.PkgInfo.Name] = bpmpkg.PkgInfo
return nil return nil
} }
@@ -1039,12 +1027,12 @@ func removePackage(pkg string, verbose bool, rootDir string) error {
return err return err
} }
bar := createProgressBar(int64(bpmpkg.GetInstalledSize()), "Removing "+bpmpkg.PkgInfo.Name, verbose) bar := createProgressBar(bpmpkg.GetInstalledSize(), "Removing "+bpmpkg.PkgInfo.Name, verbose)
defer bar.Close() defer bar.Close()
// Removing package files // Removing package files
for _, entry := range fileEntries { for _, entry := range fileEntries {
bar.Add64(int64(entry.SizeInBytes)) bar.Add64(entry.SizeInBytes)
file := path.Join(rootDir, entry.Path) file := path.Join(rootDir, entry.Path)
lstat, err := os.Lstat(file) lstat, err := os.Lstat(file)
if os.IsNotExist(err) { if os.IsNotExist(err) {
+30 -29
View File
@@ -18,20 +18,6 @@ type BPMLock struct {
path string path string
} }
func (lock *BPMLock) Unlock() error {
err := lock.file.Close()
if err != nil {
return err
}
err = os.Remove(lock.path)
if err != nil {
return err
}
return nil
}
func LockBPM(rootDir string) (*BPMLock, error) { func LockBPM(rootDir string) (*BPMLock, error) {
// Create parent directories if they don't already exist // Create parent directories if they don't already exist
err := os.MkdirAll(path.Join(rootDir, "/var/lib/bpm"), 0755) err := os.MkdirAll(path.Join(rootDir, "/var/lib/bpm"), 0755)
@@ -54,6 +40,20 @@ func LockBPM(rootDir string) (*BPMLock, error) {
return &BPMLock{f, path.Join(rootDir, "var/lib/bpm/bpm.lock")}, nil return &BPMLock{f, path.Join(rootDir, "var/lib/bpm/bpm.lock")}, nil
} }
func (lock *BPMLock) Unlock() error {
err := lock.file.Close()
if err != nil {
return err
}
err = os.Remove(lock.path)
if err != nil {
return err
}
return nil
}
func GetArch() string { func GetArch() string {
uname := syscall.Utsname{} uname := syscall.Utsname{}
err := syscall.Uname(&uname) err := syscall.Uname(&uname)
@@ -69,6 +69,13 @@ func GetArch() string {
return string(byteString[:indexLength]) return string(byteString[:indexLength])
} }
func CompareVersions(version1, version2 string) int {
v1 := version.NewVersion(version1)
v2 := version.NewVersion(version2)
return v1.Compare(v2)
}
func createProgressBar(max int64, description string, hideBar bool) *progressbar.ProgressBar { func createProgressBar(max int64, description string, hideBar bool) *progressbar.ProgressBar {
var output io.Writer var output io.Writer
if hideBar { if hideBar {
@@ -112,7 +119,7 @@ func stringSliceRemove(s []string, r string) []string {
return s return s
} }
func unsignedBytesToHumanReadable(b uint64) string { func BytesToHumanReadable(b int64) string {
bf := float64(b) bf := float64(b)
for _, unit := range []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} { for _, unit := range []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} {
if math.Abs(bf) < 1024.0 { if math.Abs(bf) < 1024.0 {
@@ -123,20 +130,14 @@ func unsignedBytesToHumanReadable(b uint64) string {
return fmt.Sprintf("%.1fYiB", bf) return fmt.Sprintf("%.1fYiB", bf)
} }
func bytesToHumanReadable(b int64) string { func removeDuplicates[T comparable](sliceList []T) []T {
bf := float64(b) allKeys := make(map[T]bool)
for _, unit := range []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} { list := []T{}
if math.Abs(bf) < 1024.0 { for _, item := range sliceList {
return fmt.Sprintf("%3.1f%sB", bf, unit) if _, value := allKeys[item]; !value {
allKeys[item] = true
list = append(list, item)
} }
bf /= 1024.0
} }
return fmt.Sprintf("%.1fYiB", bf) return list
}
func CompareVersions(version1, version2 string) int {
v1 := version.NewVersion(version1)
v2 := version.NewVersion(version2)
return v1.Compare(v2)
} }