From 6e11f937a66a389c88a315558c42b8847402e1be Mon Sep 17 00:00:00 2001 From: EnumDev Date: Wed, 23 Oct 2024 16:10:42 +0300 Subject: [PATCH 01/88] Added conflict checking --- main.go | 19 ++++++++++++++++++ utils/operations.go | 47 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/main.go b/main.go index c5b69d8..fbeeedc 100644 --- a/main.go +++ b/main.go @@ -222,6 +222,25 @@ func resolveCommand() { } } + // Check for conflicts + conflicts, err := operation.CheckForConflicts() + if err != nil { + log.Fatalf("Error: could not complete package conflict check: %s\n", err) + } + if len(conflicts) > 0 { + if !force { + log.Println("Error: conflicting packages found") + } else { + log.Fatalf("Warning: conflicting packages found") + } + for pkg, conflict := range conflicts { + fmt.Printf("%s is in conflict with the following packages: %s\n", pkg, strings.Join(conflict, ", ")) + } + if !force { + os.Exit(0) + } + } + // Show operation summary operation.ShowOperationSummary() diff --git a/utils/operations.go b/utils/operations.go index 57772d3..2035fad 100644 --- a/utils/operations.go +++ b/utils/operations.go @@ -122,6 +122,53 @@ func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, instal return nil } +func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error) { + 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, errors.New(fmt.Sprintf("could not find installed package (%s)", value)) + } + allPackages[i] = bpmpkg.PkgInfo + } + + // Add all new packages to the allPackages slice + for _, value := range slices.Clone(operation.Actions) { + if value.GetActionType() == "install" { + action := value.(*InstallPackageAction) + pkgInfo := action.BpmPackage.PkgInfo + allPackages = append(allPackages, pkgInfo) + } else if value.GetActionType() == "fetch" { + action := value.(*FetchPackageAction) + pkgInfo := action.RepositoryEntry.Info + allPackages = append(allPackages, pkgInfo) + } else if value.GetActionType() == "remove" { + action := value.(*RemovePackageAction) + pkgInfo := action.BpmPackage.PkgInfo + slices.DeleteFunc(allPackages, func(info *PackageInfo) bool { + return info.Name == pkgInfo.Name + }) + } + } + + for _, value := range allPackages { + 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() { if len(operation.Actions) == 0 { fmt.Println("All packages are up to date!") From 2c7c4eeb73e1282061565ce0cbe40d948c6fcc4c Mon Sep 17 00:00:00 2001 From: EnumDev Date: Wed, 23 Oct 2024 19:13:18 +0300 Subject: [PATCH 02/88] Fixed small bug regarding installation reasons not always being read correctly --- utils/package_utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/package_utils.go b/utils/package_utils.go index 1ef7f16..d4e23c9 100644 --- a/utils/package_utils.go +++ b/utils/package_utils.go @@ -95,7 +95,7 @@ func GetInstallationReason(pkg, rootDir string) InstallationReason { if err != nil { return Unknown } - reason := string(b) + reason := strings.TrimSpace(string(b)) if reason == "manual" { return Manual } else if reason == "dependency" { From a2da73e26bc8ba7f72af41b6e6ec2c9776143029 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 24 Oct 2024 12:44:43 +0300 Subject: [PATCH 03/88] Added a 'cleanup' subcommand that removes unused dependencies --- main.go | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/main.go b/main.go index fbeeedc..fde2acd 100644 --- a/main.go +++ b/main.go @@ -55,6 +55,7 @@ const ( update sync remove + cleanup file ) @@ -76,6 +77,8 @@ func getCommandType() commandType { return sync case "remove": return remove + case "cleanup": + return cleanup case "file": return file default: @@ -413,6 +416,76 @@ func resolveCommand() { if err != nil { log.Fatalf("Error: could not complete operation: %s\n", err) } + case cleanup: + if os.Getuid() != 0 { + log.Fatalf("Error: this subcommand needs to be run with superuser permissions") + } + + operation := &utils.BPMOperation{ + Actions: make([]utils.OperationAction, 0), + UnresolvedDepends: make([]string, 0), + RootDir: rootDir, + } + + // Get all installed packages + installedPackageNames, err := utils.GetInstalledPackages(operation.RootDir) + if err != nil { + log.Fatalf("Error: could not get installed packages: %s\n", err) + } + installedPackages := make([]*utils.PackageInfo, len(installedPackageNames)) + for i, value := range installedPackageNames { + bpmpkg := utils.GetPackage(value, operation.RootDir) + if bpmpkg == nil { + log.Fatalf("Error: could not find installed package (%s)", value) + } + installedPackages[i] = bpmpkg.PkgInfo + } + + // Get manually installed packages, resolve all their dependencies and add them to the keepPackages list + keepPackages := make([]string, 0) + for _, pkg := range slices.Clone(installedPackages) { + if utils.GetInstallationReason(pkg.Name, rootDir) != utils.Manual { + continue + } + keepPackages = append(keepPackages, pkg.Name) + resolved, _ := pkg.ResolveDependencies(&[]string{}, &[]string{}, false, true, false, verbose, rootDir) + for _, value := range resolved { + if !slices.Contains(keepPackages, value) && slices.Contains(installedPackageNames, value) { + keepPackages = append(keepPackages, value) + } + } + } + + // Get all installed packages that are not in the keepPackages slice and add them to the BPM operation + for _, pkg := range installedPackageNames { + if !slices.Contains(keepPackages, pkg) { + bpmpkg := utils.GetPackage(pkg, rootDir) + if bpmpkg == nil { + log.Fatalf("Error: could not find installed package (%s)", pkg) + } + operation.Actions = append(operation.Actions, &utils.RemovePackageAction{BpmPackage: bpmpkg}) + } + } + + // Show operation summary + operation.ShowOperationSummary() + + // 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" { + fmt.Println("Cancelling package removal...") + os.Exit(1) + } + } + + // Execute operation + err = operation.Execute(verbose, force) + if err != nil { + log.Fatalf("Error: could not complete operation: %s\n", err) + } case file: files := subcommandArgs if len(files) == 0 { @@ -507,6 +580,10 @@ func printHelp() { fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -y skips the confirmation prompt") + fmt.Println("-> bpm cleanup [-R, -v, -y] | remove all unused dependency packages") + fmt.Println(" -v Show additional information about what BPM is doing") + fmt.Println(" -R= lets you define the root path which will be used") + fmt.Println(" -y skips the confirmation prompt") fmt.Println("-> bpm file [-R] | shows what packages the following packages are managed by") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println("\033[1m----------------\033[0m") @@ -559,6 +636,12 @@ func resolveFlags() { removeFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing") removeFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts") removeFlagSet.Usage = printHelp + // Cleanup flags + cleanupFlagSet := flag.NewFlagSet("Cleanup flags", flag.ExitOnError) + cleanupFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root") + cleanupFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing") + cleanupFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts") + cleanupFlagSet.Usage = printHelp // File flags fileFlagSet := flag.NewFlagSet("Remove flags", flag.ExitOnError) fileFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root") From e30a32c56875ca30f323fb8403890d064a7a50ea Mon Sep 17 00:00:00 2001 From: EnumDev Date: Wed, 30 Oct 2024 16:35:52 +0200 Subject: [PATCH 04/88] Added an --unused flag for the remove subcommand and remove will no longer display errors if a package does not exist --- main.go | 15 +++++++++++++-- utils/operations.go | 43 ++++++++++++++++++++++++++++++++++++++++++ utils/package_utils.go | 20 ++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index fde2acd..e4c4827 100644 --- a/main.go +++ b/main.go @@ -36,6 +36,7 @@ var reinstall = false var reinstallAll = false var noOptional = false var nosync = true +var removeUnused = false func main() { utils.ReadConfig() @@ -392,11 +393,19 @@ func resolveCommand() { for _, pkg := range packages { bpmpkg := utils.GetPackage(pkg, rootDir) if bpmpkg == nil { - log.Fatalf("Error: package (%s) could not be found\n", pkg) + continue } operation.Actions = append(operation.Actions, &utils.RemovePackageAction{BpmPackage: bpmpkg}) } + // Skip needed packages if the --unused flag is on + if removeUnused { + err := operation.RemoveNeededPackages() + if err != nil { + log.Fatalf("Error: could not skip needed packages: %s\n", err) + } + } + // Show operation summary operation.ShowOperationSummary() @@ -576,10 +585,11 @@ func printHelp() { fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -y skips the confirmation prompt") - fmt.Println("-> bpm remove [-R, -v, -y] | removes the following packages") + fmt.Println("-> bpm remove [-R, -v, -y, --unused] | removes the following packages") fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -y skips the confirmation prompt") + fmt.Println(" -unused removes only packages that aren't required as dependencies by other packages") fmt.Println("-> bpm cleanup [-R, -v, -y] | remove all unused dependency packages") fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -R= lets you define the root path which will be used") @@ -635,6 +645,7 @@ func resolveFlags() { removeFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root") removeFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing") removeFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts") + removeFlagSet.BoolVar(&removeUnused, "unused", false, "Removes only packages that aren't required as dependencies by other packages") removeFlagSet.Usage = printHelp // Cleanup flags cleanupFlagSet := flag.NewFlagSet("Cleanup flags", flag.ExitOnError) diff --git a/utils/operations.go b/utils/operations.go index 2035fad..31e0c67 100644 --- a/utils/operations.go +++ b/utils/operations.go @@ -42,6 +42,22 @@ func (operation *BPMOperation) InsertActionAt(index int, action OperationAction) operation.Actions[index] = action } +func (operation *BPMOperation) RemoveAction(pkg, actionType string) { + operation.Actions = slices.DeleteFunc(operation.Actions, func(a OperationAction) bool { + if a.GetActionType() != actionType { + return false + } + if a.GetActionType() == "install" { + return a.(*InstallPackageAction).BpmPackage.PkgInfo.Name == pkg + } else if a.GetActionType() == "fetch" { + return a.(*FetchPackageAction).RepositoryEntry.Info.Name == pkg + } else if a.GetActionType() == "remove" { + return a.(*RemovePackageAction).BpmPackage.PkgInfo.Name == pkg + } + return false + }) +} + func (operation *BPMOperation) GetTotalDownloadSize() uint64 { var ret uint64 = 0 for _, action := range operation.Actions { @@ -122,6 +138,33 @@ func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, instal return nil } +func (operation *BPMOperation) RemoveNeededPackages() error { + removeActions := make(map[string]*RemovePackageAction) + for _, action := range slices.Clone(operation.Actions) { + if action.GetActionType() == "remove" { + removeActions[action.(*RemovePackageAction).BpmPackage.PkgInfo.Name] = action.(*RemovePackageAction) + } + } + + for pkg, action := range removeActions { + dependants, err := action.BpmPackage.PkgInfo.GetDependants(operation.RootDir) + if err != nil { + return errors.New("could not get dependant packages for package (" + pkg + ")") + } + dependants = slices.DeleteFunc(dependants, func(d string) bool { + if _, ok := removeActions[d]; ok { + return true + } + return false + }) + if len(dependants) != 0 { + operation.RemoveAction(pkg, action.GetActionType()) + } + } + + return nil +} + func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error) { conflicts := make(map[string][]string) installedPackages, err := GetInstalledPackages(operation.RootDir) diff --git a/utils/package_utils.go b/utils/package_utils.go index d4e23c9..3d2f00c 100644 --- a/utils/package_utils.go +++ b/utils/package_utils.go @@ -1293,6 +1293,26 @@ func (pkgInfo *PackageInfo) CheckDependencies(checkMake, checkOptional bool, roo return ret } +func (pkgInfo *PackageInfo) GetDependants(rootDir string) ([]string, error) { + ret := make([]string, 0) + + pkgs, err := GetInstalledPackages(rootDir) + if err != nil { + return nil, errors.New("could not get installed packages") + } + for _, pkg := range pkgs { + bpmpkg := GetPackage(pkg, rootDir) + if bpmpkg == nil { + return nil, errors.New("package not found: " + pkg) + } + if bpmpkg.PkgInfo.Name != pkgInfo.Name && slices.Contains(bpmpkg.PkgInfo.GetAllDependencies(false, true), pkgInfo.Name) { + ret = append(ret, pkg) + } + } + + return ret, nil +} + func (pkgInfo *PackageInfo) CheckConflicts(rootDir string) []string { var ret []string for _, conflict := range pkgInfo.Conflicts { From 6e2bb86ef068a86f30f95dacc6f55ba8e8f8816c Mon Sep 17 00:00:00 2001 From: EnumDev Date: Mon, 4 Nov 2024 14:05:38 +0200 Subject: [PATCH 05/88] Show dependant packages in the 'info' subcommand --- utils/package_utils.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/utils/package_utils.go b/utils/package_utils.go index 3d2f00c..06dc952 100644 --- a/utils/package_utils.go +++ b/utils/package_utils.go @@ -462,6 +462,10 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p appendArray("Dependencies", pkgInfo.Depends) appendArray("Make Dependencies", pkgInfo.MakeDepends) appendArray("Optional dependencies", pkgInfo.OptionalDepends) + dependants, err := pkgInfo.GetDependants(rootDir) + if err == nil { + appendArray("Dependant packages", dependants) + } appendArray("Conflicting packages", pkgInfo.Conflicts) appendArray("Provided packages", pkgInfo.Provides) From b625fe97ef70c665f6d84231fbf098756206a4f9 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Mon, 4 Nov 2024 19:38:54 +0200 Subject: [PATCH 06/88] Added --cleanup flag to the remove subcommand --- main.go | 50 ++++++++++-------------------------- utils/operations.go | 62 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 37 deletions(-) diff --git a/main.go b/main.go index e4c4827..28026d4 100644 --- a/main.go +++ b/main.go @@ -37,6 +37,7 @@ var reinstallAll = false var noOptional = false var nosync = true var removeUnused = false +var doCleanup = false func main() { utils.ReadConfig() @@ -406,6 +407,14 @@ func resolveCommand() { } } + // Do package cleanup + if doCleanup { + err := operation.Cleanup(verbose) + if err != nil { + log.Fatalf("Error: could not perform cleanup for operation: %s\n", err) + } + } + // Show operation summary operation.ShowOperationSummary() @@ -436,44 +445,10 @@ func resolveCommand() { RootDir: rootDir, } - // Get all installed packages - installedPackageNames, err := utils.GetInstalledPackages(operation.RootDir) + // Do package cleanup + err := operation.Cleanup(verbose) if err != nil { - log.Fatalf("Error: could not get installed packages: %s\n", err) - } - installedPackages := make([]*utils.PackageInfo, len(installedPackageNames)) - for i, value := range installedPackageNames { - bpmpkg := utils.GetPackage(value, operation.RootDir) - if bpmpkg == nil { - log.Fatalf("Error: could not find installed package (%s)", value) - } - installedPackages[i] = bpmpkg.PkgInfo - } - - // Get manually installed packages, resolve all their dependencies and add them to the keepPackages list - keepPackages := make([]string, 0) - for _, pkg := range slices.Clone(installedPackages) { - if utils.GetInstallationReason(pkg.Name, rootDir) != utils.Manual { - continue - } - keepPackages = append(keepPackages, pkg.Name) - resolved, _ := pkg.ResolveDependencies(&[]string{}, &[]string{}, false, true, false, verbose, rootDir) - for _, value := range resolved { - if !slices.Contains(keepPackages, value) && slices.Contains(installedPackageNames, value) { - keepPackages = append(keepPackages, value) - } - } - } - - // Get all installed packages that are not in the keepPackages slice and add them to the BPM operation - for _, pkg := range installedPackageNames { - if !slices.Contains(keepPackages, pkg) { - bpmpkg := utils.GetPackage(pkg, rootDir) - if bpmpkg == nil { - log.Fatalf("Error: could not find installed package (%s)", pkg) - } - operation.Actions = append(operation.Actions, &utils.RemovePackageAction{BpmPackage: bpmpkg}) - } + log.Fatalf("Error: could not perform cleanup for operation: %s\n", err) } // Show operation summary @@ -646,6 +621,7 @@ func resolveFlags() { removeFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing") removeFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts") removeFlagSet.BoolVar(&removeUnused, "unused", false, "Removes only packages that aren't required as dependencies by other packages") + removeFlagSet.BoolVar(&doCleanup, "cleanup", false, "Perform a dependency cleanup ") removeFlagSet.Usage = printHelp // Cleanup flags cleanupFlagSet := flag.NewFlagSet("Cleanup flags", flag.ExitOnError) diff --git a/utils/operations.go b/utils/operations.go index 31e0c67..7f73f00 100644 --- a/utils/operations.go +++ b/utils/operations.go @@ -165,6 +165,68 @@ func (operation *BPMOperation) RemoveNeededPackages() error { return nil } +func (operation *BPMOperation) Cleanup(verbose bool) error { + // Get all installed packages + installedPackageNames, err := GetInstalledPackages(operation.RootDir) + if err != nil { + log.Fatalf("Error: could not get installed packages: %s\n", err) + } + installedPackages := make([]*PackageInfo, len(installedPackageNames)) + for i, value := range installedPackageNames { + bpmpkg := GetPackage(value, operation.RootDir) + if bpmpkg == nil { + return errors.New("could not find installed package (" + value + ")") + } + installedPackages[i] = bpmpkg.PkgInfo + } + + // Get packages to remove + removeActions := make(map[string]*RemovePackageAction) + for _, action := range slices.Clone(operation.Actions) { + if action.GetActionType() == "remove" { + removeActions[action.(*RemovePackageAction).BpmPackage.PkgInfo.Name] = action.(*RemovePackageAction) + } + } + + // Get manually installed packages, resolve all their dependencies and add them to the keepPackages slice + keepPackages := make([]string, 0) + for _, pkg := range slices.Clone(installedPackages) { + if GetInstallationReason(pkg.Name, operation.RootDir) != Manual { + continue + } + + // Do not resolve dependencies or add package to keepPackages slice if package removal action exists for it + if _, ok := removeActions[pkg.Name]; ok { + continue + } + + keepPackages = append(keepPackages, pkg.Name) + resolved, _ := pkg.ResolveDependencies(&[]string{}, &[]string{}, false, true, false, verbose, operation.RootDir) + for _, value := range resolved { + if !slices.Contains(keepPackages, value) && slices.Contains(installedPackageNames, value) { + keepPackages = append(keepPackages, value) + } + } + } + + // Get all installed packages that are not in the keepPackages slice and add them to the BPM operation + for _, pkg := range installedPackageNames { + // Do not add package removal action if there already is one + if _, ok := removeActions[pkg]; ok { + continue + } + if !slices.Contains(keepPackages, pkg) { + bpmpkg := GetPackage(pkg, operation.RootDir) + if bpmpkg == nil { + return errors.New("Error: could not find installed package (" + pkg + ")") + } + operation.Actions = append(operation.Actions, &RemovePackageAction{BpmPackage: bpmpkg}) + } + } + + return nil +} + func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error) { conflicts := make(map[string][]string) installedPackages, err := GetInstalledPackages(operation.RootDir) From 2accc303908c20ad99edb6f50d6105d1bbd3dc89 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Tue, 5 Nov 2024 15:34:55 +0200 Subject: [PATCH 07/88] Added --installation-reason flag --- main.go | 34 +++++++++++++++++++++++++--------- utils/operations.go | 14 ++++++++++---- utils/package_utils.go | 7 ++----- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/main.go b/main.go index 28026d4..33fe539 100644 --- a/main.go +++ b/main.go @@ -35,6 +35,7 @@ var pkgListNames = false var reinstall = false var reinstallAll = false var noOptional = false +var installationReason = "" var nosync = true var removeUnused = false var doCleanup = false @@ -178,10 +179,21 @@ func resolveCommand() { return } + // Check if installationReason argument is valid + ir := utils.Unknown + if installationReason == "manual" { + ir = utils.Manual + } else if installationReason == "dependency" { + ir = utils.Dependency + } else if installationReason != "" { + log.Fatalf("Error: %s is not a valid installation reason", installationReason) + } + operation := utils.BPMOperation{ - Actions: make([]utils.OperationAction, 0), - UnresolvedDepends: make([]string, 0), - RootDir: rootDir, + Actions: make([]utils.OperationAction, 0), + UnresolvedDepends: make([]string, 0), + RootDir: rootDir, + ForceInstallationReason: ir, } // Search for packages @@ -296,9 +308,10 @@ func resolveCommand() { } operation := utils.BPMOperation{ - Actions: make([]utils.OperationAction, 0), - UnresolvedDepends: make([]string, 0), - RootDir: rootDir, + Actions: make([]utils.OperationAction, 0), + UnresolvedDepends: make([]string, 0), + RootDir: rootDir, + ForceInstallationReason: utils.Unknown, } // Search for packages @@ -537,7 +550,7 @@ func printHelp() { fmt.Println(" -c lists the amount of installed packages") fmt.Println(" -n lists only the names of installed packages") fmt.Println("-> bpm search | Searches for packages through declared repositories") - fmt.Println("-> bpm install [-R, -v, -y, -f, -o, -c, -b, -k, --reinstall, --reinstall-all, --no-optional] | installs the following files") + fmt.Println("-> bpm install [-R, -v, -y, -f, -o, -c, -b, -k, --reinstall, --reinstall-all, --no-optional, --installation-reason] | installs the following files") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -y skips the confirmation prompt") @@ -549,6 +562,7 @@ func printHelp() { fmt.Println(" --reinstall Reinstalls packages even if they do not have a newer version available") fmt.Println(" --reinstall-all Same as --reinstall but also reinstalls dependencies") fmt.Println(" --no-optional Prevents installation of optional dependencies") + fmt.Println(" --installation-reason= sets the installation reason for all newly installed packages") fmt.Println("-> bpm update [-R, -v, -y, -f, --reinstall, --no-sync] | updates all packages that are available in the repositories") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -v Show additional information about what BPM is doing") @@ -560,11 +574,12 @@ func printHelp() { fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -y skips the confirmation prompt") - fmt.Println("-> bpm remove [-R, -v, -y, --unused] | removes the following packages") + fmt.Println("-> bpm remove [-R, -v, -y, --unused, --cleanup] | removes the following packages") fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -y skips the confirmation prompt") fmt.Println(" -unused removes only packages that aren't required as dependencies by other packages") + fmt.Println(" -cleanup performs a dependency cleanup") fmt.Println("-> bpm cleanup [-R, -v, -y] | remove all unused dependency packages") fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -R= lets you define the root path which will be used") @@ -599,6 +614,7 @@ func resolveFlags() { installFlagSet.BoolVar(&reinstall, "reinstall", false, "Reinstalls packages even if they do not have a newer version available") installFlagSet.BoolVar(&reinstallAll, "reinstall-all", false, "Same as --reinstall but also reinstalls dependencies") installFlagSet.BoolVar(&noOptional, "no-optional", false, "Prevents installation of optional dependencies") + installFlagSet.StringVar(&installationReason, "installation-reason", "", "Set the installation reason for all newly installed packages") installFlagSet.Usage = printHelp // Update flags updateFlagSet := flag.NewFlagSet("Update flags", flag.ExitOnError) @@ -621,7 +637,7 @@ func resolveFlags() { removeFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing") removeFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts") removeFlagSet.BoolVar(&removeUnused, "unused", false, "Removes only packages that aren't required as dependencies by other packages") - removeFlagSet.BoolVar(&doCleanup, "cleanup", false, "Perform a dependency cleanup ") + removeFlagSet.BoolVar(&doCleanup, "cleanup", false, "Perform a dependency cleanup") removeFlagSet.Usage = printHelp // Cleanup flags cleanupFlagSet := flag.NewFlagSet("Cleanup flags", flag.ExitOnError) diff --git a/utils/operations.go b/utils/operations.go index 7f73f00..462dee5 100644 --- a/utils/operations.go +++ b/utils/operations.go @@ -10,9 +10,10 @@ import ( ) type BPMOperation struct { - Actions []OperationAction - UnresolvedDepends []string - RootDir string + Actions []OperationAction + UnresolvedDepends []string + RootDir string + ForceInstallationReason InstallationReason } func (operation *BPMOperation) ActionsContainPackage(pkg string) bool { @@ -396,7 +397,12 @@ func (operation *BPMOperation) Execute(verbose, force bool) error { return errors.New(fmt.Sprintf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err)) } fmt.Printf("Package (%s) was successfully installed\n", bpmpkg.PkgInfo.Name) - if value.IsDependency { + if operation.ForceInstallationReason != Unknown { + err := SetInstallationReason(bpmpkg.PkgInfo.Name, operation.ForceInstallationReason, operation.RootDir) + if err != nil { + return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err)) + } + } else if value.IsDependency && !IsPackageInstalled(bpmpkg.PkgInfo.Name, operation.RootDir) { err := SetInstallationReason(bpmpkg.PkgInfo.Name, Dependency, operation.RootDir) if err != nil { return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err)) diff --git a/utils/package_utils.go b/utils/package_utils.go index 06dc952..17fba2a 100644 --- a/utils/package_utils.go +++ b/utils/package_utils.go @@ -1185,11 +1185,8 @@ func InstallPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc, return err } pkgDir := path.Join(installedDir, bpmpkg.PkgInfo.Name) - err = os.RemoveAll(pkgDir) - if err != nil { - return err - } - err = os.Mkdir(pkgDir, 0755) + + err = os.MkdirAll(pkgDir, 0755) if err != nil { return err } From e285fd792e68028a9d0ba01fab68af6beeae2db1 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Tue, 5 Nov 2024 22:11:37 +0200 Subject: [PATCH 08/88] Fixed bug where dependencies would be installed with 'manual' as installation reason --- utils/operations.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/utils/operations.go b/utils/operations.go index 462dee5..754a17a 100644 --- a/utils/operations.go +++ b/utils/operations.go @@ -387,6 +387,7 @@ func (operation *BPMOperation) Execute(verbose, force bool) error { } else if action.GetActionType() == "install" { value := action.(*InstallPackageAction) bpmpkg := value.BpmPackage + isReinstall := IsPackageInstalled(bpmpkg.PkgInfo.Name, operation.RootDir) var err error if value.IsDependency { err = InstallPackage(value.File, operation.RootDir, verbose, true, false, false, false) @@ -396,18 +397,18 @@ func (operation *BPMOperation) Execute(verbose, force bool) error { if err != nil { return errors.New(fmt.Sprintf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err)) } - fmt.Printf("Package (%s) was successfully installed\n", bpmpkg.PkgInfo.Name) - if operation.ForceInstallationReason != Unknown { + if operation.ForceInstallationReason != Unknown && !value.IsDependency { err := SetInstallationReason(bpmpkg.PkgInfo.Name, operation.ForceInstallationReason, operation.RootDir) if err != nil { return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err)) } - } else if value.IsDependency && !IsPackageInstalled(bpmpkg.PkgInfo.Name, operation.RootDir) { + } else if value.IsDependency && !isReinstall { err := SetInstallationReason(bpmpkg.PkgInfo.Name, Dependency, operation.RootDir) if err != nil { return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err)) } } + fmt.Printf("Package (%s) was successfully installed\n", bpmpkg.PkgInfo.Name) } } fmt.Println("Operation complete!") From 852e615806121c9416d9c9b3bdad6bd35aa492f5 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sun, 17 Nov 2024 17:32:49 +0200 Subject: [PATCH 09/88] Changed info subcommand format and added support for checking .bpm file information --- main.go | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/main.go b/main.go index 33fe539..af1d339 100644 --- a/main.go +++ b/main.go @@ -102,15 +102,31 @@ func resolveCommand() { } for n, pkg := range packages { var info *utils.PackageInfo - info = utils.GetPackageInfo(pkg, rootDir) + isFile := false + if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() { + bpmpkg, err := utils.ReadPackage(pkg) + if err != nil { + log.Fatalf("Error: could not read package: %s\n", err) + } + info = bpmpkg.PkgInfo + isFile = true + } else { + info = utils.GetPackageInfo(pkg, rootDir) + } if info == nil { log.Fatalf("Error: package (%s) is not installed\n", pkg) } - fmt.Println("----------------") - fmt.Println(utils.CreateReadableInfo(true, true, true, info, rootDir)) - if n == len(packages)-1 { - fmt.Println("----------------") + if n != 0 { + fmt.Println() } + if isFile { + abs, err := filepath.Abs(pkg) + if err != nil { + log.Fatalf("Error: could not get absolute path of file (%s)\n", abs) + } + fmt.Println("File: " + abs) + } + fmt.Println(utils.CreateReadableInfo(true, true, true, info, rootDir)) } case list: packages, err := utils.GetInstalledPackages(rootDir) @@ -135,10 +151,10 @@ func resolveCommand() { fmt.Printf("Package (%s) could not be found\n", pkg) continue } - fmt.Println("----------------\n" + utils.CreateReadableInfo(true, true, true, info, rootDir)) - if n == len(packages)-1 { - fmt.Println("----------------") + if n != 0 { + fmt.Println() } + fmt.Println(utils.CreateReadableInfo(true, true, true, info, rootDir)) } } case search: @@ -146,7 +162,6 @@ func resolveCommand() { if len(searchTerms) == 0 { log.Fatalf("Error: no search terms given") } - for _, term := range searchTerms { nameResults := make([]*utils.PackageInfo, 0) descResults := make([]*utils.PackageInfo, 0) From c3981ad95cc03cf25d786c6afc4eb7e0870d0e64 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sun, 17 Nov 2024 18:02:54 +0200 Subject: [PATCH 10/88] Added repository entry information flag for the info subcommand and slightly altered the search subcommand format --- main.go | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index af1d339..8cab72c 100644 --- a/main.go +++ b/main.go @@ -39,6 +39,7 @@ var installationReason = "" var nosync = true var removeUnused = false var doCleanup = false +var showRepoInfo = false func main() { utils.ReadConfig() @@ -103,7 +104,13 @@ func resolveCommand() { for n, pkg := range packages { var info *utils.PackageInfo isFile := false - if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() { + if showRepoInfo { + entry, _, err := utils.GetRepositoryEntry(pkg) + if err != nil { + log.Fatalf("Error: could not find package (%s) in any repository\n", pkg) + } + info = entry.Info + } else if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() { bpmpkg, err := utils.ReadPackage(pkg) if err != nil { log.Fatalf("Error: could not read package: %s\n", err) @@ -162,7 +169,7 @@ func resolveCommand() { if len(searchTerms) == 0 { log.Fatalf("Error: no search terms given") } - for _, term := range searchTerms { + for i, term := range searchTerms { nameResults := make([]*utils.PackageInfo, 0) descResults := make([]*utils.PackageInfo, 0) for _, repo := range utils.BPMConfig.Repositories { @@ -178,10 +185,12 @@ func resolveCommand() { if len(results) == 0 { log.Fatalf("Error: no results for term (%s) were found\n", term) } + if i > 0 { + fmt.Println() + } fmt.Printf("Results for term (%s)\n", term) - for i, result := range results { - fmt.Println("----------------") - fmt.Printf("%d) %s: %s (%s)\n", i+1, result.Name, result.Description, result.GetFullVersion()) + for j, result := range results { + fmt.Printf("%d) %s: %s (%s)\n", j+1, result.Name, result.Description, result.GetFullVersion()) } } case install: @@ -558,8 +567,9 @@ func printHelp() { fmt.Println("-> flags will be read if passed right after the subcommand otherwise they will be read as subcommand arguments") fmt.Println("\033[1m---- Command List ----\033[0m") fmt.Println("-> bpm version | shows information on the installed version of bpm") - fmt.Println("-> bpm info [-R] | shows information on an installed package") + fmt.Println("-> bpm info [-R, --repos] | shows information on an installed package") fmt.Println(" -R= lets you define the root path which will be used") + fmt.Println(" --repos show information on package in repository") fmt.Println("-> bpm list [-R, -c, -n] | lists all installed packages") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -c lists the amount of installed packages") @@ -614,6 +624,7 @@ func resolveFlags() { // Info flags infoFlagSet := flag.NewFlagSet("Info flags", flag.ExitOnError) infoFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root") + infoFlagSet.BoolVar(&showRepoInfo, "repos", false, "Show information on package in repository") infoFlagSet.Usage = printHelp // Install flags installFlagSet := flag.NewFlagSet("Install flags", flag.ExitOnError) From e2e2629dc9598b194ae16643a65537e0b77c54ae Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 13 Mar 2025 15:34:10 +0200 Subject: [PATCH 11/88] Make bash the default shell in Makefile --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index f9a3fd7..ab65f36 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +SHELL = /bin/bash + ifeq ($(PREFIX),) PREFIX := /usr/local endif From 4f9d2cdecddd14330335464cc13202f2030a9bef Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 13 Mar 2025 15:34:43 +0200 Subject: [PATCH 12/88] Add ability to ignore packages --- main.go | 3 +++ utils/config.go | 1 + 2 files changed, 4 insertions(+) diff --git a/main.go b/main.go index 8cab72c..709a344 100644 --- a/main.go +++ b/main.go @@ -340,6 +340,9 @@ func resolveCommand() { // Search for packages for _, pkg := range pkgs { + if slices.Contains(utils.BPMConfig.IgnorePackages, pkg) { + continue + } entry, _, err := utils.GetRepositoryEntry(pkg) if err != nil { continue diff --git a/utils/config.go b/utils/config.go index 97edef6..037373b 100644 --- a/utils/config.go +++ b/utils/config.go @@ -11,6 +11,7 @@ type BPMConfigStruct struct { SilentCompilation bool `yaml:"silent_compilation"` BinaryOutputDir string `yaml:"binary_output_dir"` CompilationDir string `yaml:"compilation_dir"` + IgnorePackages []string `yaml:"ignore_packages"` Repositories []*Repository `yaml:"repositories"` } From e94b2a88166bc7a24176cd85abdb711b0224c07b Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sat, 15 Mar 2025 11:20:33 +0200 Subject: [PATCH 13/88] Add replaces field to package information --- main.go | 14 ++++++++++++-- utils/operations.go | 33 ++++++++++++++++++++++++++++++--- utils/package_utils.go | 4 +++- utils/repo_utils.go | 17 +++++++++++++++++ 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index 709a344..a43e2c8 100644 --- a/main.go +++ b/main.go @@ -263,6 +263,9 @@ func resolveCommand() { } } + // Replace obsolete packages + operation.ReplaceObsoletePackages() + // Check for conflicts conflicts, err := operation.CheckForConflicts() if err != nil { @@ -343,10 +346,14 @@ func resolveCommand() { if slices.Contains(utils.BPMConfig.IgnorePackages, pkg) { continue } - entry, _, err := utils.GetRepositoryEntry(pkg) - if err != nil { + var entry *utils.RepositoryEntry + // Check if installed package can be replaced and install that instead + if e := utils.FindReplacement(pkg); e != nil { + entry = e + } else if entry, _, err = utils.GetRepositoryEntry(pkg); err != nil { continue } + installedInfo := utils.GetPackageInfo(pkg, rootDir) if installedInfo == nil { log.Fatalf("Error: could not get package info for (%s)\n", pkg) @@ -374,6 +381,9 @@ func resolveCommand() { } } + // Replace obsolete packages + operation.ReplaceObsoletePackages() + // Show operation summary operation.ShowOperationSummary() diff --git a/utils/operations.go b/utils/operations.go index 754a17a..fe5bef5 100644 --- a/utils/operations.go +++ b/utils/operations.go @@ -228,6 +228,30 @@ func (operation *BPMOperation) Cleanup(verbose bool) error { return nil } +func (operation *BPMOperation) ReplaceObsoletePackages() { + 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.RepositoryEntry.Info + } else { + continue + } + + for _, r := range pkgInfo.Replaces { + if bpmpkg := GetPackage(r, operation.RootDir); bpmpkg != nil && !operation.ActionsContainPackage(bpmpkg.PkgInfo.Name) { + operation.InsertActionAt(0, &RemovePackageAction{ + BpmPackage: bpmpkg, + }) + } + } + } +} + func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error) { conflicts := make(map[string][]string) installedPackages, err := GetInstalledPackages(operation.RootDir) @@ -256,9 +280,12 @@ func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error) } else if value.GetActionType() == "remove" { action := value.(*RemovePackageAction) pkgInfo := action.BpmPackage.PkgInfo - slices.DeleteFunc(allPackages, func(info *PackageInfo) bool { - return info.Name == pkgInfo.Name - }) + for i := len(allPackages) - 1; i >= 0; i-- { + info := allPackages[i] + if info.Name == pkgInfo.Name { + allPackages = append(allPackages[:i], allPackages[i+1:]...) + } + } } } diff --git a/utils/package_utils.go b/utils/package_utils.go index 17fba2a..a0d2aeb 100644 --- a/utils/package_utils.go +++ b/utils/package_utils.go @@ -39,6 +39,7 @@ type PackageInfo struct { MakeDepends []string `yaml:"make_depends,omitempty"` OptionalDepends []string `yaml:"optional_depends,omitempty"` Conflicts []string `yaml:"conflicts,omitempty"` + Replaces []string `yaml:"replaces,omitempty"` Provides []string `yaml:"provides,omitempty"` } @@ -406,6 +407,7 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) { MakeDepends: make([]string, 0), OptionalDepends: make([]string, 0), Conflicts: make([]string, 0), + Replaces: make([]string, 0), Provides: make([]string, 0), } err := yaml.Unmarshal([]byte(contents), &pkgInfo) @@ -468,7 +470,7 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p } appendArray("Conflicting packages", pkgInfo.Conflicts) appendArray("Provided packages", pkgInfo.Provides) - + appendArray("Replaces packages", pkgInfo.Replaces) } ret = append(ret, "Installation Reason: "+string(GetInstallationReason(pkgInfo.Name, rootDir))) return strings.Join(ret, "\n") diff --git a/utils/repo_utils.go b/utils/repo_utils.go index 28fdaeb..4ea31ac 100644 --- a/utils/repo_utils.go +++ b/utils/repo_utils.go @@ -167,6 +167,23 @@ func GetRepositoryEntry(str string) (*RepositoryEntry, *Repository, error) { } } +func FindReplacement(pkg string) *RepositoryEntry { + for _, repo := range BPMConfig.Repositories { + for _, entry := range repo.Entries { + if entry.IsVirtualPackage { + continue + } + for _, replaced := range entry.Info.Replaces { + if replaced == pkg { + return entry + } + } + } + } + + return nil +} + func (repo *Repository) FetchPackage(pkg string) (string, error) { if !repo.ContainsPackage(pkg) { return "", errors.New("could not fetch package '" + pkg + "'") From 87c492a30c58df7393a675fdc613f6de5259f01c Mon Sep 17 00:00:00 2001 From: EnumDev Date: Fri, 4 Apr 2025 21:40:21 +0300 Subject: [PATCH 14/88] Improve virtual package handling --- main.go | 28 ++++++++++++++---- utils/config.go | 1 + utils/package_utils.go | 34 ++++++++++++++++++---- utils/repo_utils.go | 64 ++++++++++++++++++------------------------ 4 files changed, 80 insertions(+), 47 deletions(-) diff --git a/main.go b/main.go index a43e2c8..2c115ba 100644 --- a/main.go +++ b/main.go @@ -105,9 +105,13 @@ func resolveCommand() { var info *utils.PackageInfo isFile := false if showRepoInfo { - entry, _, err := utils.GetRepositoryEntry(pkg) + var err error + var entry *utils.RepositoryEntry + entry, _, err = utils.GetRepositoryEntry(pkg) if err != nil { - log.Fatalf("Error: could not find package (%s) in any repository\n", pkg) + if entry = utils.ResolveVirtualPackage(pkg); entry == nil { + log.Fatalf("Error: could not find package (%s) in any repository\n", pkg) + } } info = entry.Info } else if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() { @@ -118,7 +122,11 @@ func resolveCommand() { info = bpmpkg.PkgInfo isFile = true } else { - info = utils.GetPackageInfo(pkg, rootDir) + if isVirtual, p := utils.IsVirtualPackage(pkg, rootDir); isVirtual { + info = utils.GetPackageInfo(p, rootDir) + } else { + info = utils.GetPackageInfo(pkg, rootDir) + } } if info == nil { log.Fatalf("Error: package (%s) is not installed\n", pkg) @@ -236,8 +244,18 @@ func resolveCommand() { BpmPackage: bpmpkg, }) } else { - entry, _, err := utils.GetRepositoryEntry(pkg) - if err != nil { + var entry *utils.RepositoryEntry + + if e, _, err := utils.GetRepositoryEntry(pkg); err == nil { + entry = e + } else if isVirtual, p := utils.IsVirtualPackage(pkg, rootDir); isVirtual { + entry, _, err = utils.GetRepositoryEntry(p) + if err != nil { + log.Fatalf("Error: could not find package (%s) in any repository\n", p) + } + } else if e := utils.ResolveVirtualPackage(pkg); e != nil { + entry = e + } else { log.Fatalf("Error: could not find package (%s) in any repository\n", pkg) } if !reinstall && utils.IsPackageInstalled(entry.Info.Name, rootDir) && utils.GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() { diff --git a/utils/config.go b/utils/config.go index 037373b..e57b03f 100644 --- a/utils/config.go +++ b/utils/config.go @@ -42,6 +42,7 @@ func ReadConfig() { } for _, repo := range BPMConfig.Repositories { repo.Entries = make(map[string]*RepositoryEntry) + repo.VirtualPackages = make(map[string][]string) err := repo.ReadLocalDatabase() if err != nil { log.Fatal(err) diff --git a/utils/package_utils.go b/utils/package_utils.go index a0d2aeb..443f981 100644 --- a/utils/package_utils.go +++ b/utils/package_utils.go @@ -1340,15 +1340,19 @@ func (pkgInfo *PackageInfo) ResolveDependencies(resolved, unresolved *[]string, *resolved = append(*resolved, depend) } continue - } else if ignoreInstalled && IsPackageInstalled(depend, rootDir) { + } else if ignoreInstalled && IsPackageProvided(depend, rootDir) { continue } - entry, _, err := GetRepositoryEntry(depend) + var err error + var entry *RepositoryEntry + entry, _, err = GetRepositoryEntry(depend) if err != nil { - if !slices.Contains(*unresolved, depend) { - *unresolved = append(*unresolved, depend) + if entry = ResolveVirtualPackage(depend); entry == nil { + if !slices.Contains(*unresolved, depend) { + *unresolved = append(*unresolved, depend) + } + continue } - continue } entry.Info.ResolveDependencies(resolved, unresolved, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) } @@ -1369,6 +1373,26 @@ func IsPackageInstalled(pkg, rootDir string) bool { return true } +func IsVirtualPackage(pkg, rootDir string) (bool, string) { + pkgs, err := GetInstalledPackages(rootDir) + if err != nil { + return false, "" + } + for _, p := range pkgs { + if p == pkg { + return false, "" + } + i := GetPackageInfo(p, rootDir) + if i == nil { + continue + } + if slices.Contains(i.Provides, pkg) { + return true, p + } + } + return false, "" +} + func IsPackageProvided(pkg, rootDir string) bool { pkgs, err := GetInstalledPackages(rootDir) if err != nil { diff --git a/utils/repo_utils.go b/utils/repo_utils.go index 4ea31ac..0cf8c1f 100644 --- a/utils/repo_utils.go +++ b/utils/repo_utils.go @@ -8,24 +8,23 @@ import ( "net/url" "os" "path" - "sort" "strings" ) type Repository struct { - Name string `yaml:"name"` - Source string `yaml:"source"` - Disabled *bool `yaml:"disabled"` - Entries map[string]*RepositoryEntry + Name string `yaml:"name"` + Source string `yaml:"source"` + Disabled *bool `yaml:"disabled"` + Entries map[string]*RepositoryEntry + VirtualPackages map[string][]string } type RepositoryEntry struct { - Info *PackageInfo `yaml:"info"` - Download string `yaml:"download"` - DownloadSize uint64 `yaml:"download_size"` - InstalledSize uint64 `yaml:"installed_size"` - IsVirtualPackage bool `yaml:"-"` - Repository *Repository + Info *PackageInfo `yaml:"info"` + Download string `yaml:"download"` + DownloadSize uint64 `yaml:"download_size"` + InstalledSize uint64 `yaml:"installed_size"` + Repository *Repository } func (repo *Repository) ContainsPackage(pkg string) bool { @@ -44,8 +43,6 @@ func (repo *Repository) ReadLocalDatabase() error { return err } - virtualPackages := make(map[string][]string) - data := string(bytes) for _, b := range strings.Split(data, "---") { entry := RepositoryEntry{ @@ -65,11 +62,10 @@ func (repo *Repository) ReadLocalDatabase() error { Conflicts: make([]string, 0), Provides: make([]string, 0), }, - Download: "", - DownloadSize: 0, - InstalledSize: 0, - IsVirtualPackage: false, - Repository: repo, + Download: "", + DownloadSize: 0, + InstalledSize: 0, + Repository: repo, } err := yaml.Unmarshal([]byte(b), &entry) if err != nil { @@ -77,26 +73,11 @@ func (repo *Repository) ReadLocalDatabase() error { } for _, p := range entry.Info.Provides { - virtualPackages[p] = append(virtualPackages[p], entry.Info.Name) + repo.VirtualPackages[p] = append(repo.VirtualPackages[p], entry.Info.Name) } repo.Entries[entry.Info.Name] = &entry } - for key, value := range virtualPackages { - if _, ok := repo.Entries[key]; ok { - continue - } - sort.Strings(value) - entry := RepositoryEntry{ - Info: repo.Entries[value[0]].Info, - Download: repo.Entries[value[0]].Download, - DownloadSize: repo.Entries[value[0]].DownloadSize, - InstalledSize: repo.Entries[value[0]].InstalledSize, - IsVirtualPackage: true, - Repository: repo, - } - repo.Entries[key] = &entry - } return nil } @@ -170,9 +151,6 @@ func GetRepositoryEntry(str string) (*RepositoryEntry, *Repository, error) { func FindReplacement(pkg string) *RepositoryEntry { for _, repo := range BPMConfig.Repositories { for _, entry := range repo.Entries { - if entry.IsVirtualPackage { - continue - } for _, replaced := range entry.Info.Replaces { if replaced == pkg { return entry @@ -184,6 +162,18 @@ func FindReplacement(pkg string) *RepositoryEntry { return nil } +func ResolveVirtualPackage(vpkg string) *RepositoryEntry { + for _, repo := range BPMConfig.Repositories { + if v, ok := repo.VirtualPackages[vpkg]; ok { + for _, pkg := range v { + return repo.Entries[pkg] + } + } + } + + return nil +} + func (repo *Repository) FetchPackage(pkg string) (string, error) { if !repo.ContainsPackage(pkg) { return "", errors.New("could not fetch package '" + pkg + "'") From 5f8e4f00ea238deb7d013de687929d6c2bb91052 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sun, 27 Oct 2024 09:47:56 +0200 Subject: [PATCH 15/88] Added basic hook functionality --- main.go | 40 ++++++++++-- utils/hooks.go | 149 ++++++++++++++++++++++++++++++++++++++++++++ utils/operations.go | 55 +++++++++++++++- 3 files changed, 238 insertions(+), 6 deletions(-) create mode 100644 utils/hooks.go diff --git a/main.go b/main.go index 2c115ba..7db4691 100644 --- a/main.go +++ b/main.go @@ -224,6 +224,7 @@ func resolveCommand() { operation := utils.BPMOperation{ Actions: make([]utils.OperationAction, 0), UnresolvedDepends: make([]string, 0), + Changes: make(map[string]string), RootDir: rootDir, ForceInstallationReason: ir, } @@ -238,7 +239,7 @@ func resolveCommand() { if !reinstall && utils.IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) && utils.GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() == bpmpkg.PkgInfo.GetFullVersion() { continue } - operation.Actions = append(operation.Actions, &utils.InstallPackageAction{ + operation.AppendAction(&utils.InstallPackageAction{ File: pkg, IsDependency: false, BpmPackage: bpmpkg, @@ -261,7 +262,7 @@ func resolveCommand() { if !reinstall && utils.IsPackageInstalled(entry.Info.Name, rootDir) && utils.GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() { continue } - operation.Actions = append(operation.Actions, &utils.FetchPackageAction{ + operation.AppendAction(&utils.FetchPackageAction{ IsDependency: false, RepositoryEntry: entry, }) @@ -327,6 +328,13 @@ func resolveCommand() { if err != nil { log.Fatalf("Error: could not complete operation: %s\n", err) } + + // Executing hooks + fmt.Println("Running hooks...") + err = operation.RunHooks(verbose) + if err != nil { + log.Fatalf("Error: could not run hooks: %s\n", err) + } case update: if os.Getuid() != 0 { log.Fatalf("Error: this subcommand needs to be run with superuser permissions") @@ -355,6 +363,7 @@ func resolveCommand() { operation := utils.BPMOperation{ Actions: make([]utils.OperationAction, 0), UnresolvedDepends: make([]string, 0), + Changes: make(map[string]string), RootDir: rootDir, ForceInstallationReason: utils.Unknown, } @@ -378,7 +387,7 @@ func resolveCommand() { } else { comparison := utils.ComparePackageVersions(*entry.Info, *installedInfo) if comparison > 0 || reinstall { - operation.Actions = append(operation.Actions, &utils.FetchPackageAction{ + operation.AppendAction(&utils.FetchPackageAction{ IsDependency: false, RepositoryEntry: entry, }) @@ -421,6 +430,13 @@ func resolveCommand() { if err != nil { log.Fatalf("Error: could not complete operation: %s\n", err) } + + // Executing hooks + fmt.Println("Running hooks...") + err = operation.RunHooks(verbose) + if err != nil { + log.Fatalf("Error: could not run hooks: %s\n", err) + } case sync: if os.Getuid() != 0 { log.Fatalf("Error: this subcommand needs to be run with superuser permissions") @@ -455,6 +471,7 @@ func resolveCommand() { operation := &utils.BPMOperation{ Actions: make([]utils.OperationAction, 0), UnresolvedDepends: make([]string, 0), + Changes: make(map[string]string), RootDir: rootDir, } @@ -464,7 +481,7 @@ func resolveCommand() { if bpmpkg == nil { continue } - operation.Actions = append(operation.Actions, &utils.RemovePackageAction{BpmPackage: bpmpkg}) + operation.AppendAction(&utils.RemovePackageAction{BpmPackage: bpmpkg}) } // Skip needed packages if the --unused flag is on @@ -502,6 +519,13 @@ func resolveCommand() { if err != nil { log.Fatalf("Error: could not complete operation: %s\n", err) } + + // Executing hooks + fmt.Println("Running hooks...") + err = operation.RunHooks(verbose) + if err != nil { + log.Fatalf("Error: could not run hooks: %s\n", err) + } case cleanup: if os.Getuid() != 0 { log.Fatalf("Error: this subcommand needs to be run with superuser permissions") @@ -510,6 +534,7 @@ func resolveCommand() { operation := &utils.BPMOperation{ Actions: make([]utils.OperationAction, 0), UnresolvedDepends: make([]string, 0), + Changes: make(map[string]string), RootDir: rootDir, } @@ -538,6 +563,13 @@ func resolveCommand() { if err != nil { log.Fatalf("Error: could not complete operation: %s\n", err) } + + // Executing hooks + fmt.Println("Running hooks...") + err = operation.RunHooks(verbose) + if err != nil { + log.Fatalf("Error: could not run hooks: %s\n", err) + } case file: files := subcommandArgs if len(files) == 0 { diff --git a/utils/hooks.go b/utils/hooks.go new file mode 100644 index 0000000..4424c70 --- /dev/null +++ b/utils/hooks.go @@ -0,0 +1,149 @@ +package utils + +import ( + "errors" + "fmt" + "gopkg.in/yaml.v3" + "os" + "os/exec" + "slices" + "strings" + "syscall" +) + +type BPMHook struct { + SourcePath string + SourceContent string + TriggerOperations []string `yaml:"trigger_operations"` + TargetType string `yaml:"target_type"` + Targets []string `yaml:"targets"` + Depends []string `yaml:"depends"` + Run string `yaml:"run"` +} + +// CreateHook returns a BPMHook instance based on the content of the given string +func CreateHook(sourcePath string) (*BPMHook, error) { + // Read hook from source path + bytes, err := os.ReadFile(sourcePath) + if err != nil { + return nil, err + } + + // Create base hook structure + hook := &BPMHook{ + SourcePath: sourcePath, + SourceContent: string(bytes), + TriggerOperations: nil, + TargetType: "", + Targets: nil, + Depends: nil, + Run: "", + } + + // Unmarshal yaml string + err = yaml.Unmarshal(bytes, hook) + if err != nil { + return nil, err + } + + // Ensure hook is valid + if err := hook.IsValid(); err != nil { + return nil, err + } + + return hook, nil +} + +// IsValid ensures hook is valid +func (hook *BPMHook) IsValid() error { + ValidOperations := []string{"install", "upgrade", "remove"} + + // Return error if any trigger operation is not valid or none are given + if len(hook.TriggerOperations) == 0 { + return errors.New("no trigger operations specified") + } + for _, operation := range hook.TriggerOperations { + if !slices.Contains(ValidOperations, operation) { + return errors.New("trigger operation '" + operation + "' is not valid") + } + } + + if hook.TargetType != "package" && hook.TargetType != "path" { + return errors.New("target type '" + hook.TargetType + "' is not valid") + } + + if len(hook.Run) == 0 { + return errors.New("command to run is empty") + } + + // Return nil as hook is valid + return nil +} + +// Execute hook if all conditions are met +func (hook *BPMHook) Execute(packageChanges map[string]string, verbose bool, rootDir string) error { + // Check if package dependencies are met + installedPackages, err := GetInstalledPackages(rootDir) + if err != nil { + return err + } + + for _, depend := range hook.Depends { + if !slices.Contains(installedPackages, depend) { + return nil + } + } + + // Get modified files slice + modifiedFiles := make([]*PackageFileEntry, 0) + for pkg, _ := range packageChanges { + modifiedFiles = append(modifiedFiles, GetPackageFiles(pkg, rootDir)...) + } + + // Check if any targets are met + targetMet := false + for _, t1 := range hook.Targets { + if targetMet { + break + } + if hook.TargetType == "package" { + for t2, operation := range packageChanges { + if t1 == t2 && slices.Contains(hook.TriggerOperations, operation) { + targetMet = true + break + } + } + } else { + for _, t2 := range modifiedFiles { + if strings.HasPrefix(t2.Path, t1) { + targetMet = true + break + } + } + } + } + if !targetMet { + return nil + } + + // Execute the command + splitCommand := strings.Split(hook.Run, " ") + cmd := exec.Command(splitCommand[0], splitCommand[1:]...) + // Setup subprocess environment + cmd.Dir = "/" + // Run hook in chroot if using the -R flag + if rootDir != "/" { + cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: rootDir} + } + + if verbose { + fmt.Printf("Running hook (%s) with run command: %s\n", hook.SourcePath, strings.Join(splitCommand, " ")) + } + + err = cmd.Run() + if err != nil { + return err + } + + return nil +} diff --git a/utils/operations.go b/utils/operations.go index fe5bef5..ab3adeb 100644 --- a/utils/operations.go +++ b/utils/operations.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "os" + "path" "slices" "strings" ) @@ -12,6 +13,7 @@ import ( type BPMOperation struct { Actions []OperationAction UnresolvedDepends []string + Changes map[string]string RootDir string ForceInstallationReason InstallationReason } @@ -35,12 +37,35 @@ func (operation *BPMOperation) ActionsContainPackage(pkg string) bool { return false } +func (operation *BPMOperation) AppendAction(action OperationAction) { + operation.InsertActionAt(len(operation.Actions), action) +} + func (operation *BPMOperation) InsertActionAt(index int, action OperationAction) { if len(operation.Actions) == index { // nil or empty slice or after last element operation.Actions = append(operation.Actions, action) + } else { + operation.Actions = append(operation.Actions[:index+1], operation.Actions[index:]...) // index < len(a) + operation.Actions[index] = action + } + + if action.GetActionType() == "install" { + pkgInfo := action.(*InstallPackageAction).BpmPackage.PkgInfo + if !IsPackageInstalled(pkgInfo.Name, operation.RootDir) { + operation.Changes[pkgInfo.Name] = "install" + } else { + operation.Changes[pkgInfo.Name] = "upgrade" + } + } else if action.GetActionType() == "fetch" { + pkgInfo := action.(*FetchPackageAction).RepositoryEntry.Info + if !IsPackageInstalled(pkgInfo.Name, operation.RootDir) { + operation.Changes[pkgInfo.Name] = "install" + } else { + operation.Changes[pkgInfo.Name] = "upgrade" + } + } else if action.GetActionType() == "remove" { + operation.Changes[action.(*RemovePackageAction).BpmPackage.PkgInfo.Name] = "remove" } - operation.Actions = append(operation.Actions[:index+1], operation.Actions[index:]...) // index < len(a) - operation.Actions[index] = action } func (operation *BPMOperation) RemoveAction(pkg, actionType string) { @@ -356,6 +381,32 @@ func (operation *BPMOperation) ShowOperationSummary() { } } +func (operation *BPMOperation) RunHooks(verbose bool) error { + // Get directory entries in hooks directory + dirEntries, err := os.ReadDir(path.Join(operation.RootDir, "var/lib/bpm/hooks")) + if err != nil { + return err + } + + // Find all hooks, validate and execute them + for _, entry := range dirEntries { + if entry.Type().IsRegular() && strings.HasSuffix(entry.Name(), ".bpmhook") { + hook, err := CreateHook(path.Join(operation.RootDir, "var/lib/bpm/hooks", entry.Name())) + if err != nil { + log.Printf("Error while reading hook (%s): %s", entry.Name(), err) + } + + err = hook.Execute(operation.Changes, verbose, operation.RootDir) + if err != nil { + log.Printf("Warning: could not execute hook (%s): %s\n", entry.Name(), err) + continue + } + } + } + + return nil +} + func (operation *BPMOperation) Execute(verbose, force bool) error { // Fetch packages from repositories if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool { From 71044417272911dd2d226c17a64f299de9bb37d1 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Mon, 7 Apr 2025 14:58:56 +0300 Subject: [PATCH 16/88] Allow globstar in bpmhook path targets --- utils/hooks.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/utils/hooks.go b/utils/hooks.go index 4424c70..8ee9dc7 100644 --- a/utils/hooks.go +++ b/utils/hooks.go @@ -6,6 +6,8 @@ import ( "gopkg.in/yaml.v3" "os" "os/exec" + "path" + "path/filepath" "slices" "strings" "syscall" @@ -102,20 +104,24 @@ func (hook *BPMHook) Execute(packageChanges map[string]string, verbose bool, roo // Check if any targets are met targetMet := false - for _, t1 := range hook.Targets { + for _, target := range hook.Targets { if targetMet { break } if hook.TargetType == "package" { - for t2, operation := range packageChanges { - if t1 == t2 && slices.Contains(hook.TriggerOperations, operation) { + for change, operation := range packageChanges { + if target == change && slices.Contains(hook.TriggerOperations, operation) { targetMet = true break } } } else { - for _, t2 := range modifiedFiles { - if strings.HasPrefix(t2.Path, t1) { + glob, err := filepath.Glob(path.Join(rootDir, target)) + if err != nil { + return err + } + for _, change := range modifiedFiles { + if slices.Contains(glob, path.Join(rootDir, change.Path)) { targetMet = true break } From 90d5a23fcc0df77b4b4a97360bd128c3dc106185 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Mon, 7 Apr 2025 14:59:28 +0300 Subject: [PATCH 17/88] Remove redundant underscore --- utils/hooks.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/hooks.go b/utils/hooks.go index 8ee9dc7..28294f8 100644 --- a/utils/hooks.go +++ b/utils/hooks.go @@ -98,7 +98,7 @@ func (hook *BPMHook) Execute(packageChanges map[string]string, verbose bool, roo // Get modified files slice modifiedFiles := make([]*PackageFileEntry, 0) - for pkg, _ := range packageChanges { + for pkg := range packageChanges { modifiedFiles = append(modifiedFiles, GetPackageFiles(pkg, rootDir)...) } From 97de746a7d69f88cce7ebe151c8b3cff0494e4c4 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Mon, 7 Apr 2025 15:35:18 +0300 Subject: [PATCH 18/88] Move go code to src/ subdirectory --- Makefile | 2 +- go.mod => src/go.mod | 0 go.sum => src/go.sum | 0 main.go => src/main.go | 0 {utils => src/utils}/config.go | 0 {utils => src/utils}/extract_utils.go | 0 {utils => src/utils}/general_utils.go | 0 {utils => src/utils}/hooks.go | 0 {utils => src/utils}/operations.go | 0 {utils => src/utils}/package_utils.go | 0 {utils => src/utils}/repo_utils.go | 0 11 files changed, 1 insertion(+), 1 deletion(-) rename go.mod => src/go.mod (100%) rename go.sum => src/go.sum (100%) rename main.go => src/main.go (100%) rename {utils => src/utils}/config.go (100%) rename {utils => src/utils}/extract_utils.go (100%) rename {utils => src/utils}/general_utils.go (100%) rename {utils => src/utils}/hooks.go (100%) rename {utils => src/utils}/operations.go (100%) rename {utils => src/utils}/package_utils.go (100%) rename {utils => src/utils}/repo_utils.go (100%) diff --git a/Makefile b/Makefile index ab65f36..c6ef16e 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ endif build: mkdir -p build - $(GO) build -ldflags "-w" -o build/bpm gitlab.com/bubble-package-manager/bpm + cd src; $(GO) build -ldflags "-w" -o ../build/bpm gitlab.com/bubble-package-manager/bpm install: build/bpm config/ mkdir -p $(DESTDIR)$(BINDIR) diff --git a/go.mod b/src/go.mod similarity index 100% rename from go.mod rename to src/go.mod diff --git a/go.sum b/src/go.sum similarity index 100% rename from go.sum rename to src/go.sum diff --git a/main.go b/src/main.go similarity index 100% rename from main.go rename to src/main.go diff --git a/utils/config.go b/src/utils/config.go similarity index 100% rename from utils/config.go rename to src/utils/config.go diff --git a/utils/extract_utils.go b/src/utils/extract_utils.go similarity index 100% rename from utils/extract_utils.go rename to src/utils/extract_utils.go diff --git a/utils/general_utils.go b/src/utils/general_utils.go similarity index 100% rename from utils/general_utils.go rename to src/utils/general_utils.go diff --git a/utils/hooks.go b/src/utils/hooks.go similarity index 100% rename from utils/hooks.go rename to src/utils/hooks.go diff --git a/utils/operations.go b/src/utils/operations.go similarity index 100% rename from utils/operations.go rename to src/utils/operations.go diff --git a/utils/package_utils.go b/src/utils/package_utils.go similarity index 100% rename from utils/package_utils.go rename to src/utils/package_utils.go diff --git a/utils/repo_utils.go b/src/utils/repo_utils.go similarity index 100% rename from utils/repo_utils.go rename to src/utils/repo_utils.go From fcb2ef1515d16187f20f2f190a5f49a5b6375ecb Mon Sep 17 00:00:00 2001 From: EnumDev Date: Mon, 7 Apr 2025 17:06:40 +0300 Subject: [PATCH 19/88] Split project into the BPM frontend (src/bpm) and the BPM backend module (src/bpmlib) --- Makefile | 2 +- src/bpm/go.mod | 14 ++++ src/{ => bpm}/go.sum | 5 +- src/{ => bpm}/main.go | 112 ++++++++++++------------- src/{utils => bpmlib}/config.go | 2 +- src/{utils => bpmlib}/extract_utils.go | 2 +- src/{utils => bpmlib}/general_utils.go | 2 +- src/bpmlib/go.mod | 8 ++ src/bpmlib/go.sum | 6 ++ src/{utils => bpmlib}/hooks.go | 2 +- src/{utils => bpmlib}/operations.go | 2 +- src/{utils => bpmlib}/package_utils.go | 2 +- src/{utils => bpmlib}/repo_utils.go | 2 +- src/go.mod | 9 -- 14 files changed, 93 insertions(+), 77 deletions(-) create mode 100644 src/bpm/go.mod rename src/{ => bpm}/go.sum (57%) rename src/{ => bpm}/main.go (88%) rename src/{utils => bpmlib}/config.go (98%) rename src/{utils => bpmlib}/extract_utils.go (97%) rename src/{utils => bpmlib}/general_utils.go (98%) create mode 100644 src/bpmlib/go.mod create mode 100644 src/bpmlib/go.sum rename src/{utils => bpmlib}/hooks.go (99%) rename src/{utils => bpmlib}/operations.go (99%) rename src/{utils => bpmlib}/package_utils.go (99%) rename src/{utils => bpmlib}/repo_utils.go (99%) delete mode 100644 src/go.mod diff --git a/Makefile b/Makefile index c6ef16e..aae7c6f 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ endif build: mkdir -p build - cd src; $(GO) build -ldflags "-w" -o ../build/bpm gitlab.com/bubble-package-manager/bpm + cd src/bpm; $(GO) build -ldflags "-w" -o ../../build/bpm gitlab.com/bubble-package-manager/bpm install: build/bpm config/ mkdir -p $(DESTDIR)$(BINDIR) diff --git a/src/bpm/go.mod b/src/bpm/go.mod new file mode 100644 index 0000000..5525700 --- /dev/null +++ b/src/bpm/go.mod @@ -0,0 +1,14 @@ +module gitlab.com/bubble-package-manager/bpm + +go 1.23 + +toolchain go1.23.7 + +require git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib v0.5.0 + +replace git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib => ../bpmlib + +require ( + github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/src/go.sum b/src/bpm/go.sum similarity index 57% rename from src/go.sum rename to src/bpm/go.sum index 138d9eb..8a51310 100644 --- a/src/go.sum +++ b/src/bpm/go.sum @@ -1,9 +1,6 @@ -github.com/elliotchance/orderedmap/v2 v2.4.0 h1:6tUmMwD9F998FNpwFxA5E6NQvSpk2PVw7RKsVq3+2Cw= -github.com/elliotchance/orderedmap/v2 v2.4.0/go.mod h1:85lZyVbpGaGvHvnKa7Qhx7zncAdBIBq6u56Hb1PRU5Q= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f h1:xt29M2T6STgldg+WEP51gGePQCsQvklmP2eIhPIBK3g= github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/main.go b/src/bpm/main.go similarity index 88% rename from src/main.go rename to src/bpm/main.go index 7db4691..58de8d7 100644 --- a/src/main.go +++ b/src/bpm/main.go @@ -4,7 +4,7 @@ import ( "bufio" "flag" "fmt" - "gitlab.com/bubble-package-manager/bpm/utils" + "git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib" "log" "os" "path/filepath" @@ -42,7 +42,7 @@ var doCleanup = false var showRepoInfo = false func main() { - utils.ReadConfig() + bpmlib.ReadConfig() resolveFlags() resolveCommand() } @@ -102,30 +102,30 @@ func resolveCommand() { return } for n, pkg := range packages { - var info *utils.PackageInfo + var info *bpmlib.PackageInfo isFile := false if showRepoInfo { var err error - var entry *utils.RepositoryEntry - entry, _, err = utils.GetRepositoryEntry(pkg) + var entry *bpmlib.RepositoryEntry + entry, _, err = bpmlib.GetRepositoryEntry(pkg) if err != nil { - if entry = utils.ResolveVirtualPackage(pkg); entry == nil { + if entry = bpmlib.ResolveVirtualPackage(pkg); entry == nil { log.Fatalf("Error: could not find package (%s) in any repository\n", pkg) } } info = entry.Info } else if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() { - bpmpkg, err := utils.ReadPackage(pkg) + bpmpkg, err := bpmlib.ReadPackage(pkg) if err != nil { log.Fatalf("Error: could not read package: %s\n", err) } info = bpmpkg.PkgInfo isFile = true } else { - if isVirtual, p := utils.IsVirtualPackage(pkg, rootDir); isVirtual { - info = utils.GetPackageInfo(p, rootDir) + if isVirtual, p := bpmlib.IsVirtualPackage(pkg, rootDir); isVirtual { + info = bpmlib.GetPackageInfo(p, rootDir) } else { - info = utils.GetPackageInfo(pkg, rootDir) + info = bpmlib.GetPackageInfo(pkg, rootDir) } } if info == nil { @@ -141,10 +141,10 @@ func resolveCommand() { } fmt.Println("File: " + abs) } - fmt.Println(utils.CreateReadableInfo(true, true, true, info, rootDir)) + fmt.Println(bpmlib.CreateReadableInfo(true, true, true, info, rootDir)) } case list: - packages, err := utils.GetInstalledPackages(rootDir) + packages, err := bpmlib.GetInstalledPackages(rootDir) if err != nil { log.Fatalf("Error: could not get installed packages: %s", err.Error()) return @@ -161,7 +161,7 @@ func resolveCommand() { return } for n, pkg := range packages { - info := utils.GetPackageInfo(pkg, rootDir) + info := bpmlib.GetPackageInfo(pkg, rootDir) if info == nil { fmt.Printf("Package (%s) could not be found\n", pkg) continue @@ -169,7 +169,7 @@ func resolveCommand() { if n != 0 { fmt.Println() } - fmt.Println(utils.CreateReadableInfo(true, true, true, info, rootDir)) + fmt.Println(bpmlib.CreateReadableInfo(true, true, true, info, rootDir)) } } case search: @@ -178,9 +178,9 @@ func resolveCommand() { log.Fatalf("Error: no search terms given") } for i, term := range searchTerms { - nameResults := make([]*utils.PackageInfo, 0) - descResults := make([]*utils.PackageInfo, 0) - for _, repo := range utils.BPMConfig.Repositories { + nameResults := make([]*bpmlib.PackageInfo, 0) + descResults := make([]*bpmlib.PackageInfo, 0) + for _, repo := range bpmlib.BPMConfig.Repositories { for _, entry := range repo.Entries { if strings.Contains(entry.Info.Name, term) { nameResults = append(nameResults, entry.Info) @@ -212,17 +212,17 @@ func resolveCommand() { } // Check if installationReason argument is valid - ir := utils.Unknown + ir := bpmlib.Unknown if installationReason == "manual" { - ir = utils.Manual + ir = bpmlib.Manual } else if installationReason == "dependency" { - ir = utils.Dependency + ir = bpmlib.Dependency } else if installationReason != "" { log.Fatalf("Error: %s is not a valid installation reason", installationReason) } - operation := utils.BPMOperation{ - Actions: make([]utils.OperationAction, 0), + operation := bpmlib.BPMOperation{ + Actions: make([]bpmlib.OperationAction, 0), UnresolvedDepends: make([]string, 0), Changes: make(map[string]string), RootDir: rootDir, @@ -232,37 +232,37 @@ func resolveCommand() { // Search for packages for _, pkg := range pkgs { if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() { - bpmpkg, err := utils.ReadPackage(pkg) + bpmpkg, err := bpmlib.ReadPackage(pkg) if err != nil { log.Fatalf("Error: could not read package: %s\n", err) } - if !reinstall && utils.IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) && utils.GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() == bpmpkg.PkgInfo.GetFullVersion() { + if !reinstall && bpmlib.IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) && bpmlib.GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() == bpmpkg.PkgInfo.GetFullVersion() { continue } - operation.AppendAction(&utils.InstallPackageAction{ + operation.AppendAction(&bpmlib.InstallPackageAction{ File: pkg, IsDependency: false, BpmPackage: bpmpkg, }) } else { - var entry *utils.RepositoryEntry + var entry *bpmlib.RepositoryEntry - if e, _, err := utils.GetRepositoryEntry(pkg); err == nil { + if e, _, err := bpmlib.GetRepositoryEntry(pkg); err == nil { entry = e - } else if isVirtual, p := utils.IsVirtualPackage(pkg, rootDir); isVirtual { - entry, _, err = utils.GetRepositoryEntry(p) + } else if isVirtual, p := bpmlib.IsVirtualPackage(pkg, rootDir); isVirtual { + entry, _, err = bpmlib.GetRepositoryEntry(p) if err != nil { log.Fatalf("Error: could not find package (%s) in any repository\n", p) } - } else if e := utils.ResolveVirtualPackage(pkg); e != nil { + } else if e := bpmlib.ResolveVirtualPackage(pkg); e != nil { entry = e } else { log.Fatalf("Error: could not find package (%s) in any repository\n", pkg) } - if !reinstall && utils.IsPackageInstalled(entry.Info.Name, rootDir) && utils.GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() { + if !reinstall && bpmlib.IsPackageInstalled(entry.Info.Name, rootDir) && bpmlib.GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() { continue } - operation.AppendAction(&utils.FetchPackageAction{ + operation.AppendAction(&bpmlib.FetchPackageAction{ IsDependency: false, RepositoryEntry: entry, }) @@ -342,7 +342,7 @@ func resolveCommand() { // Sync repositories if !nosync { - for _, repo := range utils.BPMConfig.Repositories { + for _, repo := range bpmlib.BPMConfig.Repositories { fmt.Printf("Fetching package database for repository (%s)...\n", repo.Name) err := repo.SyncLocalDatabase() if err != nil { @@ -352,42 +352,42 @@ func resolveCommand() { fmt.Println("All package databases synced successfully!") } - utils.ReadConfig() + bpmlib.ReadConfig() // Get installed packages and check for updates - pkgs, err := utils.GetInstalledPackages(rootDir) + pkgs, err := bpmlib.GetInstalledPackages(rootDir) if err != nil { log.Fatalf("Error: could not get installed packages: %s\n", err) } - operation := utils.BPMOperation{ - Actions: make([]utils.OperationAction, 0), + operation := bpmlib.BPMOperation{ + Actions: make([]bpmlib.OperationAction, 0), UnresolvedDepends: make([]string, 0), Changes: make(map[string]string), RootDir: rootDir, - ForceInstallationReason: utils.Unknown, + ForceInstallationReason: bpmlib.Unknown, } // Search for packages for _, pkg := range pkgs { - if slices.Contains(utils.BPMConfig.IgnorePackages, pkg) { + if slices.Contains(bpmlib.BPMConfig.IgnorePackages, pkg) { continue } - var entry *utils.RepositoryEntry + var entry *bpmlib.RepositoryEntry // Check if installed package can be replaced and install that instead - if e := utils.FindReplacement(pkg); e != nil { + if e := bpmlib.FindReplacement(pkg); e != nil { entry = e - } else if entry, _, err = utils.GetRepositoryEntry(pkg); err != nil { + } else if entry, _, err = bpmlib.GetRepositoryEntry(pkg); err != nil { continue } - installedInfo := utils.GetPackageInfo(pkg, rootDir) + installedInfo := bpmlib.GetPackageInfo(pkg, rootDir) if installedInfo == nil { log.Fatalf("Error: could not get package info for (%s)\n", pkg) } else { - comparison := utils.ComparePackageVersions(*entry.Info, *installedInfo) + comparison := bpmlib.ComparePackageVersions(*entry.Info, *installedInfo) if comparison > 0 || reinstall { - operation.AppendAction(&utils.FetchPackageAction{ + operation.AppendAction(&bpmlib.FetchPackageAction{ IsDependency: false, RepositoryEntry: entry, }) @@ -450,7 +450,7 @@ func resolveCommand() { os.Exit(1) } } - for _, repo := range utils.BPMConfig.Repositories { + for _, repo := range bpmlib.BPMConfig.Repositories { fmt.Printf("Fetching package database for repository (%s)...\n", repo.Name) err := repo.SyncLocalDatabase() if err != nil { @@ -468,8 +468,8 @@ func resolveCommand() { return } - operation := &utils.BPMOperation{ - Actions: make([]utils.OperationAction, 0), + operation := &bpmlib.BPMOperation{ + Actions: make([]bpmlib.OperationAction, 0), UnresolvedDepends: make([]string, 0), Changes: make(map[string]string), RootDir: rootDir, @@ -477,11 +477,11 @@ func resolveCommand() { // Search for packages for _, pkg := range packages { - bpmpkg := utils.GetPackage(pkg, rootDir) + bpmpkg := bpmlib.GetPackage(pkg, rootDir) if bpmpkg == nil { continue } - operation.AppendAction(&utils.RemovePackageAction{BpmPackage: bpmpkg}) + operation.AppendAction(&bpmlib.RemovePackageAction{BpmPackage: bpmpkg}) } // Skip needed packages if the --unused flag is on @@ -531,8 +531,8 @@ func resolveCommand() { log.Fatalf("Error: this subcommand needs to be run with superuser permissions") } - operation := &utils.BPMOperation{ - Actions: make([]utils.OperationAction, 0), + operation := &bpmlib.BPMOperation{ + Actions: make([]bpmlib.OperationAction, 0), UnresolvedDepends: make([]string, 0), Changes: make(map[string]string), RootDir: rootDir, @@ -585,7 +585,7 @@ func resolveCommand() { if os.IsNotExist(err) { log.Fatalf("Error: file (%s) does not exist!\n", absFile) } - pkgs, err := utils.GetInstalledPackages(rootDir) + pkgs, err := bpmlib.GetInstalledPackages(rootDir) if err != nil { log.Fatalf("Error: could not get installed packages: %s\n", err.Error()) } @@ -604,7 +604,7 @@ func resolveCommand() { var pkgList []string for _, pkg := range pkgs { - if slices.ContainsFunc(utils.GetPackageFiles(pkg, rootDir), func(entry *utils.PackageFileEntry) bool { + if slices.ContainsFunc(bpmlib.GetPackageFiles(pkg, rootDir), func(entry *bpmlib.PackageFileEntry) bool { return entry.Path == absFile }) { pkgList = append(pkgList, pkg) @@ -694,8 +694,8 @@ func resolveFlags() { installFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root") installFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing") installFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts") - installFlagSet.StringVar(&utils.BPMConfig.BinaryOutputDir, "o", utils.BPMConfig.BinaryOutputDir, "Set the binary output directory") - installFlagSet.StringVar(&utils.BPMConfig.CompilationDir, "c", utils.BPMConfig.CompilationDir, "Set the compilation directory") + installFlagSet.StringVar(&bpmlib.BPMConfig.BinaryOutputDir, "o", bpmlib.BPMConfig.BinaryOutputDir, "Set the binary output directory") + installFlagSet.StringVar(&bpmlib.BPMConfig.CompilationDir, "c", bpmlib.BPMConfig.CompilationDir, "Set the compilation directory") installFlagSet.BoolVar(&buildSource, "b", false, "Build binary package from source package") installFlagSet.BoolVar(&skipCheck, "s", false, "Skip check function during source compilation") installFlagSet.BoolVar(&keepTempDir, "k", false, "Keep temporary directory after source compilation") diff --git a/src/utils/config.go b/src/bpmlib/config.go similarity index 98% rename from src/utils/config.go rename to src/bpmlib/config.go index e57b03f..18e03c2 100644 --- a/src/utils/config.go +++ b/src/bpmlib/config.go @@ -1,4 +1,4 @@ -package utils +package bpmlib import ( "gopkg.in/yaml.v3" diff --git a/src/utils/extract_utils.go b/src/bpmlib/extract_utils.go similarity index 97% rename from src/utils/extract_utils.go rename to src/bpmlib/extract_utils.go index 6c51928..2f0b3e8 100644 --- a/src/utils/extract_utils.go +++ b/src/bpmlib/extract_utils.go @@ -1,4 +1,4 @@ -package utils +package bpmlib import ( "archive/tar" diff --git a/src/utils/general_utils.go b/src/bpmlib/general_utils.go similarity index 98% rename from src/utils/general_utils.go rename to src/bpmlib/general_utils.go index dd1e812..411a4fe 100644 --- a/src/utils/general_utils.go +++ b/src/bpmlib/general_utils.go @@ -1,4 +1,4 @@ -package utils +package bpmlib import ( "fmt" diff --git a/src/bpmlib/go.mod b/src/bpmlib/go.mod new file mode 100644 index 0000000..d0dabd5 --- /dev/null +++ b/src/bpmlib/go.mod @@ -0,0 +1,8 @@ +module git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib + +go 1.23 + +require ( + github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f + gopkg.in/yaml.v3 v3.0.1 +) diff --git a/src/bpmlib/go.sum b/src/bpmlib/go.sum new file mode 100644 index 0000000..8a51310 --- /dev/null +++ b/src/bpmlib/go.sum @@ -0,0 +1,6 @@ +github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f h1:xt29M2T6STgldg+WEP51gGePQCsQvklmP2eIhPIBK3g= +github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/utils/hooks.go b/src/bpmlib/hooks.go similarity index 99% rename from src/utils/hooks.go rename to src/bpmlib/hooks.go index 28294f8..e0503d4 100644 --- a/src/utils/hooks.go +++ b/src/bpmlib/hooks.go @@ -1,4 +1,4 @@ -package utils +package bpmlib import ( "errors" diff --git a/src/utils/operations.go b/src/bpmlib/operations.go similarity index 99% rename from src/utils/operations.go rename to src/bpmlib/operations.go index ab3adeb..72d2979 100644 --- a/src/utils/operations.go +++ b/src/bpmlib/operations.go @@ -1,4 +1,4 @@ -package utils +package bpmlib import ( "errors" diff --git a/src/utils/package_utils.go b/src/bpmlib/package_utils.go similarity index 99% rename from src/utils/package_utils.go rename to src/bpmlib/package_utils.go index 443f981..b030cbe 100644 --- a/src/utils/package_utils.go +++ b/src/bpmlib/package_utils.go @@ -1,4 +1,4 @@ -package utils +package bpmlib import ( "archive/tar" diff --git a/src/utils/repo_utils.go b/src/bpmlib/repo_utils.go similarity index 99% rename from src/utils/repo_utils.go rename to src/bpmlib/repo_utils.go index 0cf8c1f..ffe64b2 100644 --- a/src/utils/repo_utils.go +++ b/src/bpmlib/repo_utils.go @@ -1,4 +1,4 @@ -package utils +package bpmlib import ( "errors" diff --git a/src/go.mod b/src/go.mod deleted file mode 100644 index eb5bc0a..0000000 --- a/src/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module gitlab.com/bubble-package-manager/bpm - -go 1.22 - -require ( - github.com/elliotchance/orderedmap/v2 v2.4.0 // indirect - github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) From 05f273d6871c76e5762a7d4b3971dcb5f432b9d9 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Mon, 7 Apr 2025 17:10:19 +0300 Subject: [PATCH 20/88] Update bpm frontend module name --- Makefile | 2 +- src/bpm/go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index aae7c6f..851f832 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ endif build: mkdir -p build - cd src/bpm; $(GO) build -ldflags "-w" -o ../../build/bpm gitlab.com/bubble-package-manager/bpm + cd src/bpm; $(GO) build -ldflags "-w" -o ../../build/bpm git.enumerated.dev/bubble-package-manager/bpm/src/bpm install: build/bpm config/ mkdir -p $(DESTDIR)$(BINDIR) diff --git a/src/bpm/go.mod b/src/bpm/go.mod index 5525700..5129f8b 100644 --- a/src/bpm/go.mod +++ b/src/bpm/go.mod @@ -1,4 +1,4 @@ -module gitlab.com/bubble-package-manager/bpm +module git.enumerated.dev/bubble-package-manager/bpm/src/bpm go 1.23 From 68291e2666676d619905238d473b4d63c26b722a Mon Sep 17 00:00:00 2001 From: EnumDev Date: Mon, 7 Apr 2025 17:14:04 +0300 Subject: [PATCH 21/88] Rename go files --- src/bpmlib/{package_utils.go => packages.go} | 0 src/bpmlib/{repo_utils.go => repositories.go} | 0 src/bpmlib/{extract_utils.go => tarball.go} | 0 src/bpmlib/{general_utils.go => utils.go} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename src/bpmlib/{package_utils.go => packages.go} (100%) rename src/bpmlib/{repo_utils.go => repositories.go} (100%) rename src/bpmlib/{extract_utils.go => tarball.go} (100%) rename src/bpmlib/{general_utils.go => utils.go} (100%) diff --git a/src/bpmlib/package_utils.go b/src/bpmlib/packages.go similarity index 100% rename from src/bpmlib/package_utils.go rename to src/bpmlib/packages.go diff --git a/src/bpmlib/repo_utils.go b/src/bpmlib/repositories.go similarity index 100% rename from src/bpmlib/repo_utils.go rename to src/bpmlib/repositories.go diff --git a/src/bpmlib/extract_utils.go b/src/bpmlib/tarball.go similarity index 100% rename from src/bpmlib/extract_utils.go rename to src/bpmlib/tarball.go diff --git a/src/bpmlib/general_utils.go b/src/bpmlib/utils.go similarity index 100% rename from src/bpmlib/general_utils.go rename to src/bpmlib/utils.go From 9485248d8ee831cd6065f716d4a13a75324d5b75 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Mon, 7 Apr 2025 17:28:26 +0300 Subject: [PATCH 22/88] Unexport functions --- src/bpmlib/hooks.go | 4 ++-- src/bpmlib/operations.go | 14 +++++++------- src/bpmlib/packages.go | 26 +++++++++++++------------- src/bpmlib/tarball.go | 6 +++--- src/bpmlib/utils.go | 4 ++-- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/bpmlib/hooks.go b/src/bpmlib/hooks.go index e0503d4..a9bbff4 100644 --- a/src/bpmlib/hooks.go +++ b/src/bpmlib/hooks.go @@ -23,8 +23,8 @@ type BPMHook struct { Run string `yaml:"run"` } -// CreateHook returns a BPMHook instance based on the content of the given string -func CreateHook(sourcePath string) (*BPMHook, error) { +// createHook returns a BPMHook instance based on the content of the given string +func createHook(sourcePath string) (*BPMHook, error) { // Read hook from source path bytes, err := os.ReadFile(sourcePath) if err != nil { diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index 72d2979..56efc48 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -372,12 +372,12 @@ func (operation *BPMOperation) ShowOperationSummary() { fmt.Println("Warning: Operating in " + operation.RootDir) } 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", unsignedBytesToHumanReadable(operation.GetTotalDownloadSize())) } 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 { - 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)), "-")) } } @@ -391,7 +391,7 @@ func (operation *BPMOperation) RunHooks(verbose bool) error { // Find all hooks, validate and execute them for _, entry := range dirEntries { if entry.Type().IsRegular() && strings.HasSuffix(entry.Name(), ".bpmhook") { - hook, err := CreateHook(path.Join(operation.RootDir, "var/lib/bpm/hooks", entry.Name())) + hook, err := createHook(path.Join(operation.RootDir, "var/lib/bpm/hooks", entry.Name())) if err != nil { log.Printf("Error while reading hook (%s): %s", entry.Name(), err) } @@ -458,7 +458,7 @@ func (operation *BPMOperation) Execute(verbose, force bool) error { for _, action := range operation.Actions { if action.GetActionType() == "remove" { pkgInfo := action.(*RemovePackageAction).BpmPackage.PkgInfo - err := RemovePackage(pkgInfo.Name, verbose, operation.RootDir) + err := removePackage(pkgInfo.Name, verbose, operation.RootDir) if err != nil { return errors.New(fmt.Sprintf("could not remove package (%s): %s\n", pkgInfo.Name, err)) } @@ -468,9 +468,9 @@ func (operation *BPMOperation) Execute(verbose, force bool) error { isReinstall := IsPackageInstalled(bpmpkg.PkgInfo.Name, operation.RootDir) var err error if value.IsDependency { - err = InstallPackage(value.File, operation.RootDir, verbose, true, false, false, false) + err = installPackage(value.File, operation.RootDir, verbose, true, false, false, false) } else { - err = InstallPackage(value.File, operation.RootDir, verbose, force, false, false, false) + err = installPackage(value.File, operation.RootDir, verbose, force, false, false, false) } if err != nil { return errors.New(fmt.Sprintf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err)) diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index b030cbe..d7008b7 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -288,7 +288,7 @@ const ( Remove = 2 ) -func ExecutePackageScripts(filename, rootDir string, operation Operation, postOperation bool) error { +func executePackageScripts(filename, rootDir string, operation Operation, postOperation bool) error { pkgInfo, err := ReadPackage(filename) if err != nil { return err @@ -478,12 +478,12 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) error { if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { - err := ExecutePackageScripts(filename, rootDir, Install, false) + err := executePackageScripts(filename, rootDir, Install, false) if err != nil { return err } } else { - err := ExecutePackageScripts(filename, rootDir, Update, false) + err := executePackageScripts(filename, rootDir, Update, false) if err != nil { return err } @@ -494,7 +494,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) return err } - tarballFile, err := ReadTarballContent(filename, "files.tar.gz") + tarballFile, err := readTarballContent(filename, "files.tar.gz") if err != nil { return err } @@ -629,12 +629,12 @@ func isSplitPackage(filename string) bool { func compilePackage(bpmpkg *BPMPackage, filename, rootDir string, verbose, binaryPkgFromSrc, skipCheck, keepTempDir bool) (error, []string) { var files []string if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { - err := ExecutePackageScripts(filename, rootDir, Install, false) + err := executePackageScripts(filename, rootDir, Install, false) if err != nil { return err, nil } } else { - err := ExecutePackageScripts(filename, rootDir, Update, false) + err := executePackageScripts(filename, rootDir, Update, false) if err != nil { return err, nil } @@ -747,12 +747,12 @@ func compilePackage(bpmpkg *BPMPackage, filename, rootDir string, verbose, binar } fmt.Println("Running source.sh file...") if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { - err = ExecutePackageScripts(filename, rootDir, Install, false) + err = executePackageScripts(filename, rootDir, Install, false) if err != nil { return err, nil } } else { - err = ExecutePackageScripts(filename, rootDir, Update, false) + err = executePackageScripts(filename, rootDir, Update, false) if err != nil { return err, nil } @@ -1051,7 +1051,7 @@ fi return nil, files } -func InstallPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc, skipCheck, keepTempDir bool) error { +func installPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc, skipCheck, keepTempDir bool) error { if _, err := os.Stat(filename); os.IsNotExist(err) { return err } @@ -1198,7 +1198,7 @@ func InstallPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc, return err } - tarballFile, err := ReadTarballContent(filename, "pkg.files") + tarballFile, err := readTarballContent(filename, "pkg.files") if err != nil { return err } @@ -1246,12 +1246,12 @@ func InstallPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc, } if !packageInstalled { - err = ExecutePackageScripts(filename, rootDir, Install, true) + err = executePackageScripts(filename, rootDir, Install, true) if err != nil { return err } } else { - err = ExecutePackageScripts(filename, rootDir, Update, true) + err = executePackageScripts(filename, rootDir, Update, true) if err != nil { return err } @@ -1552,7 +1552,7 @@ func GetAllPackageFiles(rootDir string, excludePackages ...string) (map[string][ return ret, nil } -func RemovePackage(pkg string, verbose bool, rootDir string) error { +func removePackage(pkg string, verbose bool, rootDir string) error { installedDir := path.Join(rootDir, "var/lib/bpm/installed/") pkgDir := path.Join(installedDir, pkg) pkgInfo := GetPackageInfo(pkg, rootDir) diff --git a/src/bpmlib/tarball.go b/src/bpmlib/tarball.go index 2f0b3e8..84a0008 100644 --- a/src/bpmlib/tarball.go +++ b/src/bpmlib/tarball.go @@ -7,12 +7,12 @@ import ( "os" ) -type TarballFileReader struct { +type tarballFileReader struct { tarReader *tar.Reader file *os.File } -func ReadTarballContent(tarballPath, fileToExtract string) (*TarballFileReader, error) { +func readTarballContent(tarballPath, fileToExtract string) (*tarballFileReader, error) { file, err := os.Open(tarballPath) if err != nil { return nil, err @@ -32,7 +32,7 @@ func ReadTarballContent(tarballPath, fileToExtract string) (*TarballFileReader, return nil, errors.New("file to extract must be a regular file") } - return &TarballFileReader{ + return &tarballFileReader{ tarReader: tr, file: file, }, nil diff --git a/src/bpmlib/utils.go b/src/bpmlib/utils.go index 411a4fe..a89dff8 100644 --- a/src/bpmlib/utils.go +++ b/src/bpmlib/utils.go @@ -55,7 +55,7 @@ func stringSliceRemove(s []string, r string) []string { return s } -func UnsignedBytesToHumanReadable(b uint64) string { +func unsignedBytesToHumanReadable(b uint64) string { bf := float64(b) for _, unit := range []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} { if math.Abs(bf) < 1024.0 { @@ -66,7 +66,7 @@ func UnsignedBytesToHumanReadable(b uint64) string { return fmt.Sprintf("%.1fYiB", bf) } -func BytesToHumanReadable(b int64) string { +func bytesToHumanReadable(b int64) string { bf := float64(b) for _, unit := range []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} { if math.Abs(bf) < 1024.0 { From 0801612166e261ff1ec7cd7278d15e5296216953 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Mon, 7 Apr 2025 21:03:33 +0300 Subject: [PATCH 23/88] Create functions for basic package management in bpmlib --- src/bpm/main.go | 246 ++++++++--------------------------------- src/bpmlib/general.go | 250 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 293 insertions(+), 203 deletions(-) create mode 100644 src/bpmlib/general.go diff --git a/src/bpm/main.go b/src/bpm/main.go index 58de8d7..f263423 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -202,107 +202,41 @@ func resolveCommand() { } } case install: + // Check for required permissions if os.Getuid() != 0 { log.Fatalf("Error: this subcommand needs to be run with superuser permissions") } - pkgs := subcommandArgs - if len(pkgs) == 0 { + + // Return if no packages are specified + if len(subcommandArgs) == 0 { fmt.Println("No packages or files were given to install") return } // Check if installationReason argument is valid ir := bpmlib.Unknown - if installationReason == "manual" { + switch installationReason { + case "manual": ir = bpmlib.Manual - } else if installationReason == "dependency" { + case "dependency": ir = bpmlib.Dependency - } else if installationReason != "" { + case "": + default: log.Fatalf("Error: %s is not a valid installation reason", installationReason) } - operation := bpmlib.BPMOperation{ - Actions: make([]bpmlib.OperationAction, 0), - UnresolvedDepends: make([]string, 0), - Changes: make(map[string]string), - RootDir: rootDir, - ForceInstallationReason: ir, + // Get reinstall method + var reinstallMethod bpmlib.ReinstallMethod + if reinstallAll { + reinstallMethod = bpmlib.ReinstallMethodAll + } else if reinstall { + reinstallMethod = bpmlib.ReinstallMethodSpecified + } else { + reinstallMethod = bpmlib.ReinstallMethodNone } - // Search for packages - for _, pkg := range pkgs { - if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() { - bpmpkg, err := bpmlib.ReadPackage(pkg) - if err != nil { - log.Fatalf("Error: could not read package: %s\n", err) - } - if !reinstall && bpmlib.IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) && bpmlib.GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() == bpmpkg.PkgInfo.GetFullVersion() { - continue - } - operation.AppendAction(&bpmlib.InstallPackageAction{ - File: pkg, - IsDependency: false, - BpmPackage: bpmpkg, - }) - } else { - var entry *bpmlib.RepositoryEntry - - if e, _, err := bpmlib.GetRepositoryEntry(pkg); err == nil { - entry = e - } else if isVirtual, p := bpmlib.IsVirtualPackage(pkg, rootDir); isVirtual { - entry, _, err = bpmlib.GetRepositoryEntry(p) - if err != nil { - log.Fatalf("Error: could not find package (%s) in any repository\n", p) - } - } else if e := bpmlib.ResolveVirtualPackage(pkg); e != nil { - entry = e - } else { - log.Fatalf("Error: could not find package (%s) in any repository\n", pkg) - } - if !reinstall && bpmlib.IsPackageInstalled(entry.Info.Name, rootDir) && bpmlib.GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() { - continue - } - operation.AppendAction(&bpmlib.FetchPackageAction{ - IsDependency: false, - RepositoryEntry: entry, - }) - } - } - - // Resolve dependencies - err := operation.ResolveDependencies(reinstallAll, !noOptional, verbose) - if err != nil { - log.Fatalf("Error: could not resolve dependencies: %s\n", err) - } - if len(operation.UnresolvedDepends) != 0 { - if !force { - log.Fatalf("Error: the following dependencies could not be found in any repositories: %s\n", strings.Join(operation.UnresolvedDepends, ", ")) - } else { - log.Println("Warning: The following dependencies could not be found in any repositories: " + strings.Join(operation.UnresolvedDepends, ", ")) - } - } - - // Replace obsolete packages - operation.ReplaceObsoletePackages() - - // Check for conflicts - conflicts, err := operation.CheckForConflicts() - if err != nil { - log.Fatalf("Error: could not complete package conflict check: %s\n", err) - } - if len(conflicts) > 0 { - if !force { - log.Println("Error: conflicting packages found") - } else { - log.Fatalf("Warning: conflicting packages found") - } - for pkg, conflict := range conflicts { - fmt.Printf("%s is in conflict with the following packages: %s\n", pkg, strings.Join(conflict, ", ")) - } - if !force { - os.Exit(0) - } - } + // Create Installation Operation + operation, err := bpmlib.InstallPackages(rootDir, ir, reinstallMethod, !noOptional, force, verbose, subcommandArgs...) // Show operation summary operation.ShowOperationSummary() @@ -336,81 +270,17 @@ func resolveCommand() { log.Fatalf("Error: could not run hooks: %s\n", err) } case update: + // Check for required permissions if os.Getuid() != 0 { log.Fatalf("Error: this subcommand needs to be run with superuser permissions") } - // Sync repositories - if !nosync { - for _, repo := range bpmlib.BPMConfig.Repositories { - fmt.Printf("Fetching package database for repository (%s)...\n", repo.Name) - err := repo.SyncLocalDatabase() - if err != nil { - log.Fatalf("Error: could not sync local database for repository (%s): %s\n", repo.Name, err) - } - } - fmt.Println("All package databases synced successfully!") - } - - bpmlib.ReadConfig() - - // Get installed packages and check for updates - pkgs, err := bpmlib.GetInstalledPackages(rootDir) + // Create Update Operation + operation, err := bpmlib.UpdatePackages(rootDir, !nosync, !noOptional, force, verbose) if err != nil { - log.Fatalf("Error: could not get installed packages: %s\n", err) + log.Fatalf("Error: could not update packages: %s\n", err) } - operation := bpmlib.BPMOperation{ - Actions: make([]bpmlib.OperationAction, 0), - UnresolvedDepends: make([]string, 0), - Changes: make(map[string]string), - RootDir: rootDir, - ForceInstallationReason: bpmlib.Unknown, - } - - // Search for packages - for _, pkg := range pkgs { - if slices.Contains(bpmlib.BPMConfig.IgnorePackages, pkg) { - continue - } - var entry *bpmlib.RepositoryEntry - // Check if installed package can be replaced and install that instead - if e := bpmlib.FindReplacement(pkg); e != nil { - entry = e - } else if entry, _, err = bpmlib.GetRepositoryEntry(pkg); err != nil { - continue - } - - installedInfo := bpmlib.GetPackageInfo(pkg, rootDir) - if installedInfo == nil { - log.Fatalf("Error: could not get package info for (%s)\n", pkg) - } else { - comparison := bpmlib.ComparePackageVersions(*entry.Info, *installedInfo) - if comparison > 0 || reinstall { - operation.AppendAction(&bpmlib.FetchPackageAction{ - IsDependency: false, - RepositoryEntry: entry, - }) - } - } - } - - // Check for new dependencies in updated packages - err = operation.ResolveDependencies(reinstallAll, !noOptional, verbose) - if err != nil { - log.Fatalf("Error: could not resolve dependencies: %s\n", err) - } - if len(operation.UnresolvedDepends) != 0 { - if !force { - log.Fatalf("Error: the following dependencies could not be found in any repositories: %s\n", strings.Join(operation.UnresolvedDepends, ", ")) - } else { - log.Println("Warning: The following dependencies could not be found in any repositories: " + strings.Join(operation.UnresolvedDepends, ", ")) - } - } - - // Replace obsolete packages - operation.ReplaceObsoletePackages() - // Show operation summary operation.ShowOperationSummary() @@ -438,9 +308,12 @@ func resolveCommand() { log.Fatalf("Error: could not run hooks: %s\n", err) } case sync: + // Check for required permissions if os.Getuid() != 0 { log.Fatalf("Error: this subcommand needs to be run with superuser permissions") } + + // Confirmation Prompt if !yesAll { fmt.Printf("Are you sure you wish to sync all databases? [y\\N] ") reader := bufio.NewReader(os.Stdin) @@ -450,54 +323,28 @@ func resolveCommand() { os.Exit(1) } } - for _, repo := range bpmlib.BPMConfig.Repositories { - fmt.Printf("Fetching package database for repository (%s)...\n", repo.Name) - err := repo.SyncLocalDatabase() - if err != nil { - log.Fatalf("Error: could not sync local database for repository (%s): %s\n", repo.Name, err) - } + + err := bpmlib.SyncDatabase(verbose) + if err != nil { + log.Fatalf("Error: could not sync local database: %s\n", err) } + fmt.Println("All package databases synced successfully!") case remove: + // Check for required permissions if os.Getuid() != 0 { log.Fatalf("Error: this subcommand needs to be run with superuser permissions") } - packages := subcommandArgs - if len(packages) == 0 { + + if len(subcommandArgs) == 0 { fmt.Println("No packages were given") return } - operation := &bpmlib.BPMOperation{ - Actions: make([]bpmlib.OperationAction, 0), - UnresolvedDepends: make([]string, 0), - Changes: make(map[string]string), - RootDir: rootDir, - } - - // Search for packages - for _, pkg := range packages { - bpmpkg := bpmlib.GetPackage(pkg, rootDir) - if bpmpkg == nil { - continue - } - operation.AppendAction(&bpmlib.RemovePackageAction{BpmPackage: bpmpkg}) - } - - // Skip needed packages if the --unused flag is on - if removeUnused { - err := operation.RemoveNeededPackages() - if err != nil { - log.Fatalf("Error: could not skip needed packages: %s\n", err) - } - } - - // Do package cleanup - if doCleanup { - err := operation.Cleanup(verbose) - if err != nil { - log.Fatalf("Error: could not perform cleanup for operation: %s\n", err) - } + // Remove packages + operation, err := bpmlib.RemovePackages(rootDir, removeUnused, doCleanup, verbose, subcommandArgs...) + if err != nil { + log.Fatalf("Error: could not remove packages: %s\n", err) } // Show operation summary @@ -515,7 +362,7 @@ func resolveCommand() { } // Execute operation - err := operation.Execute(verbose, force) + err = operation.Execute(verbose, force) if err != nil { log.Fatalf("Error: could not complete operation: %s\n", err) } @@ -527,21 +374,15 @@ func resolveCommand() { log.Fatalf("Error: could not run hooks: %s\n", err) } case cleanup: + // Check for required permissions if os.Getuid() != 0 { log.Fatalf("Error: this subcommand needs to be run with superuser permissions") } - operation := &bpmlib.BPMOperation{ - Actions: make([]bpmlib.OperationAction, 0), - UnresolvedDepends: make([]string, 0), - Changes: make(map[string]string), - RootDir: rootDir, - } - - // Do package cleanup - err := operation.Cleanup(verbose) + // Do cleanup + operation, err := bpmlib.CleanupPackages(rootDir, verbose) if err != nil { - log.Fatalf("Error: could not perform cleanup for operation: %s\n", err) + log.Fatalf("Error: could not cleanup packages: %s\n", err) } // Show operation summary @@ -711,7 +552,6 @@ func resolveFlags() { updateFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing") updateFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts") updateFlagSet.BoolVar(&force, "f", false, "Force update by skipping architecture and dependency resolution") - updateFlagSet.BoolVar(&reinstall, "reinstall", false, "Fetches and reinstalls all packages even if they do not have a newer version available") updateFlagSet.BoolVar(&nosync, "no-sync", false, "Skips package database syncing") updateFlagSet.Usage = printHelp // Sync flags diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go new file mode 100644 index 0000000..57d7eb2 --- /dev/null +++ b/src/bpmlib/general.go @@ -0,0 +1,250 @@ +package bpmlib + +import ( + "fmt" + "log" + "os" + "slices" + "strings" +) + +type ReinstallMethod uint8 + +const ( + ReinstallMethodNone ReinstallMethod = iota + ReinstallMethodSpecified ReinstallMethod = iota + ReinstallMethodAll ReinstallMethod = iota +) + +// InstallPackages installs the specified packages into the given root directory by fetching them from repositories or directly from local bpm archives +func InstallPackages(rootDir string, installationReason InstallationReason, reinstallMethod ReinstallMethod, installOptionalDependencies, forceInstallation, verbose bool, packages ...string) (operation *BPMOperation, err error) { + // Setup operation struct + operation = &BPMOperation{ + Actions: make([]OperationAction, 0), + UnresolvedDepends: make([]string, 0), + Changes: make(map[string]string), + RootDir: rootDir, + ForceInstallationReason: installationReason, + } + + // Resolve packages + for _, pkg := range packages { + if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() { + bpmpkg, err := ReadPackage(pkg) + if err != nil { + return nil, fmt.Errorf("could not read package: %s", err) + } + if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) && GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() == bpmpkg.PkgInfo.GetFullVersion() { + continue + } + operation.AppendAction(&InstallPackageAction{ + File: pkg, + IsDependency: false, + BpmPackage: bpmpkg, + }) + } else { + var entry *RepositoryEntry + + if e, _, err := GetRepositoryEntry(pkg); err == nil { + entry = e + } else if isVirtual, p := IsVirtualPackage(pkg, rootDir); isVirtual { + entry, _, err = GetRepositoryEntry(p) + if err != nil { + return nil, fmt.Errorf("could not find package (%s) in any repositor", p) + } + } else if e := ResolveVirtualPackage(pkg); e != nil { + entry = e + } else { + return nil, fmt.Errorf("could not find package (%s) in any repository", pkg) + } + if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(entry.Info.Name, rootDir) && GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() { + continue + } + operation.AppendAction(&FetchPackageAction{ + IsDependency: false, + RepositoryEntry: entry, + }) + } + } + + // Resolve dependencies + err = operation.ResolveDependencies(reinstallMethod == ReinstallMethodAll, installOptionalDependencies, verbose) + if err != nil { + return nil, fmt.Errorf("could not resolve dependencies: %s", err) + } + if len(operation.UnresolvedDepends) != 0 { + if !forceInstallation { + return nil, fmt.Errorf("dependencies (%s) could not be found in any repositories", strings.Join(operation.UnresolvedDepends, ", ")) + } else if verbose { + log.Println("Warning: The following dependencies could not be found in any repositories: " + strings.Join(operation.UnresolvedDepends, ", ")) + } + } + + // Replace obsolete packages + operation.ReplaceObsoletePackages() + + // Check for conflicts + conflicts, err := operation.CheckForConflicts() + if err != nil { + return nil, fmt.Errorf("could not complete package conflict check: %s", err) + } + if len(conflicts) > 0 { + if verbose { + for pkg, conflict := range conflicts { + fmt.Printf("%s is in conflict with packages (%s)\n", pkg, strings.Join(conflict, ", ")) + } + } + if !forceInstallation { + return nil, fmt.Errorf("conflicting packages found") + } else { + log.Println("Warning: conflicting packages found") + } + } + + return operation, nil +} + +// RemovePackages removes the specified packages from the given root directory +func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencies, verbose bool, packages ...string) (operation *BPMOperation, err error) { + operation = &BPMOperation{ + Actions: make([]OperationAction, 0), + UnresolvedDepends: make([]string, 0), + Changes: make(map[string]string), + RootDir: rootDir, + } + + // Search for packages + for _, pkg := range packages { + bpmpkg := GetPackage(pkg, 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(verbose) + if err != nil { + return nil, fmt.Errorf("could not perform cleanup for operation: %s", err) + } + } + return operation, nil +} + +// CleanupPackages finds packages installed as dependencies which are no longer required by the rest of the system in the given root directory +func CleanupPackages(rootDir string, verbose bool) (operation *BPMOperation, err error) { + operation = &BPMOperation{ + Actions: make([]OperationAction, 0), + UnresolvedDepends: make([]string, 0), + Changes: make(map[string]string), + RootDir: rootDir, + } + + // Do package cleanup + err = operation.Cleanup(verbose) + if err != nil { + return nil, fmt.Errorf("could not perform cleanup for operation: %s", err) + } + + return operation, nil +} + +// UpdatePackages fetches the newest versions of all installed packages from +func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependencies, forceInstallation, verbose bool) (operation *BPMOperation, err error) { + // Sync repositories + if syncDatabase { + err := SyncDatabase(verbose) + if err != nil { + return nil, fmt.Errorf("could not sync local database: %s", err) + } + if verbose { + fmt.Println("All package databases synced successfully!") + } + } + + // Reload config and local databases + ReadConfig() + + // Get installed packages and check for updates + pkgs, err := GetInstalledPackages(rootDir) + if err != nil { + return nil, fmt.Errorf("could not get installed packages: %s", err) + } + + operation = &BPMOperation{ + Actions: make([]OperationAction, 0), + UnresolvedDepends: make([]string, 0), + Changes: make(map[string]string), + RootDir: rootDir, + ForceInstallationReason: Unknown, + } + + // Search for packages + for _, pkg := range pkgs { + if slices.Contains(BPMConfig.IgnorePackages, pkg) { + continue + } + var entry *RepositoryEntry + // Check if installed package can be replaced and install that instead + if e := FindReplacement(pkg); e != nil { + entry = e + } else if entry, _, err = GetRepositoryEntry(pkg); err != nil { + continue + } + + installedInfo := GetPackageInfo(pkg, rootDir) + if installedInfo == nil { + return nil, fmt.Errorf("could not get package info for package (%s)", pkg) + } else { + comparison := ComparePackageVersions(*entry.Info, *installedInfo) + if comparison > 0 { + operation.AppendAction(&FetchPackageAction{ + IsDependency: false, + RepositoryEntry: entry, + }) + } + } + } + + // Check for new dependencies in updated packages + err = operation.ResolveDependencies(false, installOptionalDependencies, verbose) + if err != nil { + return nil, fmt.Errorf("could not resolve dependencies: %s", err) + } + if len(operation.UnresolvedDepends) != 0 { + if !forceInstallation { + return nil, fmt.Errorf("dependencies (%s) could not be found in any repositories", strings.Join(operation.UnresolvedDepends, ", ")) + } else if verbose { + log.Printf("Warning: dependencies (%s) could not be found in any repositories\n", strings.Join(operation.UnresolvedDepends, ", ")) + } + } + + // Replace obsolete packages + operation.ReplaceObsoletePackages() + return operation, nil +} + +// SyncDatabase syncs all databases declared in /etc/bpm.conf +func SyncDatabase(verbose bool) (err error) { + for _, repo := range BPMConfig.Repositories { + if verbose { + fmt.Printf("Fetching package database for repository (%s)...\n", repo.Name) + } + + err := repo.SyncLocalDatabase() + if err != nil { + return err + } + } + + return nil +} From e60381beb1d923177489f952fc619adf179cc163 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Tue, 8 Apr 2025 17:41:14 +0300 Subject: [PATCH 24/88] Rename constants --- src/bpm/main.go | 6 ++--- src/bpmlib/general.go | 2 +- src/bpmlib/operations.go | 6 ++--- src/bpmlib/packages.go | 52 ++++++++++++++++++++-------------------- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index f263423..d38ef09 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -214,12 +214,12 @@ func resolveCommand() { } // Check if installationReason argument is valid - ir := bpmlib.Unknown + ir := bpmlib.InstallationReasonUnknown switch installationReason { case "manual": - ir = bpmlib.Manual + ir = bpmlib.InstallationReasonManual case "dependency": - ir = bpmlib.Dependency + ir = bpmlib.InstallationReasonDependency case "": default: log.Fatalf("Error: %s is not a valid installation reason", installationReason) diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index 57d7eb2..dfe51c9 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -185,7 +185,7 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci UnresolvedDepends: make([]string, 0), Changes: make(map[string]string), RootDir: rootDir, - ForceInstallationReason: Unknown, + ForceInstallationReason: InstallationReasonUnknown, } // Search for packages diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index 56efc48..867b61c 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -217,7 +217,7 @@ func (operation *BPMOperation) Cleanup(verbose bool) error { // Get manually installed packages, resolve all their dependencies and add them to the keepPackages slice keepPackages := make([]string, 0) for _, pkg := range slices.Clone(installedPackages) { - if GetInstallationReason(pkg.Name, operation.RootDir) != Manual { + if GetInstallationReason(pkg.Name, operation.RootDir) != InstallationReasonManual { continue } @@ -475,13 +475,13 @@ func (operation *BPMOperation) Execute(verbose, force bool) error { if err != nil { return errors.New(fmt.Sprintf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err)) } - if operation.ForceInstallationReason != Unknown && !value.IsDependency { + if operation.ForceInstallationReason != InstallationReasonUnknown && !value.IsDependency { err := SetInstallationReason(bpmpkg.PkgInfo.Name, operation.ForceInstallationReason, operation.RootDir) if err != nil { return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err)) } } else if value.IsDependency && !isReinstall { - err := SetInstallationReason(bpmpkg.PkgInfo.Name, Dependency, operation.RootDir) + err := SetInstallationReason(bpmpkg.PkgInfo.Name, InstallationReasonDependency, operation.RootDir) if err != nil { return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err)) } diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index d7008b7..abfffcc 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -74,9 +74,9 @@ func (pkgInfo *PackageInfo) GetFullVersion() string { type InstallationReason string const ( - Manual InstallationReason = "manual" - Dependency InstallationReason = "dependency" - Unknown InstallationReason = "unknown" + InstallationReasonManual InstallationReason = "manual" + InstallationReasonDependency InstallationReason = "dependency" + InstallationReasonUnknown InstallationReason = "unknown" ) func ComparePackageVersions(info1, info2 PackageInfo) int { @@ -90,19 +90,19 @@ func GetInstallationReason(pkg, rootDir string) InstallationReason { installedDir := path.Join(rootDir, "var/lib/bpm/installed/") pkgDir := path.Join(installedDir, pkg) if stat, err := os.Stat(path.Join(pkgDir, "installation_reason")); err != nil || stat.IsDir() { - return Manual + return InstallationReasonManual } b, err := os.ReadFile(path.Join(pkgDir, "installation_reason")) if err != nil { - return Unknown + return InstallationReasonUnknown } reason := strings.TrimSpace(string(b)) if reason == "manual" { - return Manual + return InstallationReasonManual } else if reason == "dependency" { - return Dependency + return InstallationReasonDependency } - return Unknown + return InstallationReasonUnknown } func SetInstallationReason(pkg string, reason InstallationReason, rootDir string) error { @@ -280,15 +280,15 @@ func ReadPackageScripts(filename string) (map[string]string, error) { return ret, nil } -type Operation uint8 +type packageOperation uint8 const ( - Install Operation = 0 - Update = 1 - Remove = 2 + packageOperationInstall packageOperation = 0 + packageOperationUpdate = 1 + packageOperationRemove = 2 ) -func executePackageScripts(filename, rootDir string, operation Operation, postOperation bool) error { +func executePackageScripts(filename, rootDir string, operation packageOperation, postOperation bool) error { pkgInfo, err := ReadPackage(filename) if err != nil { return err @@ -320,11 +320,11 @@ func executePackageScripts(filename, rootDir string, operation Operation, postOp cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", pkgInfo.PkgInfo.Name)) cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DESC=%s", pkgInfo.PkgInfo.Description)) cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", pkgInfo.PkgInfo.Version)) - if operation != Install { + if operation != packageOperationInstall { cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_OLD_VERSION=%s", GetPackageInfo(pkgInfo.PkgInfo.Name, rootDir).Version)) } cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", pkgInfo.PkgInfo.Revision)) - if operation != Install { + if operation != packageOperationInstall { cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_OLD_REVISION=%d", GetPackageInfo(pkgInfo.PkgInfo.Name, rootDir).Revision)) } cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", pkgInfo.PkgInfo.Url)) @@ -349,7 +349,7 @@ func executePackageScripts(filename, rootDir string, operation Operation, postOp return nil } - if operation == Install { + if operation == packageOperationInstall { if val, ok := scripts["pre_install.sh"]; !postOperation && ok { err := run("pre_install.sh", val) if err != nil { @@ -362,7 +362,7 @@ func executePackageScripts(filename, rootDir string, operation Operation, postOp return err } } - } else if operation == Update { + } else if operation == packageOperationUpdate { if val, ok := scripts["pre_update.sh"]; !postOperation && ok { err := run("pre_update.sh", val) if err != nil { @@ -375,7 +375,7 @@ func executePackageScripts(filename, rootDir string, operation Operation, postOp return err } } - } else if operation == Remove { + } else if operation == packageOperationRemove { if val, ok := scripts["pre_remove.sh"]; !postOperation && ok { err := run("pre_remove.sh", val) if err != nil { @@ -478,12 +478,12 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) error { if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { - err := executePackageScripts(filename, rootDir, Install, false) + err := executePackageScripts(filename, rootDir, packageOperationInstall, false) if err != nil { return err } } else { - err := executePackageScripts(filename, rootDir, Update, false) + err := executePackageScripts(filename, rootDir, packageOperationUpdate, false) if err != nil { return err } @@ -629,12 +629,12 @@ func isSplitPackage(filename string) bool { func compilePackage(bpmpkg *BPMPackage, filename, rootDir string, verbose, binaryPkgFromSrc, skipCheck, keepTempDir bool) (error, []string) { var files []string if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { - err := executePackageScripts(filename, rootDir, Install, false) + err := executePackageScripts(filename, rootDir, packageOperationInstall, false) if err != nil { return err, nil } } else { - err := executePackageScripts(filename, rootDir, Update, false) + err := executePackageScripts(filename, rootDir, packageOperationUpdate, false) if err != nil { return err, nil } @@ -747,12 +747,12 @@ func compilePackage(bpmpkg *BPMPackage, filename, rootDir string, verbose, binar } fmt.Println("Running source.sh file...") if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { - err = executePackageScripts(filename, rootDir, Install, false) + err = executePackageScripts(filename, rootDir, packageOperationInstall, false) if err != nil { return err, nil } } else { - err = executePackageScripts(filename, rootDir, Update, false) + err = executePackageScripts(filename, rootDir, packageOperationUpdate, false) if err != nil { return err, nil } @@ -1246,12 +1246,12 @@ func installPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc, } if !packageInstalled { - err = executePackageScripts(filename, rootDir, Install, true) + err = executePackageScripts(filename, rootDir, packageOperationInstall, true) if err != nil { return err } } else { - err = executePackageScripts(filename, rootDir, Update, true) + err = executePackageScripts(filename, rootDir, packageOperationUpdate, true) if err != nil { return err } From 151de2112ef18da2e7672aab82af0675ee31a229 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Tue, 8 Apr 2025 19:20:49 +0300 Subject: [PATCH 25/88] Fix virtual packages not being accounted for in GetDependants function --- src/bpmlib/packages.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index abfffcc..dbc137c 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -1308,8 +1308,21 @@ func (pkgInfo *PackageInfo) GetDependants(rootDir string) ([]string, error) { if bpmpkg == nil { return nil, errors.New("package not found: " + pkg) } - if bpmpkg.PkgInfo.Name != pkgInfo.Name && slices.Contains(bpmpkg.PkgInfo.GetAllDependencies(false, true), pkgInfo.Name) { + if bpmpkg.PkgInfo.Name == pkgInfo.Name { + continue + } + + dependencies := bpmpkg.PkgInfo.GetAllDependencies(false, true) + + if slices.Contains(dependencies, pkgInfo.Name) { ret = append(ret, pkg) + continue + } + for _, vpkg := range pkgInfo.Provides { + if slices.Contains(dependencies, vpkg) { + ret = append(ret, pkg) + break + } } } From d1a2d28cff6672e46d7722cc9acf85f6a78902ff Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 10 Apr 2025 13:06:18 +0300 Subject: [PATCH 26/88] Fix some error handling and comments --- src/bpm/main.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index d38ef09..8cb250f 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -235,8 +235,11 @@ func resolveCommand() { reinstallMethod = bpmlib.ReinstallMethodNone } - // Create Installation Operation + // Create installation operation operation, err := bpmlib.InstallPackages(rootDir, ir, reinstallMethod, !noOptional, force, verbose, subcommandArgs...) + if err != nil { + log.Fatalf("Error: could not setup operation: %s\n", err) + } // Show operation summary operation.ShowOperationSummary() @@ -275,10 +278,10 @@ func resolveCommand() { log.Fatalf("Error: this subcommand needs to be run with superuser permissions") } - // Create Update Operation + // Create update operation operation, err := bpmlib.UpdatePackages(rootDir, !nosync, !noOptional, force, verbose) if err != nil { - log.Fatalf("Error: could not update packages: %s\n", err) + log.Fatalf("Error: could not setuo operation: %s\n", err) } // Show operation summary @@ -324,6 +327,7 @@ func resolveCommand() { } } + // Sync databases err := bpmlib.SyncDatabase(verbose) if err != nil { log.Fatalf("Error: could not sync local database: %s\n", err) @@ -341,10 +345,10 @@ func resolveCommand() { return } - // Remove packages + // Create remove operation operation, err := bpmlib.RemovePackages(rootDir, removeUnused, doCleanup, verbose, subcommandArgs...) if err != nil { - log.Fatalf("Error: could not remove packages: %s\n", err) + log.Fatalf("Error: could not setup operation: %s\n", err) } // Show operation summary @@ -379,10 +383,10 @@ func resolveCommand() { log.Fatalf("Error: this subcommand needs to be run with superuser permissions") } - // Do cleanup + // Create cleanup operation operation, err := bpmlib.CleanupPackages(rootDir, verbose) if err != nil { - log.Fatalf("Error: could not cleanup packages: %s\n", err) + log.Fatalf("Error: could not setup operation: %s\n", err) } // Show operation summary From 14fe71842efd19d509550b53dd7c5d97aa569020 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 10 Apr 2025 14:38:32 +0300 Subject: [PATCH 27/88] Added custom error types --- src/bpm/main.go | 19 ++++++++++++++----- src/bpmlib/errors.go | 31 +++++++++++++++++++++++++++++++ src/bpmlib/general.go | 33 ++++++++++++++++++++------------- 3 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 src/bpmlib/errors.go diff --git a/src/bpm/main.go b/src/bpm/main.go index 8cb250f..1b6a3d1 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "errors" "flag" "fmt" "git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib" @@ -237,7 +238,9 @@ func resolveCommand() { // Create installation operation operation, err := bpmlib.InstallPackages(rootDir, ir, reinstallMethod, !noOptional, force, verbose, subcommandArgs...) - if err != nil { + if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { + log.Fatalf("Error: %s", err) + } else if err != nil { log.Fatalf("Error: could not setup operation: %s\n", err) } @@ -280,8 +283,10 @@ func resolveCommand() { // Create update operation operation, err := bpmlib.UpdatePackages(rootDir, !nosync, !noOptional, force, verbose) - if err != nil { - log.Fatalf("Error: could not setuo operation: %s\n", err) + if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { + log.Fatalf("Error: %s", err) + } else if err != nil { + log.Fatalf("Error: could not setup operation: %s\n", err) } // Show operation summary @@ -347,7 +352,9 @@ func resolveCommand() { // Create remove operation operation, err := bpmlib.RemovePackages(rootDir, removeUnused, doCleanup, verbose, subcommandArgs...) - if err != nil { + if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { + log.Fatalf("Error: %s", err) + } else if err != nil { log.Fatalf("Error: could not setup operation: %s\n", err) } @@ -385,7 +392,9 @@ func resolveCommand() { // Create cleanup operation operation, err := bpmlib.CleanupPackages(rootDir, verbose) - if err != nil { + if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { + log.Fatalf("Error: %s", err) + } else if err != nil { log.Fatalf("Error: could not setup operation: %s\n", err) } diff --git a/src/bpmlib/errors.go b/src/bpmlib/errors.go new file mode 100644 index 0000000..b9d193e --- /dev/null +++ b/src/bpmlib/errors.go @@ -0,0 +1,31 @@ +package bpmlib + +import ( + "fmt" + "strings" +) + +type PackageNotFoundErr struct { + packages []string +} + +func (e PackageNotFoundErr) Error() string { + return "The following packages were not found in any repositories: " + strings.Join(e.packages, ", ") +} + +type DependencyNotFoundErr struct { + dependencies []string +} + +func (e DependencyNotFoundErr) Error() string { + return "The following dependencies were not found in any repositories: " + strings.Join(e.dependencies, ", ") +} + +type PackageConflictErr struct { + pkg string + conflicts []string +} + +func (e PackageConflictErr) Error() string { + return fmt.Sprintf("Package (%s) is in conflict with the following packages: %s", e.pkg, strings.Join(e.conflicts, ", ")) +} diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index dfe51c9..1d5ee80 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -1,11 +1,11 @@ package bpmlib import ( + "errors" "fmt" "log" "os" "slices" - "strings" ) type ReinstallMethod uint8 @@ -28,6 +28,7 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein } // Resolve packages + pkgsNotFound := make([]string, 0) for _, pkg := range packages { if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() { bpmpkg, err := ReadPackage(pkg) @@ -50,12 +51,14 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein } else if isVirtual, p := IsVirtualPackage(pkg, rootDir); isVirtual { entry, _, err = GetRepositoryEntry(p) if err != nil { - return nil, fmt.Errorf("could not find package (%s) in any repositor", p) + pkgsNotFound = append(pkgsNotFound, pkg) + continue } } else if e := ResolveVirtualPackage(pkg); e != nil { entry = e } else { - return nil, fmt.Errorf("could not find package (%s) in any repository", pkg) + pkgsNotFound = append(pkgsNotFound, pkg) + continue } if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(entry.Info.Name, rootDir) && GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() { continue @@ -67,6 +70,11 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein } } + // Return error if not all packages are found + if len(pkgsNotFound) != 0 { + return nil, PackageNotFoundErr{pkgsNotFound} + } + // Resolve dependencies err = operation.ResolveDependencies(reinstallMethod == ReinstallMethodAll, installOptionalDependencies, verbose) if err != nil { @@ -74,9 +82,9 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein } if len(operation.UnresolvedDepends) != 0 { if !forceInstallation { - return nil, fmt.Errorf("dependencies (%s) could not be found in any repositories", strings.Join(operation.UnresolvedDepends, ", ")) + return nil, DependencyNotFoundErr{operation.UnresolvedDepends} } else if verbose { - log.Println("Warning: The following dependencies could not be found in any repositories: " + strings.Join(operation.UnresolvedDepends, ", ")) + log.Printf("Warning: %s", DependencyNotFoundErr{operation.UnresolvedDepends}) } } @@ -89,15 +97,14 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein return nil, fmt.Errorf("could not complete package conflict check: %s", err) } if len(conflicts) > 0 { - if verbose { - for pkg, conflict := range conflicts { - fmt.Printf("%s is in conflict with packages (%s)\n", pkg, strings.Join(conflict, ", ")) - } + err = nil + for pkg, conflict := range conflicts { + err = errors.Join(err, PackageConflictErr{pkg, conflict}) } if !forceInstallation { - return nil, fmt.Errorf("conflicting packages found") + return nil, err } else { - log.Println("Warning: conflicting packages found") + log.Printf("Warning: %s", err) } } @@ -222,9 +229,9 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci } if len(operation.UnresolvedDepends) != 0 { if !forceInstallation { - return nil, fmt.Errorf("dependencies (%s) could not be found in any repositories", strings.Join(operation.UnresolvedDepends, ", ")) + return nil, DependencyNotFoundErr{operation.UnresolvedDepends} } else if verbose { - log.Printf("Warning: dependencies (%s) could not be found in any repositories\n", strings.Join(operation.UnresolvedDepends, ", ")) + log.Printf("Warning: %s", DependencyNotFoundErr{operation.UnresolvedDepends}) } } From 3f2d5cf7a15582d90b5b20e6c7f536b111ca0df6 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 10 Apr 2025 14:54:41 +0300 Subject: [PATCH 28/88] Remove all log.Fatal and os.Exit calls from bpmlib --- src/bpm/main.go | 29 ++++++++++++++++++++++++++++- src/bpmlib/config.go | 19 ++++++++++++------- src/bpmlib/general.go | 5 ++++- src/bpmlib/operations.go | 9 +++------ 4 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index 1b6a3d1..702e950 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -43,7 +43,10 @@ var doCleanup = false var showRepoInfo = false func main() { - bpmlib.ReadConfig() + err := bpmlib.ReadConfig() + if err != nil { + log.Fatalf("Error: could not read BPM config: %s", err) + } resolveFlags() resolveCommand() } @@ -244,6 +247,12 @@ func resolveCommand() { log.Fatalf("Error: could not setup operation: %s\n", err) } + // Exit if operation contains no actions + if len(operation.Actions) == 0 { + fmt.Println("No action needs to be taken") + return + } + // Show operation summary operation.ShowOperationSummary() @@ -289,6 +298,12 @@ func resolveCommand() { log.Fatalf("Error: could not setup operation: %s\n", err) } + // Exit if operation contains no actions + if len(operation.Actions) == 0 { + fmt.Println("No action needs to be taken") + return + } + // Show operation summary operation.ShowOperationSummary() @@ -358,6 +373,12 @@ func resolveCommand() { log.Fatalf("Error: could not setup operation: %s\n", err) } + // Exit if operation contains no actions + if len(operation.Actions) == 0 { + fmt.Println("No action needs to be taken") + return + } + // Show operation summary operation.ShowOperationSummary() @@ -398,6 +419,12 @@ func resolveCommand() { log.Fatalf("Error: could not setup operation: %s\n", err) } + // Exit if operation contains no actions + if len(operation.Actions) == 0 { + fmt.Println("No action needs to be taken") + return + } + // Show operation summary operation.ShowOperationSummary() diff --git a/src/bpmlib/config.go b/src/bpmlib/config.go index 18e03c2..8c3e50a 100644 --- a/src/bpmlib/config.go +++ b/src/bpmlib/config.go @@ -2,7 +2,6 @@ package bpmlib import ( "gopkg.in/yaml.v3" - "log" "os" ) @@ -17,14 +16,16 @@ type BPMConfigStruct struct { var BPMConfig BPMConfigStruct -func ReadConfig() { - if _, err := os.Stat("/etc/bpm.conf"); os.IsNotExist(err) { - log.Fatal(err) +func ReadConfig() (err error) { + if _, err = os.Stat("/etc/bpm.conf"); os.IsNotExist(err) { + return err } + bytes, err := os.ReadFile("/etc/bpm.conf") if err != nil { - log.Fatal(err) + return err } + BPMConfig = BPMConfigStruct{ CompilationEnv: make([]string, 0), SilentCompilation: false, @@ -33,19 +34,23 @@ func ReadConfig() { } err = yaml.Unmarshal(bytes, &BPMConfig) if err != nil { - log.Fatal(err) + return err } + for i := len(BPMConfig.Repositories) - 1; i >= 0; i-- { if BPMConfig.Repositories[i].Disabled != nil && *BPMConfig.Repositories[i].Disabled { BPMConfig.Repositories = append(BPMConfig.Repositories[:i], BPMConfig.Repositories[i+1:]...) } } + for _, repo := range BPMConfig.Repositories { repo.Entries = make(map[string]*RepositoryEntry) repo.VirtualPackages = make(map[string][]string) err := repo.ReadLocalDatabase() if err != nil { - log.Fatal(err) + return err } } + + return nil } diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index 1d5ee80..c88ac32 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -179,7 +179,10 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci } // Reload config and local databases - ReadConfig() + err = ReadConfig() + if err != nil { + return nil, fmt.Errorf("could not read BPM config: %s", err) + } // Get installed packages and check for updates pkgs, err := GetInstalledPackages(rootDir) diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index 867b61c..a23acae 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -195,7 +195,7 @@ func (operation *BPMOperation) Cleanup(verbose bool) error { // Get all installed packages installedPackageNames, err := GetInstalledPackages(operation.RootDir) if err != nil { - log.Fatalf("Error: could not get installed packages: %s\n", err) + return fmt.Errorf("could not get installed packages: %s", err) } installedPackages := make([]*PackageInfo, len(installedPackageNames)) for i, value := range installedPackageNames { @@ -329,8 +329,8 @@ func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error) func (operation *BPMOperation) ShowOperationSummary() { if len(operation.Actions) == 0 { - fmt.Println("All packages are up to date!") - os.Exit(0) + fmt.Println("No action needs to be taken") + return } for _, value := range operation.Actions { @@ -348,9 +348,6 @@ func (operation *BPMOperation) ShowOperationSummary() { installedInfo := GetPackageInfo(pkgInfo.Name, operation.RootDir) sourceInfo := "" if pkgInfo.Type == "source" { - if operation.RootDir != "/" { - log.Fatalf("cannot compile and install source packages to a different root directory") - } sourceInfo = "(From Source)" } From c767f269375cc98422ba23516a987a4ba5c4a5ce Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 10 Apr 2025 14:59:41 +0300 Subject: [PATCH 29/88] Remove direct source compilation (To be reworked) --- src/bpm/main.go | 11 - src/bpmlib/config.go | 15 +- src/bpmlib/operations.go | 4 +- src/bpmlib/packages.go | 471 +-------------------------------------- 4 files changed, 7 insertions(+), 494 deletions(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index 702e950..45349f3 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -27,9 +27,6 @@ var subcommandArgs []string var rootDir = "/" var verbose = false var yesAll = false -var buildSource = false -var skipCheck = false -var keepTempDir = false var force = false var pkgListNumbers = false var pkgListNames = false @@ -524,9 +521,6 @@ func printHelp() { fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -y skips the confirmation prompt") fmt.Println(" -f skips dependency, conflict and architecture checking") - fmt.Println(" -o= set the binary package output directory (defaults to /var/lib/bpm/compiled)") - fmt.Println(" -c= set the compilation directory (defaults to /var/tmp)") - fmt.Println(" -b creates a binary package from a source package after compilation and saves it in the binary package output directory") fmt.Println(" -k keeps the compilation directory created by BPM after source package installation") fmt.Println(" --reinstall Reinstalls packages even if they do not have a newer version available") fmt.Println(" --reinstall-all Same as --reinstall but also reinstalls dependencies") @@ -575,11 +569,6 @@ func resolveFlags() { installFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root") installFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing") installFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts") - installFlagSet.StringVar(&bpmlib.BPMConfig.BinaryOutputDir, "o", bpmlib.BPMConfig.BinaryOutputDir, "Set the binary output directory") - installFlagSet.StringVar(&bpmlib.BPMConfig.CompilationDir, "c", bpmlib.BPMConfig.CompilationDir, "Set the compilation directory") - installFlagSet.BoolVar(&buildSource, "b", false, "Build binary package from source package") - installFlagSet.BoolVar(&skipCheck, "s", false, "Skip check function during source compilation") - installFlagSet.BoolVar(&keepTempDir, "k", false, "Keep temporary directory after source compilation") installFlagSet.BoolVar(&force, "f", false, "Force installation by skipping architecture and dependency resolution") installFlagSet.BoolVar(&reinstall, "reinstall", false, "Reinstalls packages even if they do not have a newer version available") installFlagSet.BoolVar(&reinstallAll, "reinstall-all", false, "Same as --reinstall but also reinstalls dependencies") diff --git a/src/bpmlib/config.go b/src/bpmlib/config.go index 8c3e50a..fdbf246 100644 --- a/src/bpmlib/config.go +++ b/src/bpmlib/config.go @@ -6,12 +6,8 @@ import ( ) type BPMConfigStruct struct { - CompilationEnv []string `yaml:"compilation_env"` - SilentCompilation bool `yaml:"silent_compilation"` - BinaryOutputDir string `yaml:"binary_output_dir"` - CompilationDir string `yaml:"compilation_dir"` - IgnorePackages []string `yaml:"ignore_packages"` - Repositories []*Repository `yaml:"repositories"` + IgnorePackages []string `yaml:"ignore_packages"` + Repositories []*Repository `yaml:"repositories"` } var BPMConfig BPMConfigStruct @@ -26,12 +22,7 @@ func ReadConfig() (err error) { return err } - BPMConfig = BPMConfigStruct{ - CompilationEnv: make([]string, 0), - SilentCompilation: false, - BinaryOutputDir: "/var/lib/bpm/compiled/", - CompilationDir: "/var/tmp/", - } + BPMConfig = BPMConfigStruct{} err = yaml.Unmarshal(bytes, &BPMConfig) if err != nil { return err diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index a23acae..cb56419 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -465,9 +465,9 @@ func (operation *BPMOperation) Execute(verbose, force bool) error { isReinstall := IsPackageInstalled(bpmpkg.PkgInfo.Name, operation.RootDir) var err error if value.IsDependency { - err = installPackage(value.File, operation.RootDir, verbose, true, false, false, false) + err = installPackage(value.File, operation.RootDir, verbose, true) } else { - err = installPackage(value.File, operation.RootDir, verbose, force, false, false, false) + err = installPackage(value.File, operation.RootDir, verbose, force) } if err != nil { return errors.New(fmt.Sprintf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err)) diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index dbc137c..91ebf5a 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -8,16 +8,13 @@ import ( version "github.com/knqyf263/go-rpm-version" "gopkg.in/yaml.v3" "io" - "io/fs" "os" "os/exec" "path" - "path/filepath" "slices" "sort" "strconv" "strings" - "syscall" ) type BPMPackage struct { @@ -309,11 +306,6 @@ func executePackageScripts(filename, rootDir string, operation packageOperation, } cmd := exec.Command("/bin/bash", temp.Name()) - if !BPMConfig.SilentCompilation { - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - } cmd.Dir = rootDir cmd.Env = os.Environ() cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir)) @@ -433,14 +425,6 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) { return &pkgInfo, nil } -func CreateInfoFile(pkgInfo *PackageInfo) string { - b, err := yaml.Marshal(&pkgInfo) - if err != nil { - return "" - } - return string(b) -} - func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, pkgInfo *PackageInfo, rootDir string) string { ret := make([]string, 0) appendArray := func(label string, array []string) { @@ -611,447 +595,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) return nil } -func isSplitPackage(filename string) bool { - pkgInfo, err := ReadPackage(filename) - if err != nil { - return false - } - if pkgInfo.PkgInfo.Type != "source" { - return false - } - cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("test $(tar -tf %s | grep '^pkg.info' | wc -l) -eq 1", filename)) - if err := cmd.Run(); err == nil { - return false - } - return true -} - -func compilePackage(bpmpkg *BPMPackage, filename, rootDir string, verbose, binaryPkgFromSrc, skipCheck, keepTempDir bool) (error, []string) { - var files []string - if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { - err := executePackageScripts(filename, rootDir, packageOperationInstall, false) - if err != nil { - return err, nil - } - } else { - err := executePackageScripts(filename, rootDir, packageOperationUpdate, false) - if err != nil { - return err, nil - } - } - //seenHardlinks := make(map[string]string) - file, err := os.Open(filename) - if err != nil { - return err, nil - } - - tr := tar.NewReader(file) - - temp := path.Join(BPMConfig.CompilationDir, "bpm_source-"+bpmpkg.PkgInfo.Name) - err = os.RemoveAll(temp) - if err != nil { - return err, nil - } - if verbose { - fmt.Println("Creating temp directory at: " + temp) - } - err = os.Mkdir(temp, 0755) - if err != nil { - return err, nil - } - err = os.Chown(temp, 65534, 65534) - if err != nil { - return err, nil - } - - for { - header, err := tr.Next() - if err == io.EOF { - break - } - if err != nil { - return err, nil - } - if strings.HasPrefix(header.Name, "source-files/") && header.Name != "source-files/" { - extractFilename := path.Join(temp, strings.TrimPrefix(header.Name, "source-files/")) - switch header.Typeflag { - case tar.TypeDir: - if err := os.Mkdir(extractFilename, 0755); err != nil { - if !os.IsExist(err) { - return err, nil - } - } else { - if verbose { - fmt.Println("Creating Directory: " + extractFilename) - } - err = os.Chown(extractFilename, 65534, 65534) - if err != nil { - return err, nil - } - } - case tar.TypeReg: - err := os.Remove(extractFilename) - if err != nil && !os.IsNotExist(err) { - return err, nil - } - outFile, err := os.Create(extractFilename) - if verbose { - fmt.Println("Creating File: " + extractFilename) - } - if err != nil { - return err, nil - } - err = os.Chown(extractFilename, 65534, 65534) - if err != nil { - return err, nil - } - if _, err := io.Copy(outFile, tr); err != nil { - return err, nil - } - if err := os.Chmod(extractFilename, header.FileInfo().Mode()); err != nil { - return err, nil - } - err = outFile.Close() - if err != nil { - return err, nil - } - case tar.TypeSymlink: - if verbose { - fmt.Println("Skipping symlink (Bundling symlinks in source packages is not supported)") - } - case tar.TypeLink: - if verbose { - fmt.Println("Skipping hard link (Bundling hard links in source packages is not supported)") - } - default: - return errors.New("unknown type (" + strconv.Itoa(int(header.Typeflag)) + ") in " + extractFilename), nil - } - } - if header.Name == "source.sh" { - bs, err := io.ReadAll(tr) - if err != nil { - return err, nil - } - err = os.WriteFile(path.Join(temp, "source.sh"), bs, 0644) - if err != nil { - return err, nil - } - err = os.Chown(path.Join(temp, "source.sh"), 65534, 65534) - if err != nil { - return err, nil - } - } - } - if _, err := os.Stat(path.Join(temp, "source.sh")); os.IsNotExist(err) { - return errors.New("source.sh file could not be found in the temporary build directory"), nil - } - fmt.Println("Running source.sh file...") - if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { - err = executePackageScripts(filename, rootDir, packageOperationInstall, false) - if err != nil { - return err, nil - } - } else { - err = executePackageScripts(filename, rootDir, packageOperationUpdate, false) - if err != nil { - return err, nil - } - } - bs, err := os.ReadFile(path.Join(temp, "source.sh")) - if err != nil { - return err, nil - } - - if !strings.Contains(string(bs), "package()") { - fmt.Print("This package does not seem to have the required 'package' function\nThe source.sh file may have been created for an older BPM version\nPlease update the source.sh file") - return errors.New("invalid source.sh format"), nil - } - - runScript := ` -cd "$BPM_WORKDIR" - -set -a -source "source.sh" -set +a - -if [[ $(type -t prepare) == function ]]; then - echo "Running prepare() function..." - bash -e -c prepare - if [ $? -ne 0 ]; then - echo "Failed to run prepare() function in source.sh" - exit 1 - fi -fi - -cd "$BPM_SOURCE" -if [[ $(type -t build) == function ]]; then - echo "Running build() function..." - bash -e -c build - if [ $? -ne 0 ]; then - echo "Failed to run build() function in source.sh" - exit 1 - fi -fi - -cd "$BPM_SOURCE" -if [[ $(type -t check) == function ]] && [ -z "$SKIPCHECK" ]; then - echo "Running check() function..." - bash -e -c check - if [ $? -ne 0 ]; then - echo "Failed to run check() function in source.sh" - exit 1 - fi -fi - - -cd "$BPM_SOURCE" -if ! [[ $(type -t package) == function ]]; then - echo "Failed to locate package() function in source.sh" - exit 1 -fi -echo "Running package() function..." -touch "$BPM_WORKDIR"/fakeroot_file -fakeroot -s "$BPM_WORKDIR"/fakeroot_file bash -e -c package -bash -e -c package -if [ $? -ne 0 ]; then - echo "Failed to run package() function in source.sh" -fi -` - err = os.WriteFile(path.Join(temp, "run.sh"), []byte(runScript), 0644) - if err != nil { - return err, nil - } - err = os.Chown(path.Join(temp, "run.sh"), 65534, 65534) - if err != nil { - return err, nil - } - - cmd := exec.Command("/bin/bash", "-e", "run.sh") - cmd.SysProcAttr = &syscall.SysProcAttr{} - cmd.SysProcAttr.Credential = &syscall.Credential{Uid: 65534, Gid: 65534} - cmd.Dir = temp - cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, "USER=nobody") - cmd.Env = append(cmd.Env, "HOME="+temp) - - err = os.Mkdir(path.Join(temp, "source"), 0755) - if err != nil { - return err, nil - } - err = os.Chown(path.Join(temp, "source"), 65534, 65534) - if err != nil { - return err, nil - } - err = os.Mkdir(path.Join(temp, "output"), 0755) - if err != nil { - return err, nil - } - err = os.Chown(path.Join(temp, "output"), 65534, 65534) - if err != nil { - return err, nil - } - cmd.Env = append(cmd.Env, "BPM_WORKDIR="+temp) - cmd.Env = append(cmd.Env, "BPM_SOURCE="+path.Join(temp, "source")) - cmd.Env = append(cmd.Env, "BPM_OUTPUT="+path.Join(temp, "output")) - cmd.Env = append(cmd.Env, "SKIPCHECK="+strconv.FormatBool(skipCheck)) - - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", bpmpkg.PkgInfo.Name)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DESC=%s", bpmpkg.PkgInfo.Description)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", bpmpkg.PkgInfo.Version)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", bpmpkg.PkgInfo.Revision)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", bpmpkg.PkgInfo.Url)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", bpmpkg.PkgInfo.Arch)) - depends := make([]string, len(bpmpkg.PkgInfo.Depends)) - copy(depends, bpmpkg.PkgInfo.Depends) - for i := 0; i < len(depends); i++ { - depends[i] = fmt.Sprintf("\"%s\"", depends[i]) - } - makeDepends := make([]string, len(bpmpkg.PkgInfo.MakeDepends)) - copy(makeDepends, bpmpkg.PkgInfo.MakeDepends) - for i := 0; i < len(makeDepends); i++ { - makeDepends[i] = fmt.Sprintf("\"%s\"", makeDepends[i]) - } - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DEPENDS=(%s)", strings.Join(depends, " "))) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_MAKE_DEPENDS=(%s)", strings.Join(makeDepends, " "))) - for _, value := range BPMConfig.CompilationEnv { - cmd.Env = append(cmd.Env, value) - } - cmd.Env = append(cmd.Env, "BPM_PKG_TYPE=source") - - if !BPMConfig.SilentCompilation { - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - } - err = cmd.Run() - if err != nil { - return err, nil - } - if _, err := os.Stat(path.Join(temp, "output/")); err != nil { - if os.IsNotExist(err) { - return errors.New("output directory not be found at " + path.Join(temp, "output/")), nil - } - return err, nil - } - if dir, _ := os.ReadDir(path.Join(temp, "output/")); len(dir) == 0 { - return errors.New("output directory is empty"), nil - } - fmt.Println("Copying all files...") - err = filepath.WalkDir(path.Join(temp, "/output/"), func(fullpath string, d fs.DirEntry, err error) error { - relFilename, err := filepath.Rel(path.Join(temp, "/output/"), fullpath) - if relFilename == "." { - return nil - } - extractFilename := path.Join(rootDir, relFilename) - if err != nil { - return err - } - if d.Type() == os.ModeDir { - files = append(files, relFilename+"/") - if err := os.Mkdir(extractFilename, 0755); err != nil { - if !os.IsExist(err) { - return err - } - } else { - if verbose { - fmt.Println("Creating Directory: " + extractFilename) - } - } - } else if d.Type().IsRegular() { - if _, err := os.Stat(extractFilename); err == nil { - if slices.Contains(bpmpkg.PkgInfo.Keep, relFilename) { - if verbose { - fmt.Println("Skipping File: " + extractFilename + "(File is configured to be kept during installs/updates)") - } - files = append(files, relFilename) - return nil - } - } - err := os.Remove(extractFilename) - if err != nil && !os.IsNotExist(err) { - return err - } - outFile, err := os.Create(extractFilename) - if verbose { - fmt.Println("Creating File: " + extractFilename) - } - files = append(files, relFilename) - if err != nil { - return err - } - f, err := os.Open(fullpath) - if err != nil { - return err - } - if _, err := io.Copy(outFile, f); err != nil { - return err - } - info, err := os.Stat(fullpath) - if err != nil { - return err - } - if err := os.Chmod(extractFilename, info.Mode()); err != nil { - return err - } - err = outFile.Close() - if err != nil { - return err - } - err = f.Close() - if err != nil { - return err - } - } else if d.Type() == os.ModeSymlink { - link, err := os.Readlink(fullpath) - if err != nil { - return err - } - err = os.Remove(extractFilename) - if err != nil && !os.IsNotExist(err) { - return err - } - if verbose { - fmt.Println("Creating Symlink: "+extractFilename, " -> "+link) - } - files = append(files, relFilename) - err = os.Symlink(link, extractFilename) - if err != nil { - return err - } - } - return nil - }) - if err != nil { - return err, nil - } - if binaryPkgFromSrc { - compiledDir := path.Join(BPMConfig.BinaryOutputDir) - err = os.MkdirAll(compiledDir, 0755) - compiledInfo := PackageInfo{} - compiledInfo = *bpmpkg.PkgInfo - compiledInfo.Type = "binary" - compiledInfo.Arch = GetArch() - err = os.WriteFile(path.Join(temp, "pkg.info"), []byte(CreateInfoFile(&compiledInfo)), 0644) - if err != nil { - return err, nil - } - err = os.Chown(path.Join(temp, "pkg.info"), 65534, 65534) - if err != nil { - return err, nil - } - scripts, err := ReadPackageScripts(filename) - for key, val := range scripts { - err = os.WriteFile(path.Join(temp, key), []byte(val), 0644) - if err != nil { - return err, nil - } - err = os.Chown(path.Join(temp, key), 65534, 65534) - if err != nil { - return err, nil - } - } - sed := fmt.Sprintf("s/output/files/") - fileName := compiledInfo.Name + "-" + compiledInfo.GetFullVersion() + "-" + compiledInfo.Arch + ".bpm" - cmd := exec.Command("/usr/bin/fakeroot", "-i fakeroot_file", "tar", "-czvpf", fileName, "pkg.info", "output/", "--transform", sed) - if !BPMConfig.SilentCompilation { - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - } - cmd.SysProcAttr = &syscall.SysProcAttr{} - cmd.SysProcAttr.Credential = &syscall.Credential{Uid: 65534, Gid: 65534} - cmd.Dir = temp - cmd.Env = os.Environ() - fmt.Printf("running command: %s\n", strings.Join(cmd.Args, " ")) - err = cmd.Run() - if err != nil { - return err, nil - } - err = copyFileContents(path.Join(temp, fileName), path.Join(compiledDir, fileName)) - if err != nil { - return err, nil - } - err = os.Chown(path.Join(compiledDir, fileName), 0, 0) - if err != nil { - return err, nil - } - } - if !keepTempDir { - err := os.RemoveAll(temp) - if err != nil { - return err, nil - } - } - if len(files) == 0 { - return errors.New("no output files for source package. Cancelling package installation"), nil - } - - defer file.Close() - return nil, files -} - -func installPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc, skipCheck, keepTempDir bool) error { +func installPackage(filename, rootDir string, verbose, force bool) error { if _, err := os.Stat(filename); os.IsNotExist(err) { return err } @@ -1170,13 +714,7 @@ func installPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc, return err } } else if bpmpkg.PkgInfo.Type == "source" { - if isSplitPackage(filename) { - return errors.New("BPM is unable to install split source packages") - } - err, _ := compilePackage(bpmpkg, filename, rootDir, verbose, binaryPkgFromSrc, skipCheck, keepTempDir) - if err != nil { - return err - } + return errors.New("direct source package compilation in BPM has been temporarily removed and is being reworked on") } else { return errors.New("unknown package type: " + bpmpkg.PkgInfo.Type) } @@ -1649,11 +1187,6 @@ func removePackage(pkg string, verbose bool, rootDir string) error { // Executing post_remove script if _, err := os.Stat(path.Join(pkgDir, "post_remove.sh")); err == nil { cmd := exec.Command("/bin/bash", path.Join(pkgDir, "post_remove.sh")) - if !BPMConfig.SilentCompilation { - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - } cmd.Dir = rootDir cmd.Env = os.Environ() cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir)) From 2f8d6a7ea9e2230cfbd5e73e7719eb827b484c4a Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 10 Apr 2025 15:08:27 +0300 Subject: [PATCH 30/88] Add missing dashes in the printHelp function --- src/bpm/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index 45349f3..7265eb9 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -541,8 +541,8 @@ func printHelp() { fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -y skips the confirmation prompt") - fmt.Println(" -unused removes only packages that aren't required as dependencies by other packages") - fmt.Println(" -cleanup performs a dependency cleanup") + fmt.Println(" --unused removes only packages that aren't required as dependencies by other packages") + fmt.Println(" --cleanup performs a dependency cleanup") fmt.Println("-> bpm cleanup [-R, -v, -y] | remove all unused dependency packages") fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -R= lets you define the root path which will be used") From 46f7d96acf2d90e9fc7aa7e3d3228a778238ae03 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 10 Apr 2025 15:29:44 +0300 Subject: [PATCH 31/88] Update README.md --- README.md | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 637e170..a15ab6c 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,10 @@ BPM is a simple package manager for Linux systems ## Features - Simple to use subcommands -- Can install binary and source packages -- Can be easily installed on practically any system -- No bloat +- Can install binary packages (and source packages in the future) ## Information - -BPM is still in very early development. It should not be installed on any system you use seriously. I recommend trying this out in a VM or container. In addition to this, this is one of the first projects I have made using the go programming language so code quality may not be the best. This project was made to help me learn go and how linux systems work better. It is not meant to replace the big package managers in any way +BPM is still in very early development. Do not install it without knowing what you are doing. I would only recommend using it in a Virtual Machine for testing ## Build from source @@ -21,7 +18,7 @@ BPM is still in very early development. It should not be installed on any system ``` make ``` -- Run the following command to install stormfetch into your system. You may also append a DESTDIR variable at the end of this line if you wish to install in a different location +- Run the following command to install BPM into your system. You may also append a DESTDIR variable at the end of this line if you wish to install in a different location ``` make install PREFIX=/usr SYSCONFDIR=/etc ``` @@ -32,17 +29,30 @@ You are able to install bpm packages by typing the following: ```sh bpm install /path/to/package.bpm ``` -You may also use the -y flag as shown below to bypass the installation confirmation prompt +You can also use the package name directly if using repositories +```sh +bpm install package_name +``` +The -y flag may be used as shown below to bypass the confirmation prompt ```sh bpm install -y /path/to/package.bpm ``` -Flags must strictly be typed before the first package path otherwise they'll be read as package locations themselves +Flags must strictly be typed before the first package path or name, otherwise they'll be read as package locations themselves You can remove an installed package by typing the following ```sh bpm remove package_name ``` -The -y flag applies here as well if you wish to skip the removal confirmation prompt + +To remove all unused dependencies try using the cleanup command +```sh +bpm cleanup +``` + +If using repositories, all packages can be updated using this simple command +```sh +bpm update +``` For information on the rest of the commands simply use the help command or pass in no arguments at all ``` @@ -51,5 +61,6 @@ bpm help ## Package Creation -Package creation is simplified using the bpm-utils package which contains helper scripts for creating and archiving packages. \ -Learn more here: https://gitlab.com/bubble-package-manager/bpm-utils \ No newline at end of file +Package creation is simplified using the bpm-utils package which contains helper scripts for creating and archiving packages + +Learn more here: https://git.enumerated.dev/bubble-package-manager/bpm-utils \ No newline at end of file From fa3f59d8ba9b923bc447f811dde3bbdd4f3c4996 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 10 Apr 2025 15:31:36 +0300 Subject: [PATCH 32/88] Update bpm.conf --- config/bpm.conf | 4 ---- 1 file changed, 4 deletions(-) diff --git a/config/bpm.conf b/config/bpm.conf index 6b92f57..1ca6fb9 100644 --- a/config/bpm.conf +++ b/config/bpm.conf @@ -1,7 +1,3 @@ -compilation_env: [] -silent_compilation: false -compilation_dir: "/var/tmp/" -binary_output_dir: "/var/lib/bpm/compiled/" repositories: - name: example-repository source: https://my-repo.xyz/ From dd41369e057c38fca299ef3e12fb41c326a6564e Mon Sep 17 00:00:00 2001 From: EnumDev Date: Wed, 16 Apr 2025 18:49:54 +0300 Subject: [PATCH 33/88] Fix error when hooks directory does not exist --- src/bpmlib/operations.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index cb56419..f072759 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -379,6 +379,11 @@ func (operation *BPMOperation) ShowOperationSummary() { } func (operation *BPMOperation) RunHooks(verbose bool) error { + // Return if hooks directory does not exist + if stat, err := os.Stat(path.Join(operation.RootDir, "var/lib/bpm/hooks")); err != nil || !stat.IsDir() { + return nil + } + // Get directory entries in hooks directory dirEntries, err := os.ReadDir(path.Join(operation.RootDir, "var/lib/bpm/hooks")) if err != nil { From f74372f13b50a80e1b0218f2272fc192df43d20f Mon Sep 17 00:00:00 2001 From: EnumDev Date: Wed, 16 Apr 2025 19:02:55 +0300 Subject: [PATCH 34/88] Add 'ignore_packages' field to default config --- config/bpm.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/config/bpm.conf b/config/bpm.conf index 1ca6fb9..3e8e4ae 100644 --- a/config/bpm.conf +++ b/config/bpm.conf @@ -1,3 +1,4 @@ +ignore_packages: [] repositories: - name: example-repository source: https://my-repo.xyz/ From 78dce34b6424115e6c5f2ba15f43b760d1b5a678 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Wed, 16 Apr 2025 20:08:24 +0300 Subject: [PATCH 35/88] Hide make dependencies when reading info of binary packages --- src/bpmlib/packages.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index 91ebf5a..da57201 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -446,7 +446,9 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p } if showPackageRelations { appendArray("Dependencies", pkgInfo.Depends) - appendArray("Make Dependencies", pkgInfo.MakeDepends) + if pkgInfo.Type == "source" { + appendArray("Make Dependencies", pkgInfo.MakeDepends) + } appendArray("Optional dependencies", pkgInfo.OptionalDepends) dependants, err := pkgInfo.GetDependants(rootDir) if err == nil { From b27137da29a63e2c7c062d4dec1fe83da8323342 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Wed, 16 Apr 2025 20:13:22 +0300 Subject: [PATCH 36/88] Allow sync and update subcommands when local database files are corrupt --- src/bpm/main.go | 46 ++++++++++++++++++++++++++++++++++++++ src/bpmlib/config.go | 9 -------- src/bpmlib/repositories.go | 41 ++++++++++++++++++++++++++++----- 3 files changed, 82 insertions(+), 14 deletions(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index 7265eb9..9ff4a10 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -102,6 +102,13 @@ func resolveCommand() { fmt.Println("No packages were given") return } + + // Read local databases + err := bpmlib.ReadLocalDatabases() + if err != nil { + log.Fatalf("Error: could not read local databases: %s", err) + } + for n, pkg := range packages { var info *bpmlib.PackageInfo isFile := false @@ -145,6 +152,12 @@ func resolveCommand() { fmt.Println(bpmlib.CreateReadableInfo(true, true, true, info, rootDir)) } case list: + // Read local databases + err := bpmlib.ReadLocalDatabases() + if err != nil { + log.Fatalf("Error: could not read local databases: %s", err) + } + packages, err := bpmlib.GetInstalledPackages(rootDir) if err != nil { log.Fatalf("Error: could not get installed packages: %s", err.Error()) @@ -178,6 +191,13 @@ func resolveCommand() { if len(searchTerms) == 0 { log.Fatalf("Error: no search terms given") } + + // Read local databases + err := bpmlib.ReadLocalDatabases() + if err != nil { + log.Fatalf("Error: could not read local databases: %s", err) + } + for i, term := range searchTerms { nameResults := make([]*bpmlib.PackageInfo, 0) descResults := make([]*bpmlib.PackageInfo, 0) @@ -236,6 +256,12 @@ func resolveCommand() { reinstallMethod = bpmlib.ReinstallMethodNone } + // Read local databases + err := bpmlib.ReadLocalDatabases() + if err != nil { + log.Fatalf("Error: could not read local databases: %s", err) + } + // Create installation operation operation, err := bpmlib.InstallPackages(rootDir, ir, reinstallMethod, !noOptional, force, verbose, subcommandArgs...) if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { @@ -287,6 +313,14 @@ func resolveCommand() { log.Fatalf("Error: this subcommand needs to be run with superuser permissions") } + // Read local databases if no sync + if nosync { + err := bpmlib.ReadLocalDatabases() + if err != nil { + log.Fatalf("Error: could not read local databases: %s", err) + } + } + // Create update operation operation, err := bpmlib.UpdatePackages(rootDir, !nosync, !noOptional, force, verbose) if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { @@ -362,6 +396,12 @@ func resolveCommand() { return } + // Read local databases + err := bpmlib.ReadLocalDatabases() + if err != nil { + log.Fatalf("Error: could not read local databases: %s", err) + } + // Create remove operation operation, err := bpmlib.RemovePackages(rootDir, removeUnused, doCleanup, verbose, subcommandArgs...) if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { @@ -408,6 +448,12 @@ func resolveCommand() { log.Fatalf("Error: this subcommand needs to be run with superuser permissions") } + // Read local databases + err := bpmlib.ReadLocalDatabases() + if err != nil { + log.Fatalf("Error: could not read local databases: %s", err) + } + // Create cleanup operation operation, err := bpmlib.CleanupPackages(rootDir, verbose) if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { diff --git a/src/bpmlib/config.go b/src/bpmlib/config.go index fdbf246..eee5d57 100644 --- a/src/bpmlib/config.go +++ b/src/bpmlib/config.go @@ -34,14 +34,5 @@ func ReadConfig() (err error) { } } - for _, repo := range BPMConfig.Repositories { - repo.Entries = make(map[string]*RepositoryEntry) - repo.VirtualPackages = make(map[string][]string) - err := repo.ReadLocalDatabase() - if err != nil { - return err - } - } - return nil } diff --git a/src/bpmlib/repositories.go b/src/bpmlib/repositories.go index ffe64b2..c74e440 100644 --- a/src/bpmlib/repositories.go +++ b/src/bpmlib/repositories.go @@ -2,6 +2,7 @@ package bpmlib import ( "errors" + "fmt" "gopkg.in/yaml.v3" "io" "net/http" @@ -83,29 +84,59 @@ func (repo *Repository) ReadLocalDatabase() error { func (repo *Repository) SyncLocalDatabase() error { repoFile := "/var/lib/bpm/repositories/" + repo.Name + ".bpmdb" - err := os.MkdirAll(path.Dir(repoFile), 0755) - if err != nil { - return err - } + // Get URL to database u, err := url.JoinPath(repo.Source, "database.bpmdb") if err != nil { return err } + // Retrieve data from URL resp, err := http.Get(u) if err != nil { return err } defer resp.Body.Close() + // Load data into byte buffer + buffer, err := io.ReadAll(resp.Body) + + // Unmarshal data to ensure it is a valid BPM repository + err = yaml.Unmarshal(buffer, &Repository{}) + if err != nil { + return fmt.Errorf("could not decode repository: %s", err) + } + + // Create parent directories to repository file + err = os.MkdirAll(path.Dir(repoFile), 0755) + if err != nil { + return err + } + + // Create file and save repository data out, err := os.Create(repoFile) if err != nil { return err } defer out.Close() - _, err = io.Copy(out, resp.Body) + _, err = out.Write(buffer) + + return nil +} + +func ReadLocalDatabases() (err error) { + for _, repo := range BPMConfig.Repositories { + // Initialize struct values + repo.Entries = make(map[string]*RepositoryEntry) + repo.VirtualPackages = make(map[string][]string) + + // Read database + err = repo.ReadLocalDatabase() + if err != nil { + return err + } + } return nil } From 0858a4963634206dff95674db9876fb2f4d358ae Mon Sep 17 00:00:00 2001 From: EnumDev Date: Wed, 16 Apr 2025 20:15:46 +0300 Subject: [PATCH 37/88] Add .gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6bbca8c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# IDE Directories +.idea + +# Build directory +build From 877fba8767e1e4f8a582d274b432a6a0ce3368d5 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 17 Apr 2025 13:49:41 +0300 Subject: [PATCH 38/88] Use install command in Makefile, remove compress and run target and add uninstall target --- Makefile | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 851f832..d757063 100644 --- a/Makefile +++ b/Makefile @@ -18,21 +18,16 @@ build: cd src/bpm; $(GO) build -ldflags "-w" -o ../../build/bpm git.enumerated.dev/bubble-package-manager/bpm/src/bpm install: build/bpm config/ - mkdir -p $(DESTDIR)$(BINDIR) - mkdir -p $(DESTDIR)$(SYSCONFDIR) - cp build/bpm $(DESTDIR)$(BINDIR)/bpm - cp config/bpm.conf $(DESTDIR)$(SYSCONFDIR)/bpm.conf + # Create directories + install -dm755 $(DESTDIR)$(BINDIR) + install -dm755 $(DESTDIR)$(SYSCONFDIR) + # Install files + install -Dm755 build/bpm $(DESTDIR)$(BINDIR)/bpm + install -Dm644 config/bpm.conf $(DESTDIR)$(SYSCONFDIR)/bpm.conf -compress: build/bpm config/ - mkdir -p bpm/$(BINDIR) - mkdir -p bpm/$(SYSCONFDIR) - cp build/bpm bpm/$(BINDIR)/bpm - cp config/bpm.conf bpm/$(SYSCONFDIR)/bpm.conf - tar --owner=root --group=root -czf bpm.tar.gz bpm - rm -r bpm - -run: build/bpm - build/bpm +uninstall: + rm $(DESTDIR)$(BINDIR)/bpm + rm $(DESTDIR)$(SYSCONFDIR)/bpm.conf clean: rm -r build/ From d9d85b49435f83ad045f4f0b749bfb2ce882a698 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 17 Apr 2025 13:58:49 +0300 Subject: [PATCH 39/88] Simplify Makefile and make it posix compliant --- Makefile | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index d757063..1b4a678 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,10 @@ -SHELL = /bin/bash +# Installation paths +PREFIX ?= /usr/local +BINDIR ?= $(PREFIX)/bin +SYSCONFDIR := $(PREFIX)/etc -ifeq ($(PREFIX),) - PREFIX := /usr/local -endif -ifeq ($(BINDIR),) - BINDIR := $(PREFIX)/bin -endif -ifeq ($(SYSCONFDIR),) - SYSCONFDIR := $(PREFIX)/etc -endif -ifeq ($(GO),) - GO := $(shell type -a -P go | head -n 1) -endif +# Compilers and tools +GO ?= $(shell which go) build: mkdir -p build From 3a62f23db5b780596afe341d7244687a021498d8 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 17 Apr 2025 14:04:11 +0300 Subject: [PATCH 40/88] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a15ab6c..231a717 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,13 @@ BPM is still in very early development. Do not install it without knowing what y - Download `go` from your package manager or from the go website - Download `make` from your package manager -- Run the following command to compile the project -``` +- Download `which` from your package manager +- Run the following command to compile the project. You may need to set the `GO` environment variable if your Go installation is not in your PATH +```sh make ``` - Run the following command to install BPM into your system. You may also append a DESTDIR variable at the end of this line if you wish to install in a different location -``` +```sh make install PREFIX=/usr SYSCONFDIR=/etc ``` @@ -55,7 +56,7 @@ bpm update ``` For information on the rest of the commands simply use the help command or pass in no arguments at all -``` +```sh bpm help ``` From e8d5f0a565f93cbe5a1c6ed061fa59195652aa5e Mon Sep 17 00:00:00 2001 From: EnumDev Date: Tue, 22 Apr 2025 17:00:20 +0300 Subject: [PATCH 41/88] Add basic compilation functionality --- src/bpm/main.go | 49 ++++++++++ src/bpmlib/compilation.go | 185 ++++++++++++++++++++++++++++++++++++++ src/bpmlib/tarball.go | 64 +++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 src/bpmlib/compilation.go diff --git a/src/bpm/main.go b/src/bpm/main.go index 9ff4a10..f3c807b 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -8,6 +8,7 @@ import ( "git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib" "log" "os" + "path" "path/filepath" "slices" "strings" @@ -62,6 +63,7 @@ const ( remove cleanup file + compile ) func getCommandType() commandType { @@ -86,6 +88,8 @@ func getCommandType() commandType { return cleanup case "file": return file + case "compile": + return compile default: return help } @@ -543,6 +547,51 @@ func resolveCommand() { } } } + case compile: + if len(subcommandArgs) == 0 { + fmt.Println("No source packages were given") + return + } + + // Read local databases + err := bpmlib.ReadLocalDatabases() + if err != nil { + log.Fatalf("Error: could not read local databases: %s", err) + } + + // Compile packages + for _, sourcePackage := range subcommandArgs { + if _, err := os.Stat(sourcePackage); os.IsNotExist(err) { + log.Fatalf("Error: file (%s) does not exist!", sourcePackage) + } + + // Read archive + bpmpkg, err := bpmlib.ReadPackage(sourcePackage) + if err != nil { + log.Fatalf("Could not read package (%s): %s", sourcePackage, err) + } + + // Ensure archive is source BPM package + if bpmpkg.PkgInfo.Type != "source" { + log.Fatalf("Error: cannot compile a non-source package!") + } + + // Get current working directory + workdir, err := os.Getwd() + if err != nil { + log.Fatalf("Error: could not get working directory: %s", err) + } + + outputFilename := fmt.Sprintf(path.Join(workdir, "%s-%s-%d.bpm"), bpmpkg.PkgInfo.Name, bpmpkg.PkgInfo.Version, bpmpkg.PkgInfo.Revision) + + err = bpmlib.CompileSourcePackage(sourcePackage, outputFilename) + if err != nil { + log.Fatalf("Error: could not compile source package (%s): %s", sourcePackage, err) + } + + fmt.Printf("Package (%s) was successfully compiled! Binary package generated at: %s\n", sourcePackage, outputFilename) + } + default: printHelp() } diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go new file mode 100644 index 0000000..be677e4 --- /dev/null +++ b/src/bpmlib/compilation.go @@ -0,0 +1,185 @@ +package bpmlib + +import ( + "errors" + "fmt" + "gopkg.in/yaml.v3" + "io" + "os" + "os/exec" + "path" + "strconv" +) + +func CompileSourcePackage(archiveFilename, outputFilename string) (err error) { + // Read BPM archive + bpmpkg, err := ReadPackage(archiveFilename) + if err != nil { + return err + } + + // Ensure package type is 'source' + if bpmpkg.PkgInfo.Type != "source" { + return errors.New("cannot compile a non-source package") + } + + // Get HOME directory + homeDir, err := os.UserHomeDir() + if err != nil { + return err + } + + tempDirectory := path.Join(homeDir, ".cache/bpm/compilation/", bpmpkg.PkgInfo.Name) + + // Ensure temporary directory does not exist + if _, err := os.Stat(tempDirectory); err == nil { + err := os.RemoveAll(tempDirectory) + if err != nil { + return err + } + } + + // Create temporary directory + err = os.MkdirAll(tempDirectory, 0755) + if err != nil { + return err + } + + // Extract source.sh file + content, err := readTarballContent(archiveFilename, "source.sh") + if err != nil { + return err + } + sourceFile, err := os.Create(path.Join(tempDirectory, "source.sh")) + if err != nil { + return err + } + _, err = io.Copy(sourceFile, content.tarReader) + if err != nil { + return err + } + err = sourceFile.Close() + if err != nil { + return err + } + err = content.file.Close() + if err != nil { + return err + } + + // Extract source files + err = extractTarballDirectory(archiveFilename, "source-files", tempDirectory) + if err != nil { + return err + } + + // Create source directory + err = os.Mkdir(path.Join(tempDirectory, "source"), 0755) + if err != nil { + return err + } + + // Setup environment for commands + env := os.Environ() + env = append(env, "HOME="+tempDirectory) + env = append(env, "BPM_WORKDIR="+tempDirectory) + env = append(env, "BPM_SOURCE="+path.Join(tempDirectory, "source")) + env = append(env, "BPM_OUTPUT="+path.Join(tempDirectory, "output")) + env = append(env, "BPM_PKG_NAME="+bpmpkg.PkgInfo.Name) + 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_ARCH="+GetArch()) + + // Execute prepare and build functions in source.sh script + cmd := exec.Command("bash", "-c", + "set -a\n"+ // Source and export functions and variables in source.sh script + ". \"${BPM_WORKDIR}\"/source.sh\n"+ + "set +a\n"+ + "[[ $(type -t prepare) == \"function\" ]] && (echo \"Running prepare() function\" && cd \"$BPM_SOURCE\" && set -e && prepare)\n"+ // Run prepare() function if it exists + "[[ $(type -t build) == \"function\" ]] && (echo \"Running build() function\" && cd \"$BPM_SOURCE\" && set -e && build)\n"+ // Run build() function if it exists + "[[ $(type -t check) == \"function\" ]] && (echo \"Running check() function\" && cd \"$BPM_SOURCE\" && set -e && check)\n"+ // Run check() function if it exists + "exit 0") + cmd.Dir = tempDirectory + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = env + err = cmd.Run() + if err != nil { + return err + } + + // Remove 'output' directory if it already exists + if _, err := os.Stat(path.Join(tempDirectory, "output")); err == nil { + err := os.RemoveAll(path.Join(tempDirectory, "output")) + if err != nil { + return err + } + } + + // Create new 'output' directory + err = os.Mkdir(path.Join(tempDirectory, "output"), 0755) + if err != nil { + return err + } + + // Run bash command + cmd = exec.Command("bash", "-e", "-c", + "set -a\n"+ // Source and export functions and variables in source.sh script + ". \"${BPM_WORKDIR}\"/source.sh\n"+ + "set +a\n"+ + "(echo \"Running package() function\" && cd \"$BPM_SOURCE\" && fakeroot -s \"$BPM_WORKDIR\"/fakeroot_file package)\n"+ // Run package() function + "fakeroot -i \"$BPM_WORKDIR\"/fakeroot_file find \"$BPM_OUTPUT\" -mindepth 1 -printf \"%P %#m %U %G %s\\n\" > \"$BPM_WORKDIR\"/pkg.files") // Create package file list + cmd.Dir = tempDirectory + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = env + err = cmd.Run() + if err != nil { + return err + } + + // Create gzip-compressed archive for the package files + cmd = exec.Command("bash", "-c", "find output -printf \"%P\\n\" | fakeroot -i \"$BPM_WORKDIR\"/fakeroot_file tar -czf files.tar.gz --no-recursion -C output -T -") + cmd.Dir = tempDirectory + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = env + err = cmd.Run() + if err != nil { + return fmt.Errorf("files.tar.gz archive could not be created: %s", err) + } + + // Copy pkgInfo struct and set package type to binary + pkgInfo := bpmpkg.PkgInfo + pkgInfo.Type = "binary" + + // Marshal package info + pkgInfoBytes, err := yaml.Marshal(pkgInfo) + if err != nil { + return err + } + pkgInfoBytes = append(pkgInfoBytes, '\n') + + // Create pkg.info file + err = os.WriteFile(path.Join(tempDirectory, "pkg.info"), pkgInfoBytes, 0644) + if err != nil { + return err + } + + // Create final BPM archive + cmd = exec.Command("bash", "-c", "tar -cf "+outputFilename+" --owner=0 --group=0 -C \"$BPM_WORKDIR\" pkg.info pkg.files ${PACKAGE_SCRIPTS[@]} files.tar.gz") + cmd.Dir = tempDirectory + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + currentDir, err := os.Getwd() + if err != nil { + return err + } + cmd.Env = append(env, "CURRENT_DIR="+currentDir) + err = cmd.Run() + if err != nil { + return fmt.Errorf("BPM archive could not be created: %s", err) + } + + return nil +} diff --git a/src/bpmlib/tarball.go b/src/bpmlib/tarball.go index 84a0008..a6ef764 100644 --- a/src/bpmlib/tarball.go +++ b/src/bpmlib/tarball.go @@ -5,6 +5,8 @@ import ( "errors" "io" "os" + "path" + "strings" ) type tarballFileReader struct { @@ -41,3 +43,65 @@ func readTarballContent(tarballPath, fileToExtract string) (*tarballFileReader, return nil, errors.New("could not file in tarball") } + +func extractTarballDirectory(tarballPath, directoryToExtract, workingDirectory string) (err error) { + file, err := os.Open(tarballPath) + if err != nil { + return err + } + defer file.Close() + + tr := tar.NewReader(file) + for { + header, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + + if strings.HasPrefix(header.Name, directoryToExtract+"/") { + // Skip directory to extract + if strings.TrimRight(header.Name, "/") == workingDirectory { + continue + } + + // Trim directory name from header name + header.Name = strings.TrimPrefix(header.Name, directoryToExtract+"/") + outputPath := path.Join(workingDirectory, header.Name) + + switch header.Typeflag { + case tar.TypeDir: + // Create directory + err := os.MkdirAll(outputPath, 0755) + if err != nil { + return err + } + case tar.TypeReg: + // Create file and set permissions + file, err = os.Create(outputPath) + if err != nil { + return err + } + err := file.Chmod(header.FileInfo().Mode()) + if err != nil { + return err + } + + // Copy data to file + _, err = io.Copy(file, tr) + if err != nil { + return err + } + + // Close file + file.Close() + default: + continue + } + } + } + + return nil +} From 8b6ef5fa63b776407729b4abce780a0da7681e79 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Tue, 22 Apr 2025 17:41:37 +0300 Subject: [PATCH 42/88] Added compilation environment array to BPM config --- config/bpm.conf | 1 + src/bpmlib/compilation.go | 1 + src/bpmlib/config.go | 5 +++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/config/bpm.conf b/config/bpm.conf index 3e8e4ae..2f2e876 100644 --- a/config/bpm.conf +++ b/config/bpm.conf @@ -1,4 +1,5 @@ ignore_packages: [] +compilation_env: [] repositories: - name: example-repository source: https://my-repo.xyz/ diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index be677e4..08f26d4 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -89,6 +89,7 @@ func CompileSourcePackage(archiveFilename, outputFilename string) (err error) { 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_ARCH="+GetArch()) + env = append(env, BPMConfig.CompilationEnvironment...) // Execute prepare and build functions in source.sh script cmd := exec.Command("bash", "-c", diff --git a/src/bpmlib/config.go b/src/bpmlib/config.go index eee5d57..ebf0224 100644 --- a/src/bpmlib/config.go +++ b/src/bpmlib/config.go @@ -6,8 +6,9 @@ import ( ) type BPMConfigStruct struct { - IgnorePackages []string `yaml:"ignore_packages"` - Repositories []*Repository `yaml:"repositories"` + IgnorePackages []string `yaml:"ignore_packages"` + CompilationEnvironment []string `yaml:"compilation_env"` + Repositories []*Repository `yaml:"repositories"` } var BPMConfig BPMConfigStruct From 6af3b77d693ea8c1afc4969246a60fce319419df Mon Sep 17 00:00:00 2001 From: EnumDev Date: Tue, 22 Apr 2025 19:10:53 +0300 Subject: [PATCH 43/88] Include package scripts in compilation --- src/bpmlib/compilation.go | 34 ++++++++-------- src/bpmlib/packages.go | 29 +++++++++++++- src/bpmlib/tarball.go | 84 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 126 insertions(+), 21 deletions(-) diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index 08f26d4..ccc6beb 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -4,11 +4,11 @@ import ( "errors" "fmt" "gopkg.in/yaml.v3" - "io" "os" "os/exec" "path" "strconv" + "strings" ) func CompileSourcePackage(archiveFilename, outputFilename string) (err error) { @@ -46,25 +46,18 @@ func CompileSourcePackage(archiveFilename, outputFilename string) (err error) { } // Extract source.sh file - content, err := readTarballContent(archiveFilename, "source.sh") + err = extractTarballFile(archiveFilename, "source.sh", tempDirectory) if err != nil { return err } - sourceFile, err := os.Create(path.Join(tempDirectory, "source.sh")) - if err != nil { - return err - } - _, err = io.Copy(sourceFile, content.tarReader) - if err != nil { - return err - } - err = sourceFile.Close() - if err != nil { - return err - } - err = content.file.Close() - if err != nil { - return err + + // Get package scripts and extract them + packageScripts := getPackageScripts(archiveFilename) + for _, script := range packageScripts { + err = extractTarballFile(archiveFilename, script, tempDirectory) + if err != nil { + return err + } } // Extract source files @@ -167,8 +160,13 @@ func CompileSourcePackage(archiveFilename, outputFilename string) (err error) { return err } + // Get files to include in BPM archive + bpmArchiveFiles := make([]string, 0) + bpmArchiveFiles = append(bpmArchiveFiles, "pkg.info", "pkg.files", "files.tar.gz") // Base files + bpmArchiveFiles = append(bpmArchiveFiles, packageScripts...) // Package scripts + // Create final BPM archive - cmd = exec.Command("bash", "-c", "tar -cf "+outputFilename+" --owner=0 --group=0 -C \"$BPM_WORKDIR\" pkg.info pkg.files ${PACKAGE_SCRIPTS[@]} files.tar.gz") + cmd = exec.Command("bash", "-c", "tar -cf "+outputFilename+" --owner=0 --group=0 -C \"$BPM_WORKDIR\" "+strings.Join(bpmArchiveFiles, " ")) cmd.Dir = tempDirectory cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index da57201..ec0df7a 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -218,6 +218,31 @@ func ReadPackage(filename string) (*BPMPackage, error) { }, nil } +func getPackageScripts(filename string) (packageScripts []string) { + content, err := listTarballContent(filename) + if err != nil { + return + } + + for _, file := range content { + if file == "pre_install.sh" { + packageScripts = append(packageScripts, "pre_install.sh") + } else if file == "post_install.sh" { + packageScripts = append(packageScripts, "post_install.sh") + } else if file == "pre_update.sh" { + packageScripts = append(packageScripts, "pre_update.sh") + } else if file == "post_update.sh" { + packageScripts = append(packageScripts, "post_update.sh") + } else if file == "pre_remove.sh" { + packageScripts = append(packageScripts, "pre_remove.sh") + } else if file == "post_remove.sh" { + packageScripts = append(packageScripts, "post_remove.sh") + } + } + + return packageScripts +} + func ReadPackageScripts(filename string) (map[string]string, error) { if _, err := os.Stat(filename); os.IsNotExist(err) { return nil, err @@ -480,7 +505,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) return err } - tarballFile, err := readTarballContent(filename, "files.tar.gz") + tarballFile, err := readTarballFile(filename, "files.tar.gz") if err != nil { return err } @@ -738,7 +763,7 @@ func installPackage(filename, rootDir string, verbose, force bool) error { return err } - tarballFile, err := readTarballContent(filename, "pkg.files") + tarballFile, err := readTarballFile(filename, "pkg.files") if err != nil { return err } diff --git a/src/bpmlib/tarball.go b/src/bpmlib/tarball.go index a6ef764..f60f96a 100644 --- a/src/bpmlib/tarball.go +++ b/src/bpmlib/tarball.go @@ -14,7 +14,35 @@ type tarballFileReader struct { file *os.File } -func readTarballContent(tarballPath, fileToExtract string) (*tarballFileReader, error) { +func listTarballContent(tarballPath string) (content []string, err error) { + file, err := os.Open(tarballPath) + if err != nil { + return nil, err + } + defer file.Close() + + tr := tar.NewReader(file) + for { + header, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + + switch header.Typeflag { + case tar.TypeDir: + continue + default: + content = append(content, header.Name) + } + } + + return content, nil +} + +func readTarballFile(tarballPath, fileToExtract string) (*tarballFileReader, error) { file, err := os.Open(tarballPath) if err != nil { return nil, err @@ -44,6 +72,60 @@ func readTarballContent(tarballPath, fileToExtract string) (*tarballFileReader, return nil, errors.New("could not file in tarball") } +func extractTarballFile(tarballPath, fileToExtract string, workingDirectory string) (err error) { + file, err := os.Open(tarballPath) + if err != nil { + return err + } + defer file.Close() + + tr := tar.NewReader(file) + for { + header, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + + // Skip if filename does not match + if header.Name != fileToExtract { + continue + } + + // Trim directory name from header name + header.Name = strings.Split(header.Name, "/")[len(strings.Split(header.Name, "/"))-1] + outputPath := path.Join(workingDirectory, header.Name) + + switch header.Typeflag { + case tar.TypeReg: + // Create file and set permissions + file, err = os.Create(outputPath) + if err != nil { + return err + } + err := file.Chmod(header.FileInfo().Mode()) + if err != nil { + return err + } + + // Copy data to file + _, err = io.Copy(file, tr) + if err != nil { + return err + } + + // Close file + file.Close() + default: + continue + } + } + + return nil +} + func extractTarballDirectory(tarballPath, directoryToExtract, workingDirectory string) (err error) { file, err := os.Open(tarballPath) if err != nil { From 1843bceef5cffb045058de547735d4b40255273c Mon Sep 17 00:00:00 2001 From: EnumDev Date: Tue, 22 Apr 2025 19:28:36 +0300 Subject: [PATCH 44/88] Fix pre_remove.sh package script not being read or run --- src/bpmlib/packages.go | 85 ++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 48 deletions(-) diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index ec0df7a..85b6772 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -8,6 +8,7 @@ import ( version "github.com/knqyf263/go-rpm-version" "gopkg.in/yaml.v3" "io" + "log" "os" "os/exec" "path" @@ -262,31 +263,7 @@ func ReadPackageScripts(filename string) (map[string]string, error) { if err != nil { return nil, err } - if header.Name == "pre_install.sh" { - bs, err := io.ReadAll(tr) - if err != nil { - return nil, err - } - ret[header.Name] = string(bs) - } else if header.Name == "post_install.sh" { - bs, err := io.ReadAll(tr) - if err != nil { - return nil, err - } - ret[header.Name] = string(bs) - } else if header.Name == "pre_update.sh" { - bs, err := io.ReadAll(tr) - if err != nil { - return nil, err - } - ret[header.Name] = string(bs) - } else if header.Name == "post_update.sh" { - bs, err := io.ReadAll(tr) - if err != nil { - return nil, err - } - ret[header.Name] = string(bs) - } else if header.Name == "post_remove.sh" { + if header.Name == "pre_install.sh" || header.Name == "post_install.sh" || header.Name == "pre_update.sh" || header.Name == "post_update.sh" || header.Name == "pre_remove.sh" || header.Name == "post_remove.sh" { bs, err := io.ReadAll(tr) if err != nil { return nil, err @@ -791,23 +768,30 @@ func installPackage(filename, rootDir string, verbose, force bool) error { return err } - scripts, err := ReadPackageScripts(filename) + // Save remove package scripts + packageScripts, err := ReadPackageScripts(filename) if err != nil { return err } - if val, ok := scripts["post_remove.sh"]; ok { - f, err = os.Create(path.Join(pkgDir, "post_remove.sh")) + for script, content := range packageScripts { + if !strings.HasSuffix(script, "_remove.sh") { + continue + } + + // Create file + f, err = os.Create(path.Join(pkgDir, script)) if err != nil { return err } - _, err = f.WriteString(val) - if err != nil { - return err - } - err = f.Close() + + // Write script contents to file + _, err = f.WriteString(content) if err != nil { return err } + + // Close file + f.Close() } if !packageInstalled { @@ -1138,6 +1122,24 @@ func removePackage(pkg string, verbose bool, rootDir string) error { return errors.New("could not get package info") } + // Executing pre_remove script + if _, err := os.Stat(path.Join(pkgDir, "pre_remove.sh")); err == nil { + cmd := exec.Command("/bin/bash", path.Join(pkgDir, "pre_remove.sh")) + cmd.Dir = rootDir + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir)) + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", pkgInfo.Name)) + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", pkgInfo.Version)) + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", pkgInfo.Revision)) + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", pkgInfo.Url)) + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", pkgInfo.Arch)) + + err = cmd.Run() + if err != nil { + log.Printf("Warning: could not run pre_remove.sh package script: %s", err) + } + } + // Fetching and reversing package file entry list fileEntries := GetPackageFiles(pkg, rootDir) sort.Slice(fileEntries, func(i, j int) bool { @@ -1218,27 +1220,14 @@ func removePackage(pkg string, verbose bool, rootDir string) error { cmd.Env = os.Environ() cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir)) cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", pkgInfo.Name)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DESC=%s", pkgInfo.Description)) cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", pkgInfo.Version)) cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", pkgInfo.Revision)) cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", pkgInfo.Url)) cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", pkgInfo.Arch)) - depends := make([]string, len(pkgInfo.Depends)) - copy(depends, pkgInfo.Depends) - for i := 0; i < len(depends); i++ { - depends[i] = fmt.Sprintf("\"%s\"", depends[i]) - } - makeDepends := make([]string, len(pkgInfo.MakeDepends)) - copy(makeDepends, pkgInfo.MakeDepends) - for i := 0; i < len(makeDepends); i++ { - makeDepends[i] = fmt.Sprintf("\"%s\"", makeDepends[i]) - } - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DEPENDS=(%s)", strings.Join(depends, " "))) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_MAKE_DEPENDS=(%s)", strings.Join(makeDepends, " "))) - cmd.Env = append(cmd.Env, "BPM_PKG_TYPE=source") + err = cmd.Run() if err != nil { - return err + log.Printf("Warning: could not run pre_remove.sh package script: %s", err) } } From 382a3fc8b61e3a1625a38b202d2699f5896be9ef Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 24 Apr 2025 13:56:30 +0300 Subject: [PATCH 45/88] Add flags for 'compile' subcommand --- config/bpm.conf | 1 + src/bpm/main.go | 138 +++++++++++++++++++++++++++++++++++++- src/bpmlib/compilation.go | 21 +++++- src/bpmlib/config.go | 1 + 4 files changed, 157 insertions(+), 4 deletions(-) diff --git a/config/bpm.conf b/config/bpm.conf index 2f2e876..6d31c3e 100644 --- a/config/bpm.conf +++ b/config/bpm.conf @@ -1,4 +1,5 @@ ignore_packages: [] +privilege_escalator_cmd: "sudo" compilation_env: [] repositories: - name: example-repository diff --git a/src/bpm/main.go b/src/bpm/main.go index f3c807b..b3dcf57 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -8,6 +8,7 @@ import ( "git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib" "log" "os" + "os/exec" "path" "path/filepath" "slices" @@ -39,6 +40,9 @@ var nosync = true var removeUnused = false var doCleanup = false var showRepoInfo = false +var installSrcPkgDepends = false +var skipChecks = false +var outputFilename = "" func main() { err := bpmlib.ReadConfig() @@ -576,20 +580,128 @@ func resolveCommand() { log.Fatalf("Error: cannot compile a non-source package!") } + // Get direct runtime and make dependencies + totalDepends := make([]string, 0) + for _, depend := range bpmpkg.PkgInfo.GetAllDependencies(true, false) { + if !slices.Contains(totalDepends, depend) { + totalDepends = append(totalDepends, depend) + } + } + + // Get unmet dependencies + unmetDepends := slices.Clone(totalDepends) + installedPackages, err := bpmlib.GetInstalledPackages("/") + if err != nil { + log.Fatalf("Error: could not get installed packages: %s\n", err) + } + for i := len(unmetDepends) - 1; i >= 0; i-- { + if slices.Contains(installedPackages, unmetDepends[i]) { + unmetDepends = append(unmetDepends[:i], unmetDepends[i+1:]...) + } + } + + // Install missing source package dependencies + if installSrcPkgDepends && len(unmetDepends) > 0 { + // Get path to current executable + executable, err := os.Executable() + if err != nil { + log.Fatalf("Error: could not get path to executable: %s\n", err) + } + + // Run 'bpm install' using the set privilege escalator command + cmd := exec.Command(bpmlib.BPMConfig.PrivilegeEscalatorCmd, executable, "install", "--installation-reason=dependency", strings.Join(unmetDepends, " ")) + if yesAll { + cmd.Args = slices.Insert(cmd.Args, 3, "-y") + } + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + if verbose { + fmt.Println("Running command: " + cmd.String()) + } + err = cmd.Run() + if err != nil { + log.Fatalf("Error: dependency installation command failed: %s\n", err) + } + } else { + // Ensure the required dependencies are installed + if len(unmetDepends) != 0 { + log.Fatalf("Error: could not resolve dependencies: the following dependencies were not found in any repositories: " + strings.Join(unmetDepends, ", ")) + } + } + // Get current working directory workdir, err := os.Getwd() if err != nil { log.Fatalf("Error: could not get working directory: %s", err) } - outputFilename := fmt.Sprintf(path.Join(workdir, "%s-%s-%d.bpm"), bpmpkg.PkgInfo.Name, bpmpkg.PkgInfo.Version, bpmpkg.PkgInfo.Revision) + // Get user home directory + homedir, err := os.UserHomeDir() + if err != nil { + log.Fatalf("Error: could not get user home directory: %s", err) + } - err = bpmlib.CompileSourcePackage(sourcePackage, outputFilename) + // Trim output filename + outputFilename = strings.TrimSpace(outputFilename) + if outputFilename != "/" { + outputFilename = strings.TrimSuffix(outputFilename, "/") + } + + // Set output filename if empty + if outputFilename == "" { + outputFilename = path.Join(workdir, fmt.Sprintf("%s-%s-%d.bpm", bpmpkg.PkgInfo.Name, bpmpkg.PkgInfo.Version, bpmpkg.PkgInfo.Revision)) + } + + // Replace first tilde with user home directory + if strings.Split(outputFilename, "/")[0] == "~" { + outputFilename = strings.Replace(outputFilename, "~", homedir, 1) + } + + // Prepend current working directory to output filename if not an absolute path + if outputFilename != "" && !strings.HasPrefix(outputFilename, "/") { + outputFilename = filepath.Join(workdir, outputFilename) + } + + // Clean path + path.Clean(outputFilename) + + // Append archive filename if path is set to a directory + if stat, err := os.Stat(outputFilename); err == nil && stat.IsDir() { + outputFilename = path.Join(outputFilename, fmt.Sprintf("%s-%s-%d.bpm", bpmpkg.PkgInfo.Name, bpmpkg.PkgInfo.Version, bpmpkg.PkgInfo.Revision)) + } + + err = bpmlib.CompileSourcePackage(sourcePackage, outputFilename, skipChecks) if err != nil { log.Fatalf("Error: could not compile source package (%s): %s", sourcePackage, err) } fmt.Printf("Package (%s) was successfully compiled! Binary package generated at: %s\n", sourcePackage, outputFilename) + + // Remove unused packages + if installSrcPkgDepends && len(unmetDepends) > 0 { + // Get path to current executable + executable, err := os.Executable() + if err != nil { + log.Fatalf("Error: could not get path to executable: %s\n", err) + } + + // Run 'bpm cleanup' using the set privilege escalator command + cmd := exec.Command(bpmlib.BPMConfig.PrivilegeEscalatorCmd, executable, "cleanup") + if yesAll { + cmd.Args = slices.Insert(cmd.Args, 3, "-y") + } + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + if verbose { + fmt.Println("Running command: " + cmd.String()) + } + err = cmd.Run() + if err != nil { + log.Fatalf("Error: dependency cleanup command failed: %s\n", err) + } + } } default: @@ -644,6 +756,13 @@ func printHelp() { fmt.Println(" -y skips the confirmation prompt") fmt.Println("-> bpm file [-R] | shows what packages the following packages are managed by") fmt.Println(" -R= lets you define the root path which will be used") + fmt.Println("-> bpm compile [-d, -s, -o] | Compile source BPM package") + fmt.Println(" -v Show additional information about what BPM is doing") + fmt.Println(" -d installs required dependencies for package compilation") + fmt.Println(" -s skips the check function in source.sh scripts") + fmt.Println(" -o sets output filename") + fmt.Println(" -y skips the confirmation prompt") + fmt.Println("\033[1m----------------\033[0m") } @@ -702,6 +821,15 @@ func resolveFlags() { fileFlagSet := flag.NewFlagSet("Remove flags", flag.ExitOnError) fileFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root") fileFlagSet.Usage = printHelp + // Compile flags + compileFlagSet := flag.NewFlagSet("Compile flags", flag.ExitOnError) + compileFlagSet.BoolVar(&installSrcPkgDepends, "d", false, "Install required dependencies for package compilation") + compileFlagSet.BoolVar(&skipChecks, "s", false, "Skip the check function in source.sh scripts") + compileFlagSet.StringVar(&outputFilename, "o", "", "Set output filename") + compileFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing") + compileFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts") + + compileFlagSet.Usage = printHelp if len(os.Args[1:]) <= 0 { subcommand = "help" } else { @@ -749,6 +877,12 @@ func resolveFlags() { return } subcommandArgs = fileFlagSet.Args() + } else if getCommandType() == compile { + err := compileFlagSet.Parse(subcommandArgs) + if err != nil { + return + } + subcommandArgs = compileFlagSet.Args() } if reinstallAll { reinstall = true diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index ccc6beb..387b4b3 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -11,7 +11,7 @@ import ( "strings" ) -func CompileSourcePackage(archiveFilename, outputFilename string) (err error) { +func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks bool) (err error) { // Read BPM archive bpmpkg, err := ReadPackage(archiveFilename) if err != nil { @@ -91,7 +91,6 @@ func CompileSourcePackage(archiveFilename, outputFilename string) (err error) { "set +a\n"+ "[[ $(type -t prepare) == \"function\" ]] && (echo \"Running prepare() function\" && cd \"$BPM_SOURCE\" && set -e && prepare)\n"+ // Run prepare() function if it exists "[[ $(type -t build) == \"function\" ]] && (echo \"Running build() function\" && cd \"$BPM_SOURCE\" && set -e && build)\n"+ // Run build() function if it exists - "[[ $(type -t check) == \"function\" ]] && (echo \"Running check() function\" && cd \"$BPM_SOURCE\" && set -e && check)\n"+ // Run check() function if it exists "exit 0") cmd.Dir = tempDirectory cmd.Stdout = os.Stdout @@ -102,6 +101,24 @@ func CompileSourcePackage(archiveFilename, outputFilename string) (err error) { return err } + // Execute check function in source.sh script if not skipping checks + if !skipChecks { + cmd = exec.Command("bash", "-c", + "set -a\n"+ // Source and export functions and variables in source.sh script + ". \"${BPM_WORKDIR}\"/source.sh\n"+ + "set +a\n"+ + "[[ $(type -t check) == \"function\" ]] && (echo \"Running check() function\" && cd \"$BPM_SOURCE\" && set -e && check)\n"+ // Run check() function if it exists + "exit 0") + cmd.Dir = tempDirectory + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = env + err = cmd.Run() + if err != nil { + return err + } + } + // Remove 'output' directory if it already exists if _, err := os.Stat(path.Join(tempDirectory, "output")); err == nil { err := os.RemoveAll(path.Join(tempDirectory, "output")) diff --git a/src/bpmlib/config.go b/src/bpmlib/config.go index ebf0224..fbf5fda 100644 --- a/src/bpmlib/config.go +++ b/src/bpmlib/config.go @@ -7,6 +7,7 @@ import ( type BPMConfigStruct struct { IgnorePackages []string `yaml:"ignore_packages"` + PrivilegeEscalatorCmd string `yaml:"privilege_escalator_cmd"` CompilationEnvironment []string `yaml:"compilation_env"` Repositories []*Repository `yaml:"repositories"` } From b73519ba9cf23ac1cdd215dffc19bff07370a6ca Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 24 Apr 2025 16:44:37 +0300 Subject: [PATCH 46/88] Fix shell commands used in source package compilation --- src/bpmlib/compilation.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index 387b4b3..b1bd998 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -89,8 +89,8 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo "set -a\n"+ // Source and export functions and variables in source.sh script ". \"${BPM_WORKDIR}\"/source.sh\n"+ "set +a\n"+ - "[[ $(type -t prepare) == \"function\" ]] && (echo \"Running prepare() function\" && cd \"$BPM_SOURCE\" && set -e && prepare)\n"+ // Run prepare() function if it exists - "[[ $(type -t build) == \"function\" ]] && (echo \"Running build() function\" && cd \"$BPM_SOURCE\" && set -e && build)\n"+ // Run build() function if it exists + "[[ $(type -t prepare) == \"function\" ]] && { echo \"Running prepare() function\"; bash -e -c 'cd \"$BPM_SOURCE\" && prepare' || exit 1; }\n"+ // Run prepare() function if it exists + "[[ $(type -t build) == \"function\" ]] && { echo \"Running build() function\"; bash -e -c 'cd \"$BPM_SOURCE\" && build' || exit 1; }\n"+ // Run build() function if it exists "exit 0") cmd.Dir = tempDirectory cmd.Stdout = os.Stdout @@ -107,7 +107,7 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo "set -a\n"+ // Source and export functions and variables in source.sh script ". \"${BPM_WORKDIR}\"/source.sh\n"+ "set +a\n"+ - "[[ $(type -t check) == \"function\" ]] && (echo \"Running check() function\" && cd \"$BPM_SOURCE\" && set -e && check)\n"+ // Run check() function if it exists + "[[ $(type -t check) == \"function\" ]] && { echo \"Running check() function\"; bash -e -c 'cd \"$BPM_SOURCE\" && check' || exit 1; }\n"+ // Run check() function if it exists "exit 0") cmd.Dir = tempDirectory cmd.Stdout = os.Stdout @@ -133,12 +133,13 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo return err } - // Run bash command - cmd = exec.Command("bash", "-e", "-c", + // Execute package function in source.sh script and generate package file list + cmd = exec.Command("bash", "-c", "set -a\n"+ // Source and export functions and variables in source.sh script ". \"${BPM_WORKDIR}\"/source.sh\n"+ "set +a\n"+ - "(echo \"Running package() function\" && cd \"$BPM_SOURCE\" && fakeroot -s \"$BPM_WORKDIR\"/fakeroot_file package)\n"+ // Run package() function + "echo \"Running package() function\"\n"+ + "( cd \"$BPM_SOURCE\" && fakeroot -s \"$BPM_WORKDIR\"/fakeroot_file bash -e -c 'package' ) || exit 1\n"+ // Run package() function "fakeroot -i \"$BPM_WORKDIR\"/fakeroot_file find \"$BPM_OUTPUT\" -mindepth 1 -printf \"%P %#m %U %G %s\\n\" > \"$BPM_WORKDIR\"/pkg.files") // Create package file list cmd.Dir = tempDirectory cmd.Stdout = os.Stdout From 4bf9ac9c60566be7866af585c491c4004e6c79dc Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 24 Apr 2025 17:39:17 +0300 Subject: [PATCH 47/88] Add .compilation-options file support --- src/bpmlib/compilation.go | 57 ++++++++++++++++++++++++++++++++++++++- src/bpmlib/utils.go | 2 +- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index b1bd998..9ff3b51 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -23,6 +23,12 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo return errors.New("cannot compile a non-source package") } + // Read compilation options file in current directory + compilationOptions, err := readCompilationOptionsFile() + if err != nil { + return err + } + // Get HOME directory homeDir, err := os.UserHomeDir() if err != nil { @@ -81,7 +87,12 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo env = append(env, "BPM_PKG_NAME="+bpmpkg.PkgInfo.Name) 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_ARCH="+GetArch()) + // Check for architecture override in compilation options + if val, ok := compilationOptions["ARCH"]; ok { + env = append(env, "BPM_PKG_ARCH="+val) + } else { + env = append(env, "BPM_PKG_ARCH="+GetArch()) + } env = append(env, BPMConfig.CompilationEnvironment...) // Execute prepare and build functions in source.sh script @@ -200,3 +211,47 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo return nil } + +func readCompilationOptionsFile() (options map[string]string, err error) { + // Initialize options map + options = make(map[string]string) + + // Check if file compilation options file exists + stat, err := os.Stat(".compilation-options") + if err != nil { + return nil, nil + } + + // Ensure it is a regular file + if !stat.Mode().IsRegular() { + return nil, fmt.Errorf("%s is not a regular file", stat.Name()) + } + + // Read file data + data, err := os.ReadFile(stat.Name()) + if err != nil { + return nil, err + } + + for _, line := range strings.Split(string(data), "\n") { + // Trim line + line = strings.TrimSpace(line) + + // Skip empty lines + if line == "" { + continue + } + + // Split line + split := strings.SplitN(line, "=", 2) + + // Throw error if line isn't valid + if len(split) < 2 { + return nil, fmt.Errorf("invalid line in compilation-options file: '%s'", line) + } + + options[split[0]] = split[1] + } + + return options, nil +} diff --git a/src/bpmlib/utils.go b/src/bpmlib/utils.go index a89dff8..707a46b 100644 --- a/src/bpmlib/utils.go +++ b/src/bpmlib/utils.go @@ -12,7 +12,7 @@ func GetArch() string { uname := syscall.Utsname{} err := syscall.Uname(&uname) if err != nil { - return "" + return "unknown" } var byteString [65]byte From c8c1fded76faf704bedbeed86525abd948f69df4 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 24 Apr 2025 18:03:32 +0300 Subject: [PATCH 48/88] Set output package architecture after compilation --- src/bpmlib/compilation.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index 9ff3b51..1c7ea75 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -172,10 +172,19 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo return fmt.Errorf("files.tar.gz archive could not be created: %s", err) } - // Copy pkgInfo struct and set package type to binary + // Copy pkgInfo struct pkgInfo := bpmpkg.PkgInfo + + // Set package type to binary pkgInfo.Type = "binary" + // Set package architecture + if val, ok := compilationOptions["ARCH"]; ok { + pkgInfo.Arch = val + } else { + pkgInfo.Arch = GetArch() + } + // Marshal package info pkgInfoBytes, err := yaml.Marshal(pkgInfo) if err != nil { From d8146cb3f3a9dc103753d0708f5448a8f35a7bc4 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 24 Apr 2025 18:03:57 +0300 Subject: [PATCH 49/88] Remove unused function --- src/bpmlib/utils.go | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/bpmlib/utils.go b/src/bpmlib/utils.go index 707a46b..99275a0 100644 --- a/src/bpmlib/utils.go +++ b/src/bpmlib/utils.go @@ -2,9 +2,7 @@ package bpmlib import ( "fmt" - "io" "math" - "os" "syscall" ) @@ -23,29 +21,6 @@ func GetArch() string { return string(byteString[:indexLength]) } -func copyFileContents(src, dst string) (err error) { - in, err := os.Open(src) - if err != nil { - return - } - defer in.Close() - out, err := os.Create(dst) - if err != nil { - return - } - defer func() { - cerr := out.Close() - if err == nil { - err = cerr - } - }() - if _, err = io.Copy(out, in); err != nil { - return - } - err = out.Sync() - return -} - func stringSliceRemove(s []string, r string) []string { for i, v := range s { if v == r { From 9cdb3d29aa0a5424d2d631f9563537aa768be17e Mon Sep 17 00:00:00 2001 From: EnumDev Date: Fri, 25 Apr 2025 14:13:12 +0300 Subject: [PATCH 50/88] Reallow direct source package installation using 'bpm install' --- Makefile | 6 ++- src/bpmlib/compilation.go | 80 ++++++++++++++++++++++++++++++++++++--- src/bpmlib/operations.go | 34 ++++++++++++++++- src/bpmlib/packages.go | 19 +++++----- src/bpmlib/tarball.go | 24 +++++++++++- 5 files changed, 143 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index 1b4a678..7c8a82e 100644 --- a/Makefile +++ b/Makefile @@ -6,9 +6,13 @@ SYSCONFDIR := $(PREFIX)/etc # Compilers and tools GO ?= $(shell which go) +# Build-time variables +ROOT_COMPILATION_UID ?= 65534 +ROOT_COMPILATION_GID ?= 65534 + build: mkdir -p build - cd src/bpm; $(GO) build -ldflags "-w" -o ../../build/bpm git.enumerated.dev/bubble-package-manager/bpm/src/bpm + 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 install: build/bpm config/ # Create directories diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index 1c7ea75..e0e0f99 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -9,8 +9,12 @@ import ( "path" "strconv" "strings" + "syscall" ) +var rootCompilationUID = "65534" +var rootCompilationGID = "65534" + func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks bool) (err error) { // Read BPM archive bpmpkg, err := ReadPackage(archiveFilename) @@ -35,6 +39,24 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo return err } + // Get UID and GID to use for compilation + var uid, gid int + if os.Getuid() == 0 { + _uid, err := strconv.ParseInt(rootCompilationUID, 10, 32) + if err != nil { + return fmt.Errorf("could not convert UID '%s' to int", rootCompilationUID) + } + _gid, err := strconv.ParseInt(rootCompilationGID, 10, 32) + if err != nil { + return fmt.Errorf("could not convert GID '%s' to int", rootCompilationGID) + } + uid = int(_uid) + gid = int(_gid) + } else { + uid = os.Getuid() + gid = os.Getgid() + } + tempDirectory := path.Join(homeDir, ".cache/bpm/compilation/", bpmpkg.PkgInfo.Name) // Ensure temporary directory does not exist @@ -51,8 +73,14 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo return err } + // Change temporary directory owner + err = os.Chown(tempDirectory, uid, gid) + if err != nil { + return err + } + // Extract source.sh file - err = extractTarballFile(archiveFilename, "source.sh", tempDirectory) + err = extractTarballFile(archiveFilename, "source.sh", tempDirectory, uid, gid) if err != nil { return err } @@ -60,14 +88,14 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo // Get package scripts and extract them packageScripts := getPackageScripts(archiveFilename) for _, script := range packageScripts { - err = extractTarballFile(archiveFilename, script, tempDirectory) + err = extractTarballFile(archiveFilename, script, tempDirectory, uid, gid) if err != nil { return err } } // Extract source files - err = extractTarballDirectory(archiveFilename, "source-files", tempDirectory) + err = extractTarballDirectory(archiveFilename, "source-files", tempDirectory, uid, gid) if err != nil { return err } @@ -78,6 +106,12 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo return err } + // Change source directory owner + err = os.Chown(path.Join(tempDirectory, "source"), uid, gid) + if err != nil { + return err + } + // Setup environment for commands env := os.Environ() env = append(env, "HOME="+tempDirectory) @@ -107,6 +141,8 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = env + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} err = cmd.Run() if err != nil { return err @@ -124,13 +160,15 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = env + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} err = cmd.Run() if err != nil { return err } } - // Remove 'output' directory if it already exists + // Remove output directory if it already exists if _, err := os.Stat(path.Join(tempDirectory, "output")); err == nil { err := os.RemoveAll(path.Join(tempDirectory, "output")) if err != nil { @@ -138,12 +176,18 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo } } - // Create new 'output' directory + // Create new output directory err = os.Mkdir(path.Join(tempDirectory, "output"), 0755) if err != nil { return err } + // Change output directory owner + err = os.Chown(path.Join(tempDirectory, "output"), uid, gid) + if err != nil { + return err + } + // Execute package function in source.sh script and generate package file list cmd = exec.Command("bash", "-c", "set -a\n"+ // Source and export functions and variables in source.sh script @@ -156,6 +200,8 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = env + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} err = cmd.Run() if err != nil { return err @@ -167,6 +213,8 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = env + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} err = cmd.Run() if err != nil { return fmt.Errorf("files.tar.gz archive could not be created: %s", err) @@ -198,13 +246,19 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo return err } + // Change pkg.info file owner + err = os.Chown(path.Join(tempDirectory, "pkg.info"), uid, gid) + if err != nil { + return err + } + // Get files to include in BPM archive bpmArchiveFiles := make([]string, 0) bpmArchiveFiles = append(bpmArchiveFiles, "pkg.info", "pkg.files", "files.tar.gz") // Base files bpmArchiveFiles = append(bpmArchiveFiles, packageScripts...) // Package scripts // Create final BPM archive - cmd = exec.Command("bash", "-c", "tar -cf "+outputFilename+" --owner=0 --group=0 -C \"$BPM_WORKDIR\" "+strings.Join(bpmArchiveFiles, " ")) + cmd = exec.Command("bash", "-c", "tar -cf final-archive.bpm --owner=0 --group=0 -C \"$BPM_WORKDIR\" "+strings.Join(bpmArchiveFiles, " ")) cmd.Dir = tempDirectory cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -213,11 +267,25 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo return err } cmd.Env = append(env, "CURRENT_DIR="+currentDir) + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} err = cmd.Run() if err != nil { return fmt.Errorf("BPM archive could not be created: %s", err) } + // Move final BPM archive + err = os.Rename(path.Join(tempDirectory, "final-archive.bpm"), outputFilename) + if err != nil { + return err + } + + // Set final BPM archive owner + err = os.Chown(outputFilename, os.Getuid(), os.Getgid()) + if err != nil { + return err + } + return nil } diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index f072759..1078be9 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -466,13 +466,43 @@ func (operation *BPMOperation) Execute(verbose, force bool) error { } } else if action.GetActionType() == "install" { value := action.(*InstallPackageAction) + fileToInstall := value.File bpmpkg := value.BpmPackage isReinstall := IsPackageInstalled(bpmpkg.PkgInfo.Name, operation.RootDir) var err error + + // Compile package if type is 'source' + if bpmpkg.PkgInfo.Type == "source" { + // Get path to compiled package directory and output filename + compiledDir := path.Join(operation.RootDir, "/var/lib/bpm/compiled/") + outputFilename := path.Join(compiledDir, fmt.Sprintf("%s-%s-%d.bpm", bpmpkg.PkgInfo.Name, bpmpkg.PkgInfo.Version, bpmpkg.PkgInfo.Revision)) + + // Create compiled package directory if not exists + if _, err := os.Stat(compiledDir); err != nil { + err := os.MkdirAll(compiledDir, 0755) + if err != nil { + return err + } + } + + // Compile source package + err = CompileSourcePackage(value.File, outputFilename, false) + if err != nil { + return fmt.Errorf("could not compile source package (%s): %s\n", value.File, err) + } + + // Set values + fileToInstall = outputFilename + bpmpkg, err = ReadPackage(outputFilename) + if err != nil { + return fmt.Errorf("could not read package (%s): %s\n", fileToInstall, err) + } + } + if value.IsDependency { - err = installPackage(value.File, operation.RootDir, verbose, true) + err = installPackage(fileToInstall, operation.RootDir, verbose, true) } else { - err = installPackage(value.File, operation.RootDir, verbose, force) + err = installPackage(fileToInstall, operation.RootDir, verbose, force) } if err != nil { return errors.New(fmt.Sprintf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err)) diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index 85b6772..7f29197 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -607,6 +607,12 @@ func installPackage(filename, rootDir string, verbose, force bool) error { if err != nil { return err } + + // Ensure package type is 'binary' + if bpmpkg.PkgInfo.Type != "binary" { + return fmt.Errorf("can only extract binary packages") + } + packageInstalled := IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) // Check if package is installed and remove current files if packageInstalled { @@ -712,15 +718,10 @@ func installPackage(filename, rootDir string, verbose, force bool) error { fmt.Printf("Extracting files for package (%s)...\n", bpmpkg.PkgInfo.Name) } - if bpmpkg.PkgInfo.Type == "binary" { - err := extractPackage(bpmpkg, verbose, filename, rootDir) - if err != nil { - return err - } - } else if bpmpkg.PkgInfo.Type == "source" { - return errors.New("direct source package compilation in BPM has been temporarily removed and is being reworked on") - } else { - return errors.New("unknown package type: " + bpmpkg.PkgInfo.Type) + // Extract package files into rootDir + err = extractPackage(bpmpkg, verbose, filename, rootDir) + if err != nil { + return err } installedDir := path.Join(rootDir, "var/lib/bpm/installed/") diff --git a/src/bpmlib/tarball.go b/src/bpmlib/tarball.go index f60f96a..df9876d 100644 --- a/src/bpmlib/tarball.go +++ b/src/bpmlib/tarball.go @@ -72,7 +72,7 @@ func readTarballFile(tarballPath, fileToExtract string) (*tarballFileReader, err return nil, errors.New("could not file in tarball") } -func extractTarballFile(tarballPath, fileToExtract string, workingDirectory string) (err error) { +func extractTarballFile(tarballPath, fileToExtract string, workingDirectory string, uid, gid int) (err error) { file, err := os.Open(tarballPath) if err != nil { return err @@ -109,6 +109,12 @@ func extractTarballFile(tarballPath, fileToExtract string, workingDirectory stri if err != nil { return err } + if uid >= 0 && gid >= 0 { + err = file.Chown(uid, gid) + if err != nil { + return err + } + } // Copy data to file _, err = io.Copy(file, tr) @@ -126,7 +132,7 @@ func extractTarballFile(tarballPath, fileToExtract string, workingDirectory stri return nil } -func extractTarballDirectory(tarballPath, directoryToExtract, workingDirectory string) (err error) { +func extractTarballDirectory(tarballPath, directoryToExtract, workingDirectory string, uid, gid int) (err error) { file, err := os.Open(tarballPath) if err != nil { return err @@ -160,6 +166,14 @@ func extractTarballDirectory(tarballPath, directoryToExtract, workingDirectory s if err != nil { return err } + + // Set directory owner + if uid >= 0 && gid >= 0 { + err = os.Chown(outputPath, uid, gid) + if err != nil { + return err + } + } case tar.TypeReg: // Create file and set permissions file, err = os.Create(outputPath) @@ -170,6 +184,12 @@ func extractTarballDirectory(tarballPath, directoryToExtract, workingDirectory s if err != nil { return err } + if uid >= 0 && gid >= 0 { + err = file.Chown(uid, gid) + if err != nil { + return err + } + } // Copy data to file _, err = io.Copy(file, tr) From 5085981f5225c7bcc915d5a34f2908b9d85ead3b Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sat, 26 Apr 2025 15:29:45 +0300 Subject: [PATCH 51/88] Allow split source package compilation --- src/bpm/main.go | 6 +- src/bpmlib/compilation.go | 330 +++++++++++++++++++++++--------------- src/bpmlib/general.go | 10 ++ src/bpmlib/operations.go | 4 +- src/bpmlib/packages.go | 39 +++-- 5 files changed, 238 insertions(+), 151 deletions(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index b3dcf57..7560c24 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -671,12 +671,14 @@ func resolveCommand() { outputFilename = path.Join(outputFilename, fmt.Sprintf("%s-%s-%d.bpm", bpmpkg.PkgInfo.Name, bpmpkg.PkgInfo.Version, bpmpkg.PkgInfo.Revision)) } - err = bpmlib.CompileSourcePackage(sourcePackage, outputFilename, skipChecks) + outputBpmPackages, err := bpmlib.CompileSourcePackage(sourcePackage, outputFilename, skipChecks) if err != nil { log.Fatalf("Error: could not compile source package (%s): %s", sourcePackage, err) } - fmt.Printf("Package (%s) was successfully compiled! Binary package generated at: %s\n", sourcePackage, outputFilename) + for k, v := range outputBpmPackages { + fmt.Printf("Package (%s) was successfully compiled! Binary package generated at: %s\n", k, v) + } // Remove unused packages if installSrcPkgDepends && len(unmetDepends) > 0 { diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index e0e0f99..370feb9 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -15,28 +15,31 @@ import ( var rootCompilationUID = "65534" var rootCompilationGID = "65534" -func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks bool) (err error) { +func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks bool) (outputBpmPackages map[string]string, err error) { + // Initialize map + outputBpmPackages = make(map[string]string) + // Read BPM archive bpmpkg, err := ReadPackage(archiveFilename) if err != nil { - return err + return nil, err } // Ensure package type is 'source' if bpmpkg.PkgInfo.Type != "source" { - return errors.New("cannot compile a non-source package") + return nil, errors.New("cannot compile a non-source package") } // Read compilation options file in current directory compilationOptions, err := readCompilationOptionsFile() if err != nil { - return err + return nil, err } // Get HOME directory homeDir, err := os.UserHomeDir() if err != nil { - return err + return nil, err } // Get UID and GID to use for compilation @@ -44,11 +47,11 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo if os.Getuid() == 0 { _uid, err := strconv.ParseInt(rootCompilationUID, 10, 32) if err != nil { - return fmt.Errorf("could not convert UID '%s' to int", rootCompilationUID) + return nil, fmt.Errorf("could not convert UID '%s' to int", rootCompilationUID) } _gid, err := strconv.ParseInt(rootCompilationGID, 10, 32) if err != nil { - return fmt.Errorf("could not convert GID '%s' to int", rootCompilationGID) + return nil, fmt.Errorf("could not convert GID '%s' to int", rootCompilationGID) } uid = int(_uid) gid = int(_gid) @@ -63,26 +66,26 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo if _, err := os.Stat(tempDirectory); err == nil { err := os.RemoveAll(tempDirectory) if err != nil { - return err + return nil, err } } // Create temporary directory err = os.MkdirAll(tempDirectory, 0755) if err != nil { - return err + return nil, err } // Change temporary directory owner err = os.Chown(tempDirectory, uid, gid) if err != nil { - return err + return nil, err } // Extract source.sh file err = extractTarballFile(archiveFilename, "source.sh", tempDirectory, uid, gid) if err != nil { - return err + return nil, err } // Get package scripts and extract them @@ -90,26 +93,26 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo for _, script := range packageScripts { err = extractTarballFile(archiveFilename, script, tempDirectory, uid, gid) if err != nil { - return err + return nil, err } } // Extract source files err = extractTarballDirectory(archiveFilename, "source-files", tempDirectory, uid, gid) if err != nil { - return err + return nil, err } // Create source directory err = os.Mkdir(path.Join(tempDirectory, "source"), 0755) if err != nil { - return err + return nil, err } // Change source directory owner err = os.Chown(path.Join(tempDirectory, "source"), uid, gid) if err != nil { - return err + return nil, err } // Setup environment for commands @@ -145,7 +148,7 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} err = cmd.Run() if err != nil { - return err + return nil, err } // Execute check function in source.sh script if not skipping checks @@ -164,129 +167,192 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} err = cmd.Run() if err != nil { - return err + return nil, err } } - // Remove output directory if it already exists - if _, err := os.Stat(path.Join(tempDirectory, "output")); err == nil { - err := os.RemoveAll(path.Join(tempDirectory, "output")) + // Variable that will be used later + isSplitPkg := true + + // Get all packages to compile + packagesToCompile := bpmpkg.PkgInfo.SplitPackages + if len(packagesToCompile) == 0 { + packagesToCompile = append(packagesToCompile, bpmpkg.PkgInfo) + isSplitPkg = false + } + + // Compile each package + for _, pkg := range packagesToCompile { + // Get package function name + packageFunctionName := "package" + if isSplitPkg { + packageFunctionName = "package_" + pkg.Name + } + + // Remove output directory if it already exists + if _, err := os.Stat(path.Join(tempDirectory, "output")); err == nil { + err := os.RemoveAll(path.Join(tempDirectory, "output")) + if err != nil { + return nil, err + } + } + + // Create new output directory + err = os.Mkdir(path.Join(tempDirectory, "output"), 0755) if err != nil { - return err + return nil, err } + + // Change output directory owner + err = os.Chown(path.Join(tempDirectory, "output"), uid, gid) + if err != nil { + return nil, err + } + + // Execute package function in source.sh script and generate package file list + cmd = exec.Command("bash", "-c", + "set -a\n"+ // Source and export functions and variables in source.sh script + ". \"${BPM_WORKDIR}\"/source.sh\n"+ + "set +a\n"+ + "echo \"Running "+packageFunctionName+"() function\"\n"+ + "( cd \"$BPM_SOURCE\" && fakeroot -s \"$BPM_WORKDIR\"/fakeroot_file bash -e -c '"+packageFunctionName+"' ) || exit 1\n"+ // Run package() function + "fakeroot -i \"$BPM_WORKDIR\"/fakeroot_file find \"$BPM_OUTPUT\" -mindepth 1 -printf \"%P %#m %U %G %s\\n\" > \"$BPM_WORKDIR\"/pkg.files") // Create package file list + cmd.Dir = tempDirectory + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = env + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + err = cmd.Run() + if err != nil { + return nil, err + } + + // Create gzip-compressed archive for the package files + cmd = exec.Command("bash", "-c", "find output -printf \"%P\\n\" | fakeroot -i \"$BPM_WORKDIR\"/fakeroot_file tar -czf files.tar.gz --no-recursion -C output -T -") + cmd.Dir = tempDirectory + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = env + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + err = cmd.Run() + if err != nil { + return nil, fmt.Errorf("files.tar.gz archive could not be created: %s", err) + } + + // Clone source package info + var pkgInfo PackageInfo + if !isSplitPkg { + pkgInfo = *bpmpkg.PkgInfo + } else { + pkgInfo = *pkg + + // Ensure required fields are set + if strings.TrimSpace(pkgInfo.Name) == "" { + return nil, fmt.Errorf("split package name is empty") + } + + // Copy data from main source package + if pkgInfo.Description == "" { + pkgInfo.Description = bpmpkg.PkgInfo.Description + } + pkgInfo.Version = bpmpkg.PkgInfo.Version + pkgInfo.Revision = bpmpkg.PkgInfo.Revision + pkgInfo.Url = bpmpkg.PkgInfo.Url + if pkgInfo.License == "" { + pkgInfo.License = bpmpkg.PkgInfo.License + } + } + + // Set package type to binary + pkgInfo.Type = "binary" + + // Set package architecture + if val, ok := compilationOptions["ARCH"]; ok { + pkgInfo.Arch = val + } else { + pkgInfo.Arch = GetArch() + } + + // Remove source package specific fields + pkgInfo.MakeDepends = nil + pkgInfo.SplitPackages = nil + + // Marshal package info + pkgInfoBytes, err := yaml.Marshal(pkgInfo) + if err != nil { + return nil, err + } + pkgInfoBytes = append(pkgInfoBytes, '\n') + + // Create pkg.info file + err = os.WriteFile(path.Join(tempDirectory, "pkg.info"), pkgInfoBytes, 0644) + if err != nil { + return nil, err + } + + // Change pkg.info file owner + err = os.Chown(path.Join(tempDirectory, "pkg.info"), uid, gid) + if err != nil { + return nil, err + } + + // Get files to include in BPM archive + bpmArchiveFiles := make([]string, 0) + bpmArchiveFiles = append(bpmArchiveFiles, "pkg.info", "pkg.files", "files.tar.gz") // Base files + bpmArchiveFiles = append(bpmArchiveFiles, packageScripts...) // Package scripts + + // Create final BPM archive + cmd = exec.Command("bash", "-c", "tar -cf final-archive.bpm --owner=0 --group=0 -C \"$BPM_WORKDIR\" "+strings.Join(bpmArchiveFiles, " ")) + cmd.Dir = tempDirectory + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + currentDir, err := os.Getwd() + if err != nil { + return nil, err + } + cmd.Env = append(env, "CURRENT_DIR="+currentDir) + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + err = cmd.Run() + if err != nil { + return nil, fmt.Errorf("BPM archive could not be created: %s", err) + } + + // Remove pkg.info file + err = os.Remove(path.Join(tempDirectory, "pkg.info")) + if err != nil { + return nil, err + } + + // Set output filename if split package + if len(bpmpkg.PkgInfo.SplitPackages) != 1 { + // Get current working directory + workdir, err := os.Getwd() + if err != nil { + return nil, fmt.Errorf("could not get working directory: %s", err) + } + + outputFilename = path.Join(workdir, fmt.Sprintf("%s-%s-%d.bpm", pkgInfo.Name, pkgInfo.Version, pkgInfo.Revision)) + } + + // Move final BPM archive + err = os.Rename(path.Join(tempDirectory, "final-archive.bpm"), outputFilename) + if err != nil { + return nil, err + } + + // Set final BPM archive owner + err = os.Chown(outputFilename, os.Getuid(), os.Getgid()) + if err != nil { + return nil, err + } + + outputBpmPackages[pkgInfo.Name] = outputFilename } - // Create new output directory - err = os.Mkdir(path.Join(tempDirectory, "output"), 0755) - if err != nil { - return err - } - - // Change output directory owner - err = os.Chown(path.Join(tempDirectory, "output"), uid, gid) - if err != nil { - return err - } - - // Execute package function in source.sh script and generate package file list - cmd = exec.Command("bash", "-c", - "set -a\n"+ // Source and export functions and variables in source.sh script - ". \"${BPM_WORKDIR}\"/source.sh\n"+ - "set +a\n"+ - "echo \"Running package() function\"\n"+ - "( cd \"$BPM_SOURCE\" && fakeroot -s \"$BPM_WORKDIR\"/fakeroot_file bash -e -c 'package' ) || exit 1\n"+ // Run package() function - "fakeroot -i \"$BPM_WORKDIR\"/fakeroot_file find \"$BPM_OUTPUT\" -mindepth 1 -printf \"%P %#m %U %G %s\\n\" > \"$BPM_WORKDIR\"/pkg.files") // Create package file list - cmd.Dir = tempDirectory - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Env = env - cmd.SysProcAttr = &syscall.SysProcAttr{} - cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} - err = cmd.Run() - if err != nil { - return err - } - - // Create gzip-compressed archive for the package files - cmd = exec.Command("bash", "-c", "find output -printf \"%P\\n\" | fakeroot -i \"$BPM_WORKDIR\"/fakeroot_file tar -czf files.tar.gz --no-recursion -C output -T -") - cmd.Dir = tempDirectory - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Env = env - cmd.SysProcAttr = &syscall.SysProcAttr{} - cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} - err = cmd.Run() - if err != nil { - return fmt.Errorf("files.tar.gz archive could not be created: %s", err) - } - - // Copy pkgInfo struct - pkgInfo := bpmpkg.PkgInfo - - // Set package type to binary - pkgInfo.Type = "binary" - - // Set package architecture - if val, ok := compilationOptions["ARCH"]; ok { - pkgInfo.Arch = val - } else { - pkgInfo.Arch = GetArch() - } - - // Marshal package info - pkgInfoBytes, err := yaml.Marshal(pkgInfo) - if err != nil { - return err - } - pkgInfoBytes = append(pkgInfoBytes, '\n') - - // Create pkg.info file - err = os.WriteFile(path.Join(tempDirectory, "pkg.info"), pkgInfoBytes, 0644) - if err != nil { - return err - } - - // Change pkg.info file owner - err = os.Chown(path.Join(tempDirectory, "pkg.info"), uid, gid) - if err != nil { - return err - } - - // Get files to include in BPM archive - bpmArchiveFiles := make([]string, 0) - bpmArchiveFiles = append(bpmArchiveFiles, "pkg.info", "pkg.files", "files.tar.gz") // Base files - bpmArchiveFiles = append(bpmArchiveFiles, packageScripts...) // Package scripts - - // Create final BPM archive - cmd = exec.Command("bash", "-c", "tar -cf final-archive.bpm --owner=0 --group=0 -C \"$BPM_WORKDIR\" "+strings.Join(bpmArchiveFiles, " ")) - cmd.Dir = tempDirectory - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - currentDir, err := os.Getwd() - if err != nil { - return err - } - cmd.Env = append(env, "CURRENT_DIR="+currentDir) - cmd.SysProcAttr = &syscall.SysProcAttr{} - cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} - err = cmd.Run() - if err != nil { - return fmt.Errorf("BPM archive could not be created: %s", err) - } - - // Move final BPM archive - err = os.Rename(path.Join(tempDirectory, "final-archive.bpm"), outputFilename) - if err != nil { - return err - } - - // Set final BPM archive owner - err = os.Chown(outputFilename, os.Getuid(), os.Getgid()) - if err != nil { - return err - } - - return nil + return outputBpmPackages, nil } func readCompilationOptionsFile() (options map[string]string, err error) { diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index c88ac32..4c7657a 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -38,6 +38,11 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) && GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() == bpmpkg.PkgInfo.GetFullVersion() { continue } + + if bpmpkg.PkgInfo.Type == "source" && len(bpmpkg.PkgInfo.SplitPackages) != 0 { + return nil, fmt.Errorf("direct source package installation has not been implemented") + } + operation.AppendAction(&InstallPackageAction{ File: pkg, IsDependency: false, @@ -63,6 +68,11 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(entry.Info.Name, rootDir) && GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() { continue } + + if entry.Info.Type == "source" && len(entry.Info.SplitPackages) != 0 { + return nil, fmt.Errorf("direct source package installation has not been implemented") + } + operation.AppendAction(&FetchPackageAction{ IsDependency: false, RepositoryEntry: entry, diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index 1078be9..b3e011b 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -486,13 +486,13 @@ func (operation *BPMOperation) Execute(verbose, force bool) error { } // Compile source package - err = CompileSourcePackage(value.File, outputFilename, false) + outputBpmPackages, err := CompileSourcePackage(value.File, outputFilename, false) if err != nil { return fmt.Errorf("could not compile source package (%s): %s\n", value.File, err) } // Set values - fileToInstall = outputFilename + fileToInstall = outputBpmPackages[bpmpkg.PkgInfo.Name] bpmpkg, err = ReadPackage(outputFilename) if err != nil { return fmt.Errorf("could not read package (%s): %s\n", fileToInstall, err) diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index 7f29197..fd48021 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -24,21 +24,22 @@ type BPMPackage struct { } type PackageInfo struct { - Name string `yaml:"name,omitempty"` - Description string `yaml:"description,omitempty"` - Version string `yaml:"version,omitempty"` - Revision int `yaml:"revision,omitempty"` - Url string `yaml:"url,omitempty"` - License string `yaml:"license,omitempty"` - Arch string `yaml:"architecture,omitempty"` - Type string `yaml:"type,omitempty"` - Keep []string `yaml:"keep,omitempty"` - Depends []string `yaml:"depends,omitempty"` - MakeDepends []string `yaml:"make_depends,omitempty"` - OptionalDepends []string `yaml:"optional_depends,omitempty"` - Conflicts []string `yaml:"conflicts,omitempty"` - Replaces []string `yaml:"replaces,omitempty"` - Provides []string `yaml:"provides,omitempty"` + Name string `yaml:"name,omitempty"` + Description string `yaml:"description,omitempty"` + Version string `yaml:"version,omitempty"` + Revision int `yaml:"revision,omitempty"` + Url string `yaml:"url,omitempty"` + License string `yaml:"license,omitempty"` + Arch string `yaml:"architecture,omitempty"` + Type string `yaml:"type,omitempty"` + Keep []string `yaml:"keep,omitempty"` + Depends []string `yaml:"depends,omitempty"` + MakeDepends []string `yaml:"make_depends,omitempty"` + OptionalDepends []string `yaml:"optional_depends,omitempty"` + Conflicts []string `yaml:"conflicts,omitempty"` + Replaces []string `yaml:"replaces,omitempty"` + Provides []string `yaml:"provides,omitempty"` + SplitPackages []*PackageInfo `yaml:"split_packages,omitempty"` } type PackageFileEntry struct { @@ -403,6 +404,7 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) { Conflicts: make([]string, 0), Replaces: make([]string, 0), Provides: make([]string, 0), + SplitPackages: make([]*PackageInfo, 0), } err := yaml.Unmarshal([]byte(contents), &pkgInfo) if err != nil { @@ -460,6 +462,13 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p appendArray("Provided packages", pkgInfo.Provides) appendArray("Replaces packages", pkgInfo.Replaces) } + if pkgInfo.Type == "source" && len(pkgInfo.SplitPackages) != 0 { + splitPkgs := make([]string, len(pkgInfo.SplitPackages)) + for i, splitPkgInfo := range pkgInfo.SplitPackages { + splitPkgs[i] = splitPkgInfo.Name + } + appendArray("Split Packages", splitPkgs) + } ret = append(ret, "Installation Reason: "+string(GetInstallationReason(pkgInfo.Name, rootDir))) return strings.Join(ret, "\n") } From 903c7dce3e48e8e630c21c40f6315dd935ea0983 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sat, 26 Apr 2025 16:20:40 +0300 Subject: [PATCH 52/88] Change -o flag from output filename to output directory --- src/bpm/main.go | 44 +++++++++++++++++++++------------------ src/bpmlib/compilation.go | 14 +++---------- src/bpmlib/operations.go | 7 +++---- 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index 7560c24..77e2cf1 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -42,7 +42,7 @@ var doCleanup = false var showRepoInfo = false var installSrcPkgDepends = false var skipChecks = false -var outputFilename = "" +var outputDirectory = "" func main() { err := bpmlib.ReadConfig() @@ -642,36 +642,40 @@ func resolveCommand() { log.Fatalf("Error: could not get user home directory: %s", err) } - // Trim output filename - outputFilename = strings.TrimSpace(outputFilename) - if outputFilename != "/" { - outputFilename = strings.TrimSuffix(outputFilename, "/") + // Trim output directory + outputDirectory = strings.TrimSpace(outputDirectory) + if outputDirectory != "/" { + outputDirectory = strings.TrimSuffix(outputDirectory, "/") } - // Set output filename if empty - if outputFilename == "" { - outputFilename = path.Join(workdir, fmt.Sprintf("%s-%s-%d.bpm", bpmpkg.PkgInfo.Name, bpmpkg.PkgInfo.Version, bpmpkg.PkgInfo.Revision)) + // Set output directory if empty + if outputDirectory == "" { + outputDirectory = workdir } // Replace first tilde with user home directory - if strings.Split(outputFilename, "/")[0] == "~" { - outputFilename = strings.Replace(outputFilename, "~", homedir, 1) + if strings.Split(outputDirectory, "/")[0] == "~" { + outputDirectory = strings.Replace(outputDirectory, "~", homedir, 1) } - // Prepend current working directory to output filename if not an absolute path - if outputFilename != "" && !strings.HasPrefix(outputFilename, "/") { - outputFilename = filepath.Join(workdir, outputFilename) + // Prepend current working directory to output directory if not an absolute path + if outputDirectory != "" && !strings.HasPrefix(outputDirectory, "/") { + outputDirectory = filepath.Join(workdir, outputDirectory) } // Clean path - path.Clean(outputFilename) + path.Clean(outputDirectory) - // Append archive filename if path is set to a directory - if stat, err := os.Stat(outputFilename); err == nil && stat.IsDir() { - outputFilename = path.Join(outputFilename, fmt.Sprintf("%s-%s-%d.bpm", bpmpkg.PkgInfo.Name, bpmpkg.PkgInfo.Version, bpmpkg.PkgInfo.Revision)) + // Ensure output directory exists and is a directory + stat, err := os.Stat(outputDirectory) + if err != nil { + log.Fatalf("Error: could not stat output directory (%s): %s", outputDirectory, err) + } + if !stat.IsDir() { + log.Fatalf("Error: output directory (%s) is not a directory", outputDirectory) } - outputBpmPackages, err := bpmlib.CompileSourcePackage(sourcePackage, outputFilename, skipChecks) + outputBpmPackages, err := bpmlib.CompileSourcePackage(sourcePackage, outputDirectory, skipChecks) if err != nil { log.Fatalf("Error: could not compile source package (%s): %s", sourcePackage, err) } @@ -762,7 +766,7 @@ func printHelp() { fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -d installs required dependencies for package compilation") fmt.Println(" -s skips the check function in source.sh scripts") - fmt.Println(" -o sets output filename") + fmt.Println(" -o sets output directory") fmt.Println(" -y skips the confirmation prompt") fmt.Println("\033[1m----------------\033[0m") @@ -827,7 +831,7 @@ func resolveFlags() { compileFlagSet := flag.NewFlagSet("Compile flags", flag.ExitOnError) compileFlagSet.BoolVar(&installSrcPkgDepends, "d", false, "Install required dependencies for package compilation") compileFlagSet.BoolVar(&skipChecks, "s", false, "Skip the check function in source.sh scripts") - compileFlagSet.StringVar(&outputFilename, "o", "", "Set output filename") + compileFlagSet.StringVar(&outputDirectory, "o", "", "Set output directory") compileFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing") compileFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts") diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index 370feb9..dcbd8ae 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -15,7 +15,7 @@ import ( var rootCompilationUID = "65534" var rootCompilationGID = "65534" -func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks bool) (outputBpmPackages map[string]string, err error) { +func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bool) (outputBpmPackages map[string]string, err error) { // Initialize map outputBpmPackages = make(map[string]string) @@ -326,16 +326,8 @@ func CompileSourcePackage(archiveFilename, outputFilename string, skipChecks boo return nil, err } - // Set output filename if split package - if len(bpmpkg.PkgInfo.SplitPackages) != 1 { - // Get current working directory - workdir, err := os.Getwd() - if err != nil { - return nil, fmt.Errorf("could not get working directory: %s", err) - } - - outputFilename = path.Join(workdir, fmt.Sprintf("%s-%s-%d.bpm", pkgInfo.Name, pkgInfo.Version, pkgInfo.Revision)) - } + // Set output filename + outputFilename := path.Join(outputDirectory, fmt.Sprintf("%s-%s-%d.bpm", pkgInfo.Name, pkgInfo.Version, pkgInfo.Revision)) // Move final BPM archive err = os.Rename(path.Join(tempDirectory, "final-archive.bpm"), outputFilename) diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index b3e011b..a78c6a7 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -473,9 +473,8 @@ func (operation *BPMOperation) Execute(verbose, force bool) error { // Compile package if type is 'source' if bpmpkg.PkgInfo.Type == "source" { - // Get path to compiled package directory and output filename + // Get path to compiled package directory compiledDir := path.Join(operation.RootDir, "/var/lib/bpm/compiled/") - outputFilename := path.Join(compiledDir, fmt.Sprintf("%s-%s-%d.bpm", bpmpkg.PkgInfo.Name, bpmpkg.PkgInfo.Version, bpmpkg.PkgInfo.Revision)) // Create compiled package directory if not exists if _, err := os.Stat(compiledDir); err != nil { @@ -486,14 +485,14 @@ func (operation *BPMOperation) Execute(verbose, force bool) error { } // Compile source package - outputBpmPackages, err := CompileSourcePackage(value.File, outputFilename, false) + outputBpmPackages, err := CompileSourcePackage(value.File, compiledDir, false) if err != nil { return fmt.Errorf("could not compile source package (%s): %s\n", value.File, err) } // Set values fileToInstall = outputBpmPackages[bpmpkg.PkgInfo.Name] - bpmpkg, err = ReadPackage(outputFilename) + bpmpkg, err = ReadPackage(fileToInstall) if err != nil { return fmt.Errorf("could not read package (%s): %s\n", fileToInstall, err) } From 5776784021bbd7a36f7726c4ea47faed3fe34f46 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sat, 26 Apr 2025 16:23:09 +0300 Subject: [PATCH 53/88] Remove no longer used flags from help message --- src/bpm/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index 77e2cf1..e44c0e8 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -729,12 +729,11 @@ func printHelp() { fmt.Println(" -c lists the amount of installed packages") fmt.Println(" -n lists only the names of installed packages") fmt.Println("-> bpm search | Searches for packages through declared repositories") - fmt.Println("-> bpm install [-R, -v, -y, -f, -o, -c, -b, -k, --reinstall, --reinstall-all, --no-optional, --installation-reason] | installs the following files") + fmt.Println("-> bpm install [-R, -v, -y, -f, --reinstall, --reinstall-all, --no-optional, --installation-reason] | installs the following files") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -y skips the confirmation prompt") fmt.Println(" -f skips dependency, conflict and architecture checking") - fmt.Println(" -k keeps the compilation directory created by BPM after source package installation") fmt.Println(" --reinstall Reinstalls packages even if they do not have a newer version available") fmt.Println(" --reinstall-all Same as --reinstall but also reinstalls dependencies") fmt.Println(" --no-optional Prevents installation of optional dependencies") From c425b263fe7b526fd405b07dfced99d8ec8ab9c8 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sat, 26 Apr 2025 16:28:24 +0300 Subject: [PATCH 54/88] Fix cleanup flags not being read --- src/bpm/main.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bpm/main.go b/src/bpm/main.go index e44c0e8..c34eb84 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -876,6 +876,12 @@ func resolveFlags() { return } subcommandArgs = removeFlagSet.Args() + } else if getCommandType() == cleanup { + err := cleanupFlagSet.Parse(subcommandArgs) + if err != nil { + return + } + subcommandArgs = cleanupFlagSet.Args() } else if getCommandType() == file { err := fileFlagSet.Parse(subcommandArgs) if err != nil { From b1bb8de6611f76c3bbab109c141e966fbda0487f Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sun, 27 Apr 2025 12:04:30 +0300 Subject: [PATCH 55/88] Fix packages not compiling without root permissions --- src/bpmlib/compilation.go | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index dcbd8ae..caa6075 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -144,8 +144,10 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = env - cmd.SysProcAttr = &syscall.SysProcAttr{} - cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + if os.Getuid() == 0 { + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + } err = cmd.Run() if err != nil { return nil, err @@ -163,8 +165,10 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = env - cmd.SysProcAttr = &syscall.SysProcAttr{} - cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + if os.Getuid() == 0 { + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + } err = cmd.Run() if err != nil { return nil, err @@ -221,8 +225,10 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = env - cmd.SysProcAttr = &syscall.SysProcAttr{} - cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + if os.Getuid() == 0 { + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + } err = cmd.Run() if err != nil { return nil, err @@ -234,8 +240,10 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = env - cmd.SysProcAttr = &syscall.SysProcAttr{} - cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + if os.Getuid() == 0 { + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + } err = cmd.Run() if err != nil { return nil, fmt.Errorf("files.tar.gz archive could not be created: %s", err) @@ -313,8 +321,10 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo return nil, err } cmd.Env = append(env, "CURRENT_DIR="+currentDir) - cmd.SysProcAttr = &syscall.SysProcAttr{} - cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + if os.Getuid() == 0 { + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + } err = cmd.Run() if err != nil { return nil, fmt.Errorf("BPM archive could not be created: %s", err) From d8a42c780d4dc20ce1943d5198427fca1bbe78ac Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 1 May 2025 16:40:42 +0300 Subject: [PATCH 56/88] Allow direct split source package installation through local BPM archive --- src/bpmlib/compilation.go | 31 ++---------------- src/bpmlib/general.go | 27 +++++++++++++--- src/bpmlib/operations.go | 34 +++++++++++++++----- src/bpmlib/packages.go | 66 ++++++++++++++++++++++++++++++++++++--- 4 files changed, 112 insertions(+), 46 deletions(-) diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index caa6075..7e7ce31 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -175,21 +175,17 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo } } - // Variable that will be used later - isSplitPkg := true - // Get all packages to compile packagesToCompile := bpmpkg.PkgInfo.SplitPackages - if len(packagesToCompile) == 0 { + if !bpmpkg.PkgInfo.IsSplitPackage() { packagesToCompile = append(packagesToCompile, bpmpkg.PkgInfo) - isSplitPkg = false } // Compile each package for _, pkg := range packagesToCompile { // Get package function name packageFunctionName := "package" - if isSplitPkg { + if bpmpkg.PkgInfo.IsSplitPackage() { packageFunctionName = "package_" + pkg.Name } @@ -250,28 +246,7 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo } // Clone source package info - var pkgInfo PackageInfo - if !isSplitPkg { - pkgInfo = *bpmpkg.PkgInfo - } else { - pkgInfo = *pkg - - // Ensure required fields are set - if strings.TrimSpace(pkgInfo.Name) == "" { - return nil, fmt.Errorf("split package name is empty") - } - - // Copy data from main source package - if pkgInfo.Description == "" { - pkgInfo.Description = bpmpkg.PkgInfo.Description - } - pkgInfo.Version = bpmpkg.PkgInfo.Version - pkgInfo.Revision = bpmpkg.PkgInfo.Revision - pkgInfo.Url = bpmpkg.PkgInfo.Url - if pkgInfo.License == "" { - pkgInfo.License = bpmpkg.PkgInfo.License - } - } + pkgInfo := *pkg // Set package type to binary pkgInfo.Type = "binary" diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index 4c7657a..c9cc1bd 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -25,6 +25,7 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein Changes: make(map[string]string), RootDir: rootDir, ForceInstallationReason: installationReason, + compiledPackages: make(map[string]string), } // Resolve packages @@ -35,12 +36,25 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein if err != nil { return nil, fmt.Errorf("could not read package: %s", err) } - if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) && GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() == bpmpkg.PkgInfo.GetFullVersion() { + + if bpmpkg.PkgInfo.Type == "source" && bpmpkg.PkgInfo.IsSplitPackage() { + for _, splitPkg := range bpmpkg.PkgInfo.SplitPackages { + if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(splitPkg.Name, rootDir) && GetPackageInfo(splitPkg.Name, rootDir).GetFullVersion() == splitPkg.GetFullVersion() { + continue + } + + operation.AppendAction(&InstallPackageAction{ + File: pkg, + IsDependency: false, + BpmPackage: bpmpkg, + SplitPackageToInstall: splitPkg.Name, + }) + } continue } - if bpmpkg.PkgInfo.Type == "source" && len(bpmpkg.PkgInfo.SplitPackages) != 0 { - return nil, fmt.Errorf("direct source package installation has not been implemented") + if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) && GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() == bpmpkg.PkgInfo.GetFullVersion() { + continue } operation.AppendAction(&InstallPackageAction{ @@ -69,8 +83,8 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein continue } - if entry.Info.Type == "source" && len(entry.Info.SplitPackages) != 0 { - return nil, fmt.Errorf("direct source package installation has not been implemented") + if entry.Info.IsSplitPackage() { + return nil, fmt.Errorf("direct split source package installation has not been implemented") } operation.AppendAction(&FetchPackageAction{ @@ -128,6 +142,7 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie UnresolvedDepends: make([]string, 0), Changes: make(map[string]string), RootDir: rootDir, + compiledPackages: make(map[string]string), } // Search for packages @@ -164,6 +179,7 @@ func CleanupPackages(rootDir string, verbose bool) (operation *BPMOperation, err UnresolvedDepends: make([]string, 0), Changes: make(map[string]string), RootDir: rootDir, + compiledPackages: make(map[string]string), } // Do package cleanup @@ -206,6 +222,7 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci Changes: make(map[string]string), RootDir: rootDir, ForceInstallationReason: InstallationReasonUnknown, + compiledPackages: make(map[string]string), } // Search for packages diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index a78c6a7..efb5a04 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -16,6 +16,7 @@ type BPMOperation struct { Changes map[string]string RootDir string ForceInstallationReason InstallationReason + compiledPackages map[string]string } func (operation *BPMOperation) ActionsContainPackage(pkg string) bool { @@ -337,6 +338,9 @@ func (operation *BPMOperation) ShowOperationSummary() { var pkgInfo *PackageInfo if value.GetActionType() == "install" { pkgInfo = value.(*InstallPackageAction).BpmPackage.PkgInfo + if value.(*InstallPackageAction).SplitPackageToInstall != "" { + pkgInfo = pkgInfo.GetSplitPackageInfo(value.(*InstallPackageAction).SplitPackageToInstall) + } } else if value.GetActionType() == "fetch" { pkgInfo = value.(*FetchPackageAction).RepositoryEntry.Info } else { @@ -484,14 +488,27 @@ func (operation *BPMOperation) Execute(verbose, force bool) error { } } - // Compile source package - outputBpmPackages, err := CompileSourcePackage(value.File, compiledDir, false) - if err != nil { - return fmt.Errorf("could not compile source package (%s): %s\n", value.File, err) + // Get package name to install + pkgNameToInstall := bpmpkg.PkgInfo.Name + if bpmpkg.PkgInfo.IsSplitPackage() { + pkgNameToInstall = value.SplitPackageToInstall + } + + // Compile source package if not compiled already + if _, ok := operation.compiledPackages[pkgNameToInstall]; !ok { + outputBpmPackages, err := CompileSourcePackage(value.File, compiledDir, false) + if err != nil { + return fmt.Errorf("could not compile source package (%s): %s\n", value.File, err) + } + + // Add compiled packages to slice + for pkgName, pkgFile := range outputBpmPackages { + operation.compiledPackages[pkgName] = pkgFile + } } // Set values - fileToInstall = outputBpmPackages[bpmpkg.PkgInfo.Name] + fileToInstall = operation.compiledPackages[pkgNameToInstall] bpmpkg, err = ReadPackage(fileToInstall) if err != nil { return fmt.Errorf("could not read package (%s): %s\n", fileToInstall, err) @@ -530,9 +547,10 @@ type OperationAction interface { } type InstallPackageAction struct { - File string - IsDependency bool - BpmPackage *BPMPackage + File string + IsDependency bool + SplitPackageToInstall string + BpmPackage *BPMPackage } func (action *InstallPackageAction) GetActionType() string { diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index fd48021..6e510a8 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -70,6 +70,25 @@ func (pkgInfo *PackageInfo) GetFullVersion() string { return pkgInfo.Version + "-" + strconv.Itoa(pkgInfo.Revision) } +func (pkgInfo *PackageInfo) IsSplitPackage() bool { + // Return false if not a source package + if pkgInfo.Type != "source" { + return false + } + + return len(pkgInfo.SplitPackages) > 0 +} + +func (pkgInfo *PackageInfo) GetSplitPackageInfo(splitPkg string) *PackageInfo { + for _, splitPkgInfo := range pkgInfo.SplitPackages { + if splitPkgInfo.Name == splitPkg { + return splitPkgInfo + } + } + + return nil +} + type InstallationReason string const ( @@ -149,7 +168,6 @@ func ReadPackage(filename string) (*BPMPackage, error) { var pkgFiles []*PackageFileEntry if _, err := os.Stat(filename); os.IsNotExist(err) { - fmt.Println("a") return nil, err } @@ -388,7 +406,7 @@ func executePackageScripts(filename, rootDir string, operation packageOperation, } func ReadPackageInfo(contents string) (*PackageInfo, error) { - pkgInfo := PackageInfo{ + pkgInfo := &PackageInfo{ Name: "", Description: "", Version: "", @@ -410,6 +428,8 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) { if err != nil { return nil, err } + + // Ensure required fields are set properly if pkgInfo.Name == "" { return nil, errors.New("this package contains no name") } else if pkgInfo.Description == "" { @@ -423,10 +443,46 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) { } else if pkgInfo.Type == "" { return nil, errors.New("this package contains no type") } - for i := 0; i < len(pkgInfo.Keep); i++ { - pkgInfo.Keep[i] = strings.TrimPrefix(pkgInfo.Keep[i], "/") + for _, val := range pkgInfo.Keep { + if strings.HasPrefix(val, "/") { + return nil, fmt.Errorf("cannot keep file (%s) after update because it starts with a slash", val) + } } - return &pkgInfo, nil + + // Setup split package information + for i, splitPkg := range pkgInfo.SplitPackages { + // Ensure split package contains a name and one that is different from the main package name + if splitPkg.Name == "" || splitPkg.Name == pkgInfo.Name { + return nil, fmt.Errorf("invalid split package name: %s", splitPkg.Name) + } + + // Turn split package into json data + splitPkgJson, err := yaml.Marshal(splitPkg) + if err != nil { + return nil, err + } + + // Clone all main package fields onto split package + pkgInfoClone := *pkgInfo + pkgInfo.SplitPackages[i] = &pkgInfoClone + + // Set make depends and split package field of split package to nil + pkgInfo.SplitPackages[i].MakeDepends = nil + pkgInfo.SplitPackages[i].SplitPackages = nil + + // Unmarshal json data back to struct + err = yaml.Unmarshal(splitPkgJson, &pkgInfo.SplitPackages[i]) + if err != nil { + return nil, err + } + + // Force set split package version, revision and URL + pkgInfo.SplitPackages[i].Version = pkgInfo.Version + pkgInfo.SplitPackages[i].Revision = pkgInfo.Revision + pkgInfo.SplitPackages[i].Url = pkgInfo.Url + } + + return pkgInfo, nil } func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, pkgInfo *PackageInfo, rootDir string) string { From 2e416b9e6f96f485e40b3b9688f36b2131346175 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Fri, 2 May 2025 20:43:24 +0300 Subject: [PATCH 57/88] Allow direct split source package installation through repository entries --- src/bpmlib/general.go | 4 --- src/bpmlib/operations.go | 64 ++++++++++++++++++++++++++++++-------- src/bpmlib/repositories.go | 52 +++++++++++++++++++++++++++++-- 3 files changed, 100 insertions(+), 20 deletions(-) diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index c9cc1bd..1d8b05a 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -83,10 +83,6 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein continue } - if entry.Info.IsSplitPackage() { - return nil, fmt.Errorf("direct split source package installation has not been implemented") - } - operation.AppendAction(&FetchPackageAction{ IsDependency: false, RepositoryEntry: entry, diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index efb5a04..1736545 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -413,30 +413,68 @@ func (operation *BPMOperation) RunHooks(verbose bool) error { return nil } -func (operation *BPMOperation) Execute(verbose, force bool) error { +func (operation *BPMOperation) Execute(verbose, force bool) (err error) { // Fetch packages from repositories if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool { return action.GetActionType() == "fetch" }) { fmt.Println("Fetching packages from available repositories...") + + // Create map for fetched packages + fetchedPackages := make(map[string]string) + for i, action := range operation.Actions { if action.GetActionType() != "fetch" { continue } + + // Get repository entry entry := action.(*FetchPackageAction).RepositoryEntry - fetchedPackage, err := entry.Repository.FetchPackage(entry.Info.Name) - if err != nil { - return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err)) + + // Create bpmpkg variable + var bpmpkg *BPMPackage + + // Check if package has already been fetched from download link + if _, ok := fetchedPackages[entry.Download]; !ok { + // Fetch package from repository + fetchedPackage, err := entry.Repository.FetchPackage(entry.Info.Name) + if err != nil { + return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err)) + } + + // Read fetched package + bpmpkg, err = ReadPackage(fetchedPackage) + if err != nil { + return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err)) + } + + // Add fetched package to map + fetchedPackages[entry.Download] = fetchedPackage + + fmt.Printf("Package (%s) was successfully fetched!\n", entry.Info.Name) + } else { + // Read fetched package + bpmpkg, err = ReadPackage(fetchedPackages[entry.Download]) + if err != nil { + return errors.New(fmt.Sprintf("could not read package (%s): %s\n", entry.Info.Name, err)) + } + + fmt.Printf("Package (%s) was successfully fetched!\n", entry.Info.Name) } - bpmpkg, err := ReadPackage(fetchedPackage) - if err != nil { - return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err)) - } - fmt.Printf("Package (%s) was successfully fetched!\n", bpmpkg.PkgInfo.Name) - operation.Actions[i] = &InstallPackageAction{ - File: fetchedPackage, - IsDependency: action.(*FetchPackageAction).IsDependency, - BpmPackage: bpmpkg, + + if bpmpkg.PkgInfo.IsSplitPackage() { + operation.Actions[i] = &InstallPackageAction{ + File: fetchedPackages[entry.Download], + IsDependency: action.(*FetchPackageAction).IsDependency, + BpmPackage: bpmpkg, + SplitPackageToInstall: entry.Info.Name, + } + } else { + operation.Actions[i] = &InstallPackageAction{ + File: fetchedPackages[entry.Download], + IsDependency: action.(*FetchPackageAction).IsDependency, + BpmPackage: bpmpkg, + } } } } diff --git a/src/bpmlib/repositories.go b/src/bpmlib/repositories.go index c74e440..c168f79 100644 --- a/src/bpmlib/repositories.go +++ b/src/bpmlib/repositories.go @@ -73,10 +73,56 @@ func (repo *Repository) ReadLocalDatabase() error { return err } - for _, p := range entry.Info.Provides { - repo.VirtualPackages[p] = append(repo.VirtualPackages[p], entry.Info.Name) + // Create repository entries + if entry.Info.IsSplitPackage() { + for _, splitPkg := range entry.Info.SplitPackages { + // Turn split package into json data + splitPkgJson, err := yaml.Marshal(splitPkg) + if err != nil { + return err + } + + // Clone all main package fields onto split package + splitPkgClone := *entry.Info + + // Set split package field of split package to nil + splitPkgClone.SplitPackages = nil + + // Unmarshal json data back to struct + err = yaml.Unmarshal(splitPkgJson, &splitPkgClone) + if err != nil { + return err + } + + // Force set split package version, revision and URL + splitPkgClone.Version = entry.Info.Version + splitPkgClone.Revision = entry.Info.Revision + splitPkgClone.Url = entry.Info.Url + + // Create entry for split package + repo.Entries[splitPkg.Name] = &RepositoryEntry{ + Info: &splitPkgClone, + Download: entry.Download, + DownloadSize: entry.DownloadSize, + InstalledSize: 0, + Repository: repo, + } + + // Add virtual packages to repository + for _, p := range splitPkg.Provides { + repo.VirtualPackages[p] = append(repo.VirtualPackages[p], splitPkg.Name) + } + } + } else { + // Create entry for package + repo.Entries[entry.Info.Name] = &entry + + // Add virtual packages to repository + for _, p := range entry.Info.Provides { + repo.VirtualPackages[p] = append(repo.VirtualPackages[p], entry.Info.Name) + } } - repo.Entries[entry.Info.Name] = &entry + } return nil From 4f1eeeb11d5a6782301a5cf702a3df6ed8fe1b3d Mon Sep 17 00:00:00 2001 From: EnumDev Date: Fri, 2 May 2025 21:20:56 +0300 Subject: [PATCH 58/88] Fix dependency installation command arguments for 'bpm compile -d' --- src/bpm/main.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index c34eb84..7d49cb8 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -609,7 +609,9 @@ func resolveCommand() { } // Run 'bpm install' using the set privilege escalator command - cmd := exec.Command(bpmlib.BPMConfig.PrivilegeEscalatorCmd, executable, "install", "--installation-reason=dependency", strings.Join(unmetDepends, " ")) + args := []string{executable, "install", "--installation-reason=dependency"} + args = append(args, unmetDepends...) + cmd := exec.Command(bpmlib.BPMConfig.PrivilegeEscalatorCmd, args...) if yesAll { cmd.Args = slices.Insert(cmd.Args, 3, "-y") } From eb8db1bc9915ec886e55ce9890cead9f63fb2913 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Fri, 2 May 2025 21:29:07 +0300 Subject: [PATCH 59/88] Fix output filename for source package compilation --- src/bpmlib/compilation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index 7e7ce31..0b11f3b 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -312,7 +312,7 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo } // Set output filename - outputFilename := path.Join(outputDirectory, fmt.Sprintf("%s-%s-%d.bpm", pkgInfo.Name, pkgInfo.Version, pkgInfo.Revision)) + outputFilename := path.Join(outputDirectory, fmt.Sprintf("%s-%s-%d-%s.bpm", pkgInfo.Name, pkgInfo.Version, pkgInfo.Revision, pkgInfo.Arch)) // Move final BPM archive err = os.Rename(path.Join(tempDirectory, "final-archive.bpm"), outputFilename) From 4a8c4e22ec96201e585a03c3ac3643e5ef56a2d0 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sat, 3 May 2025 10:47:32 +0300 Subject: [PATCH 60/88] Move directories --- src/bpmlib/compilation.go | 8 +++++++- src/bpmlib/operations.go | 2 +- src/bpmlib/repositories.go | 6 +++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index 0b11f3b..6e72b2c 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -60,7 +60,13 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo gid = os.Getgid() } - tempDirectory := path.Join(homeDir, ".cache/bpm/compilation/", bpmpkg.PkgInfo.Name) + // Set temporary directory + var tempDirectory string + if os.Getuid() == 0 { + tempDirectory = path.Join("/var/cache/bpm/compilation/", bpmpkg.PkgInfo.Name) + } else { + tempDirectory = path.Join(homeDir, ".cache/bpm/compilation/", bpmpkg.PkgInfo.Name) + } // Ensure temporary directory does not exist if _, err := os.Stat(tempDirectory); err == nil { diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index 1736545..0a3648b 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -516,7 +516,7 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) { // Compile package if type is 'source' if bpmpkg.PkgInfo.Type == "source" { // Get path to compiled package directory - compiledDir := path.Join(operation.RootDir, "/var/lib/bpm/compiled/") + compiledDir := path.Join(operation.RootDir, "/var/cache/bpm/compiled/") // Create compiled package directory if not exists if _, err := os.Stat(compiledDir); err != nil { diff --git a/src/bpmlib/repositories.go b/src/bpmlib/repositories.go index c168f79..cafc6b1 100644 --- a/src/bpmlib/repositories.go +++ b/src/bpmlib/repositories.go @@ -266,16 +266,16 @@ func (repo *Repository) FetchPackage(pkg string) (string, error) { } defer resp.Body.Close() - err = os.MkdirAll("/var/cache/bpm/packages/", 0755) + err = os.MkdirAll("/var/cache/bpm/fetched/", 0755) if err != nil { return "", err } - out, err := os.Create("/var/cache/bpm/packages/" + path.Base(entry.Download)) + out, err := os.Create("/var/cache/bpm/fetched/" + path.Base(entry.Download)) if err != nil { return "", err } defer out.Close() _, err = io.Copy(out, resp.Body) - return "/var/cache/bpm/packages/" + path.Base(entry.Download), nil + return "/var/cache/bpm/fetched/" + path.Base(entry.Download), nil } From 132910981052d81491a347884319421558b673bc Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sat, 3 May 2025 12:38:29 +0300 Subject: [PATCH 61/88] Add cache cleanup to 'cleanup' subcommand --- README.md | 2 +- src/bpm/main.go | 105 ++++++++++++++++++++++++++---------------- src/bpmlib/general.go | 83 +++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 231a717..d49f03b 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ You can remove an installed package by typing the following bpm remove package_name ``` -To remove all unused dependencies try using the cleanup command +To remove all unused dependencies and clean cached files try using the cleanup command ```sh bpm cleanup ``` diff --git a/src/bpm/main.go b/src/bpm/main.go index 7d49cb8..92274de 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -43,6 +43,10 @@ var showRepoInfo = false var installSrcPkgDepends = false var skipChecks = false var outputDirectory = "" +var cleanupDependencies = false +var cleanupCompilationFiles = false +var cleanupCompiledPackages = false +var cleanupFetchedPackages = false func main() { err := bpmlib.ReadConfig() @@ -456,51 +460,58 @@ func resolveCommand() { log.Fatalf("Error: this subcommand needs to be run with superuser permissions") } - // Read local databases - err := bpmlib.ReadLocalDatabases() + err := bpmlib.CleanupCache(rootDir, cleanupCompilationFiles, cleanupCompiledPackages, cleanupFetchedPackages, verbose) if err != nil { - log.Fatalf("Error: could not read local databases: %s", err) + log.Fatalf("Error: could not complete cache cleanup: %s", err) } - // Create cleanup operation - operation, err := bpmlib.CleanupPackages(rootDir, verbose) - if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { - log.Fatalf("Error: %s", err) - } else if err != nil { - log.Fatalf("Error: could not setup operation: %s\n", err) - } - - // Exit if operation contains no actions - if len(operation.Actions) == 0 { - fmt.Println("No action needs to be taken") - return - } - - // Show operation summary - operation.ShowOperationSummary() - - // 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" { - fmt.Println("Cancelling package removal...") - os.Exit(1) + if cleanupDependencies { + // Read local databases + err := bpmlib.ReadLocalDatabases() + if err != nil { + log.Fatalf("Error: could not read local databases: %s", err) } - } - // Execute operation - err = operation.Execute(verbose, force) - if err != nil { - log.Fatalf("Error: could not complete operation: %s\n", err) - } + // Create cleanup operation + operation, err := bpmlib.CleanupPackages(rootDir, verbose) + if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { + log.Fatalf("Error: %s", err) + } else if err != nil { + log.Fatalf("Error: could not setup operation: %s\n", err) + } - // Executing hooks - fmt.Println("Running hooks...") - err = operation.RunHooks(verbose) - if err != nil { - log.Fatalf("Error: could not run hooks: %s\n", err) + // Exit if operation contains no actions + if len(operation.Actions) == 0 { + fmt.Println("No action needs to be taken") + return + } + + // Show operation summary + operation.ShowOperationSummary() + + // 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" { + fmt.Println("Cancelling package removal...") + os.Exit(1) + } + } + + // Execute operation + err = operation.Execute(verbose, force) + if err != nil { + log.Fatalf("Error: could not complete operation: %s\n", err) + } + + // Executing hooks + fmt.Println("Running hooks...") + err = operation.RunHooks(verbose) + if err != nil { + log.Fatalf("Error: could not run hooks: %s\n", err) + } } case file: files := subcommandArgs @@ -757,10 +768,14 @@ func printHelp() { fmt.Println(" -y skips the confirmation prompt") fmt.Println(" --unused removes only packages that aren't required as dependencies by other packages") fmt.Println(" --cleanup performs a dependency cleanup") - fmt.Println("-> bpm cleanup [-R, -v, -y] | remove all unused dependency packages") + fmt.Println("-> bpm cleanup [-R, -v, -y, --depends, --compilation-files, --compiled-pkgs, --fetched-pkgs] | remove all unused dependencies and cache directories") fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -y skips the confirmation prompt") + fmt.Println(" --depends performs a dependency cleanup") + fmt.Println(" --compilation-files performs a cleanup of compilation files") + fmt.Println(" --compiled-pkgs performs a cleanup of compilation compiled binary packages") + fmt.Println(" --fetched-pkgs performs a cleanup of fetched packages from repositories") fmt.Println("-> bpm file [-R] | shows what packages the following packages are managed by") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println("-> bpm compile [-d, -s, -o] | Compile source BPM package") @@ -823,6 +838,10 @@ func resolveFlags() { cleanupFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root") cleanupFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing") cleanupFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts") + cleanupFlagSet.BoolVar(&cleanupDependencies, "depends", false, "Perform a dependency cleanup") + cleanupFlagSet.BoolVar(&cleanupCompilationFiles, "compilation-files", false, "Perform a cleanup of compilation files") + cleanupFlagSet.BoolVar(&cleanupCompiledPackages, "compiled-pkgs", false, "Perform a cleanup of compilation compiled binary packages") + cleanupFlagSet.BoolVar(&cleanupFetchedPackages, "fetched-pkgs", false, "Perform a cleanup of fetched packages from repositories") cleanupFlagSet.Usage = printHelp // File flags fileFlagSet := flag.NewFlagSet("Remove flags", flag.ExitOnError) @@ -883,6 +902,12 @@ func resolveFlags() { if err != nil { return } + if !cleanupDependencies && !cleanupCompilationFiles && !cleanupCompiledPackages && !cleanupFetchedPackages { + cleanupDependencies = true + cleanupCompilationFiles = true + cleanupCompiledPackages = true + cleanupFetchedPackages = true + } subcommandArgs = cleanupFlagSet.Args() } else if getCommandType() == file { err := fileFlagSet.Parse(subcommandArgs) diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index 1d8b05a..8e4fcbe 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "os" + "path" "slices" ) @@ -187,6 +188,88 @@ func CleanupPackages(rootDir string, verbose bool) (operation *BPMOperation, err return operation, nil } +func CleanupCache(rootDir string, cleanupCompilationFiles, cleanupCompiledPackages, cleanupFetchedPackages, verbose bool) error { + if cleanupCompilationFiles { + globalCompilationCacheDir := path.Join(rootDir, "var/cache/bpm/compilation") + + // Ensure path exists and is a directory + if stat, err := os.Stat(globalCompilationCacheDir); err == nil && stat.IsDir() { + // Delete directory + if verbose { + log.Printf("Removing directory (%s)\n", globalCompilationCacheDir) + } + err = os.RemoveAll(globalCompilationCacheDir) + if err != nil { + return err + } + } + + // Get home directories of users in root directory + homeDirs, err := os.ReadDir(path.Join(rootDir, "home")) + if err != nil { + return err + } + + // Loop through all home directories + for _, homeDir := range homeDirs { + // Skip if not a directory + if !homeDir.IsDir() { + continue + } + + localCompilationDir := path.Join(rootDir, "home", homeDir.Name(), ".cache/bpm/compilation") + + // Ensure path exists and is a directory + if stat, err := os.Stat(localCompilationDir); err != nil || !stat.IsDir() { + continue + } + + // Delete directory + if verbose { + log.Printf("Removing directory (%s)\n", localCompilationDir) + } + err = os.RemoveAll(localCompilationDir) + if err != nil { + return err + } + } + } + + if cleanupCompiledPackages { + dirToRemove := path.Join(rootDir, "var/cache/bpm/compiled") + + // Ensure path exists and is a directory + if stat, err := os.Stat(dirToRemove); err == nil && stat.IsDir() { + // Delete directory + if verbose { + log.Printf("Removing directory (%s)\n", dirToRemove) + } + err = os.RemoveAll(dirToRemove) + if err != nil { + return err + } + } + } + + if cleanupFetchedPackages { + dirToRemove := path.Join(rootDir, "var/cache/bpm/fetched") + + // Ensure path exists and is a directory + if stat, err := os.Stat(dirToRemove); err == nil && stat.IsDir() { + // Delete directory + if verbose { + log.Printf("Removing directory (%s)\n", dirToRemove) + } + err = os.RemoveAll(dirToRemove) + if err != nil { + return err + } + } + } + + return nil +} + // UpdatePackages fetches the newest versions of all installed packages from func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependencies, forceInstallation, verbose bool) (operation *BPMOperation, err error) { // Sync repositories From b07df8c7df53d089367fcc657ce2bdbb1a6a6f70 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sat, 3 May 2025 17:30:27 +0300 Subject: [PATCH 62/88] 'compile' subcommand will now detect if virtual package is installed --- src/bpm/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bpm/main.go b/src/bpm/main.go index 92274de..88b581d 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -608,6 +608,8 @@ func resolveCommand() { for i := len(unmetDepends) - 1; i >= 0; i-- { if slices.Contains(installedPackages, unmetDepends[i]) { unmetDepends = append(unmetDepends[:i], unmetDepends[i+1:]...) + } else if ok, _ := bpmlib.IsVirtualPackage(unmetDepends[i], rootDir); ok { + unmetDepends = append(unmetDepends[:i], unmetDepends[i+1:]...) } } From a32dfba54a8f17db1ea7baff417a8374a7f71eaa Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sun, 4 May 2025 21:03:05 +0300 Subject: [PATCH 63/88] Allow split package to have same name as main package --- src/bpmlib/packages.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index 6e510a8..f1e8275 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -451,8 +451,8 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) { // Setup split package information for i, splitPkg := range pkgInfo.SplitPackages { - // Ensure split package contains a name and one that is different from the main package name - if splitPkg.Name == "" || splitPkg.Name == pkgInfo.Name { + // Ensure split package contains a name + if splitPkg.Name == "" { return nil, fmt.Errorf("invalid split package name: %s", splitPkg.Name) } From f0709ff410ed80cb2d3303f1b226ded5b4baf032 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 8 May 2025 12:44:55 +0300 Subject: [PATCH 64/88] Fix 'prepare' function running in source directory during compilation --- src/bpmlib/compilation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index 6e72b2c..f35b431 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -143,7 +143,7 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo "set -a\n"+ // Source and export functions and variables in source.sh script ". \"${BPM_WORKDIR}\"/source.sh\n"+ "set +a\n"+ - "[[ $(type -t prepare) == \"function\" ]] && { echo \"Running prepare() function\"; bash -e -c 'cd \"$BPM_SOURCE\" && prepare' || exit 1; }\n"+ // Run prepare() function if it exists + "[[ $(type -t prepare) == \"function\" ]] && { echo \"Running prepare() function\"; bash -e -c 'cd \"$BPM_WORKDIR\" && prepare' || exit 1; }\n"+ // Run prepare() function if it exists "[[ $(type -t build) == \"function\" ]] && { echo \"Running build() function\"; bash -e -c 'cd \"$BPM_SOURCE\" && build' || exit 1; }\n"+ // Run build() function if it exists "exit 0") cmd.Dir = tempDirectory From 97d5a06ce7628fe1024d97b8655a018a11f8110e Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 8 May 2025 15:12:06 +0300 Subject: [PATCH 65/88] Remove force setting of URL field in split packages --- src/bpmlib/packages.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index f1e8275..503830b 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -476,10 +476,9 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) { return nil, err } - // Force set split package version, revision and URL + // Force set split package version, revision pkgInfo.SplitPackages[i].Version = pkgInfo.Version pkgInfo.SplitPackages[i].Revision = pkgInfo.Revision - pkgInfo.SplitPackages[i].Url = pkgInfo.Url } return pkgInfo, nil From f2bc7c8968b4fa9a16ffcd2fc1060621a0fa9d3c Mon Sep 17 00:00:00 2001 From: EnumDev Date: Fri, 9 May 2025 14:36:08 +0300 Subject: [PATCH 66/88] Fix not reading local databases after reloading config during package update --- src/bpmlib/general.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index 8e4fcbe..b63ec52 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -281,12 +281,16 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci if verbose { fmt.Println("All package databases synced successfully!") } - } - // Reload config and local databases - err = ReadConfig() - if err != nil { - return nil, fmt.Errorf("could not read BPM config: %s", err) + // Reload config and local databases + err = ReadConfig() + if err != nil { + return nil, fmt.Errorf("could not read BPM config: %s", err) + } + err = ReadLocalDatabases() + if err != nil { + return nil, fmt.Errorf("could not read local databases: %s", err) + } } // Get installed packages and check for updates From 5f03a6a1ad9b5b1911bcabfde26591ba7e230077 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Tue, 13 May 2025 20:26:55 +0300 Subject: [PATCH 67/88] Redo dependency resolution and cache installed package information --- src/bpm/main.go | 4 +- src/bpmlib/dependencies.go | 164 ++++++++++++++++ src/bpmlib/general.go | 4 +- src/bpmlib/hooks.go | 2 +- src/bpmlib/installed_packages.go | 243 ++++++++++++++++++++++++ src/bpmlib/operations.go | 10 +- src/bpmlib/packages.go | 312 +------------------------------ 7 files changed, 420 insertions(+), 319 deletions(-) create mode 100644 src/bpmlib/dependencies.go create mode 100644 src/bpmlib/installed_packages.go diff --git a/src/bpm/main.go b/src/bpm/main.go index 88b581d..92b4dd8 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -547,7 +547,7 @@ func resolveCommand() { var pkgList []string for _, pkg := range pkgs { - if slices.ContainsFunc(bpmlib.GetPackageFiles(pkg, rootDir), func(entry *bpmlib.PackageFileEntry) bool { + if slices.ContainsFunc(bpmlib.GetPackage(pkg, rootDir).PkgFiles, func(entry *bpmlib.PackageFileEntry) bool { return entry.Path == absFile }) { pkgList = append(pkgList, pkg) @@ -593,7 +593,7 @@ func resolveCommand() { // Get direct runtime and make dependencies totalDepends := make([]string, 0) - for _, depend := range bpmpkg.PkgInfo.GetAllDependencies(true, false) { + for _, depend := range bpmpkg.PkgInfo.GetDependencies(true, false) { if !slices.Contains(totalDepends, depend) { totalDepends = append(totalDepends, depend) } diff --git a/src/bpmlib/dependencies.go b/src/bpmlib/dependencies.go new file mode 100644 index 0000000..d71a105 --- /dev/null +++ b/src/bpmlib/dependencies.go @@ -0,0 +1,164 @@ +package bpmlib + +import ( + "errors" + "fmt" + "slices" +) + +func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeOptionalDepends bool) []string { + allDepends := make([]string, 0) + allDepends = append(allDepends, pkgInfo.Depends...) + if includeMakeDepends { + allDepends = append(allDepends, pkgInfo.MakeDepends...) + } + if includeOptionalDepends { + allDepends = append(allDepends, pkgInfo.OptionalDepends...) + } + return allDepends +} + +func (pkgInfo *PackageInfo) GetAllDependencies(includeMakeDepends, includeOptionalDepends bool, rootDir string) (resolved []string) { + // Initialize slices + resolved = make([]string, 0) + unresolved := make([]string, 0) + + // Call unexported function + pkgInfo.getAllDependencies(&resolved, &unresolved, includeMakeDepends, includeOptionalDepends, rootDir) + + return resolved +} + +func (pkgInfo *PackageInfo) getAllDependencies(resolved *[]string, unresolved *[]string, includeMakeDepends, includeOptionalDepends bool, rootDir string) { + // Add current package name to unresolved slice + *unresolved = append(*unresolved, pkgInfo.Name) + + // Loop through all dependencies + for _, depend := range pkgInfo.GetDependencies(includeMakeDepends, includeOptionalDepends) { + if !slices.Contains(*resolved, depend) { + // Add current dependency to resolved slice when circular dependency is detected + if slices.Contains(*unresolved, depend) { + if !slices.Contains(*resolved, depend) { + *resolved = append(*resolved, depend) + } + continue + } + + var dependInfo *PackageInfo + + if isVirtual, p := IsVirtualPackage(depend, rootDir); isVirtual { + dependInfo = GetPackageInfo(p, rootDir) + } else { + dependInfo = GetPackageInfo(depend, rootDir) + } + + if dependInfo != nil { + dependInfo.getAllDependencies(resolved, unresolved, includeMakeDepends, includeOptionalDepends, rootDir) + } + } + } + if !slices.Contains(*resolved, pkgInfo.Name) { + *resolved = append(*resolved, pkgInfo.Name) + } + *unresolved = stringSliceRemove(*unresolved, pkgInfo.Name) +} + +func ResolvePackageDependenciesFromDatabases(pkgInfo *PackageInfo, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) (resolved []string, unresolved []string) { + // Initialize slices + resolved = make([]string, 0) + unresolved = make([]string, 0) + + // Call unexported function + resolvePackageDependenciesFromDatabase(&resolved, &unresolved, pkgInfo, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) + + return resolved, unresolved +} + +func resolvePackageDependenciesFromDatabase(resolved, unresolved *[]string, pkgInfo *PackageInfo, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) { + // Add current package name to unresolved slice + *unresolved = append(*unresolved, pkgInfo.Name) + + // Loop through all dependencies + for _, depend := range pkgInfo.GetDependencies(checkMake, checkOptional) { + if !slices.Contains(*resolved, depend) { + // Add current dependency to resolved slice when circular dependency is detected + if slices.Contains(*unresolved, depend) { + if verbose { + fmt.Printf("Circular dependency was detected (%s -> %s). Installing %s first\n", pkgInfo.Name, depend, depend) + } + if !slices.Contains(*resolved, depend) { + *resolved = append(*resolved, depend) + } + continue + } else if ignoreInstalled && IsPackageProvided(depend, rootDir) { + continue + } + var err error + var entry *RepositoryEntry + entry, _, err = GetRepositoryEntry(depend) + if err != nil { + if entry = ResolveVirtualPackage(depend); entry == nil { + if !slices.Contains(*unresolved, depend) { + *unresolved = append(*unresolved, depend) + } + continue + } + } + resolvePackageDependenciesFromDatabase(resolved, unresolved, entry.Info, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) + } + } + if !slices.Contains(*resolved, pkgInfo.Name) { + *resolved = append(*resolved, pkgInfo.Name) + } + *unresolved = stringSliceRemove(*unresolved, pkgInfo.Name) +} + +func GetPackageDependants(pkgName string, rootDir string) ([]string, error) { + ret := make([]string, 0) + + // Get BPM package + pkg := GetPackage(pkgName, rootDir) + if pkg == nil { + return nil, errors.New("package not found: " + pkgName) + } + + // Get installed package names + pkgs, err := GetInstalledPackages(rootDir) + if err != nil { + return nil, errors.New("could not get installed packages") + } + + // Loop through all installed packages + for _, installedPkgName := range pkgs { + // Get installed BPM package + installedPkg := GetPackage(installedPkgName, rootDir) + if installedPkg == nil { + return nil, errors.New("package not found: " + installedPkgName) + } + + // Skip iteration if comparing the same packages + if installedPkg.PkgInfo.Name == pkgName { + continue + } + + // Get installed package dependencies + dependencies := installedPkg.PkgInfo.GetDependencies(false, true) + + // Add installed package to list if its dependencies include pkgName + if slices.Contains(dependencies, pkgName) { + ret = append(ret, installedPkgName) + continue + } + + // 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.Contains(dependencies, vpkg) { + ret = append(ret, installedPkgName) + break + } + } + } + + return ret, nil +} diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index b63ec52..c41cac5 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -161,7 +161,7 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie // Do package cleanup if cleanupDependencies { - err := operation.Cleanup(verbose) + err := operation.Cleanup() if err != nil { return nil, fmt.Errorf("could not perform cleanup for operation: %s", err) } @@ -180,7 +180,7 @@ func CleanupPackages(rootDir string, verbose bool) (operation *BPMOperation, err } // Do package cleanup - err = operation.Cleanup(verbose) + err = operation.Cleanup() if err != nil { return nil, fmt.Errorf("could not perform cleanup for operation: %s", err) } diff --git a/src/bpmlib/hooks.go b/src/bpmlib/hooks.go index a9bbff4..cd966cd 100644 --- a/src/bpmlib/hooks.go +++ b/src/bpmlib/hooks.go @@ -99,7 +99,7 @@ func (hook *BPMHook) Execute(packageChanges map[string]string, verbose bool, roo // Get modified files slice modifiedFiles := make([]*PackageFileEntry, 0) for pkg := range packageChanges { - modifiedFiles = append(modifiedFiles, GetPackageFiles(pkg, rootDir)...) + modifiedFiles = append(modifiedFiles, GetPackage(pkg, rootDir).PkgFiles...) } // Check if any targets are met diff --git a/src/bpmlib/installed_packages.go b/src/bpmlib/installed_packages.go new file mode 100644 index 0000000..dc295f1 --- /dev/null +++ b/src/bpmlib/installed_packages.go @@ -0,0 +1,243 @@ +package bpmlib + +import ( + "errors" + "fmt" + "os" + "path" + "slices" + "strconv" + "strings" +) + +var localPackageInformation map[string]map[string]*BPMPackage = make(map[string]map[string]*BPMPackage) + +func initializeLocalPackageInformation(rootDir string) (err error) { + // Return if information is already initialized + if _, ok := localPackageInformation[rootDir]; ok { + return nil + } + + tempPackageInformation := make(map[string]*BPMPackage) + + // Get path to installed package information directory + installedDir := path.Join(rootDir, "var/lib/bpm/installed/") + + // Get directory content + items, err := os.ReadDir(installedDir) + if os.IsNotExist(err) { + localPackageInformation[rootDir] = make(map[string]*BPMPackage) + return nil + } + if err != nil { + return err + } + + // Loop through each subdirectory + for _, item := range items { + // Skip if not a directory + if !item.IsDir() { + continue + } + + // Read package info + infoData, err := os.ReadFile(path.Join(installedDir, item.Name(), "info")) + if err != nil { + return err + } + info, err := ReadPackageInfo(string(infoData)) + if err != nil { + return err + } + + // Read package files + files := getPackageFiles(info.Name, rootDir) + + // Add package to slice + tempPackageInformation[info.Name] = &BPMPackage{ + PkgInfo: info, + PkgFiles: files, + } + } + + localPackageInformation[rootDir] = tempPackageInformation + return nil +} + +func GetInstalledPackages(rootDir string) (ret []string, err error) { + // Initialize local package information + err = initializeLocalPackageInformation(rootDir) + if err != nil { + return nil, err + } + + // Loop through each package and add it to slice + for _, bpmpkg := range localPackageInformation[rootDir] { + ret = append(ret, bpmpkg.PkgInfo.Name) + } + + return ret, nil +} + +func IsPackageInstalled(pkg, rootDir string) bool { + // Initialize local package information + err := initializeLocalPackageInformation(rootDir) + if err != nil { + return false + } + + if _, ok := localPackageInformation[rootDir][pkg]; !ok { + return false + } + 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) { + pkgs, err := GetInstalledPackages(rootDir) + if err != nil { + return false, "" + } + for _, p := range pkgs { + if p == pkg { + return false, "" + } + i := GetPackageInfo(p, rootDir) + if i == nil { + continue + } + if slices.Contains(i.Provides, pkg) { + return true, p + } + } + return false, "" +} + +func IsPackageProvided(pkg, rootDir string) bool { + pkgs, err := GetInstalledPackages(rootDir) + if err != nil { + return false + } + for _, p := range pkgs { + if p == pkg { + return true + } + i := GetPackageInfo(p, rootDir) + if i == nil { + continue + } + if slices.Contains(i.Provides, pkg) { + return true + } + } + return false +} + +func GetPackage(pkg, rootDir string) *BPMPackage { + err := initializeLocalPackageInformation(rootDir) + if err != nil { + return nil + } + + bpmpkg := localPackageInformation[rootDir][pkg] + + return bpmpkg +} + +func GetAllPackageFiles(rootDir string, excludePackages ...string) (map[string][]*BPMPackage, error) { + ret := make(map[string][]*BPMPackage) + + pkgNames, err := GetInstalledPackages(rootDir) + if err != nil { + return nil, err + } + + for _, pkgName := range pkgNames { + if slices.Contains(excludePackages, pkgName) { + continue + } + bpmpkg := GetPackage(pkgName, rootDir) + if bpmpkg == nil { + return nil, errors.New(fmt.Sprintf("could not get BPM package (%s)", pkgName)) + } + for _, entry := range bpmpkg.PkgFiles { + if _, ok := ret[entry.Path]; ok { + ret[entry.Path] = append(ret[entry.Path], bpmpkg) + } else { + ret[entry.Path] = []*BPMPackage{bpmpkg} + } + } + } + + return ret, nil +} + +func getPackageFiles(pkg, rootDir string) []*PackageFileEntry { + var pkgFiles []*PackageFileEntry + installedDir := path.Join(rootDir, "var/lib/bpm/installed/") + pkgDir := path.Join(installedDir, pkg) + files := path.Join(pkgDir, "files") + if _, err := os.Stat(installedDir); os.IsNotExist(err) { + return nil + } + if _, err := os.Stat(pkgDir); os.IsNotExist(err) { + return nil + } + bs, err := os.ReadFile(files) + if err != nil { + return nil + } + + for _, line := range strings.Split(string(bs), "\n") { + if strings.TrimSpace(line) == "" { + continue + } + stringEntry := strings.Split(strings.TrimSpace(line), " ") + if len(stringEntry) < 5 { + pkgFiles = append(pkgFiles, &PackageFileEntry{ + Path: strings.TrimSuffix(line, "/"), + OctalPerms: 0, + UserID: 0, + GroupID: 0, + SizeInBytes: 0, + }) + continue + } + uid, err := strconv.ParseInt(stringEntry[len(stringEntry)-4], 0, 32) + if err != nil { + return nil + } + octalPerms, err := strconv.ParseInt(stringEntry[len(stringEntry)-3], 0, 32) + if err != nil { + return nil + } + gid, err := strconv.ParseInt(stringEntry[len(stringEntry)-2], 0, 32) + if err != nil { + return nil + } + size, err := strconv.ParseUint(stringEntry[len(stringEntry)-1], 0, 64) + if err != nil { + return nil + } + pkgFiles = append(pkgFiles, &PackageFileEntry{ + Path: strings.TrimSuffix(strings.Join(stringEntry[:len(stringEntry)-4], " "), "/"), + OctalPerms: uint32(octalPerms), + UserID: int(uid), + GroupID: int(gid), + SizeInBytes: size, + }) + } + + return pkgFiles +} diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index 0a3648b..0a50e41 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -139,7 +139,7 @@ func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, instal continue } - resolved, unresolved := pkgInfo.ResolveDependencies(&[]string{}, &[]string{}, pkgInfo.Type == "source", installOptionalDependencies, !reinstallDependencies, verbose, operation.RootDir) + resolved, unresolved := ResolvePackageDependenciesFromDatabases(pkgInfo, pkgInfo.Type == "source", installOptionalDependencies, !reinstallDependencies, verbose, operation.RootDir) operation.UnresolvedDepends = append(operation.UnresolvedDepends, unresolved...) @@ -174,7 +174,7 @@ func (operation *BPMOperation) RemoveNeededPackages() error { } for pkg, action := range removeActions { - dependants, err := action.BpmPackage.PkgInfo.GetDependants(operation.RootDir) + dependants, err := GetPackageDependants(action.BpmPackage.PkgInfo.Name, operation.RootDir) if err != nil { return errors.New("could not get dependant packages for package (" + pkg + ")") } @@ -192,7 +192,7 @@ func (operation *BPMOperation) RemoveNeededPackages() error { return nil } -func (operation *BPMOperation) Cleanup(verbose bool) error { +func (operation *BPMOperation) Cleanup() error { // Get all installed packages installedPackageNames, err := GetInstalledPackages(operation.RootDir) if err != nil { @@ -228,9 +228,9 @@ func (operation *BPMOperation) Cleanup(verbose bool) error { } keepPackages = append(keepPackages, pkg.Name) - resolved, _ := pkg.ResolveDependencies(&[]string{}, &[]string{}, false, true, false, verbose, operation.RootDir) + resolved := pkg.GetAllDependencies(false, true, operation.RootDir) for _, value := range resolved { - if !slices.Contains(keepPackages, value) && slices.Contains(installedPackageNames, value) { + if !slices.Contains(keepPackages, value) { keepPackages = append(keepPackages, value) } } diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index 503830b..6ea2b56 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -509,7 +509,7 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p appendArray("Make Dependencies", pkgInfo.MakeDepends) } appendArray("Optional dependencies", pkgInfo.OptionalDepends) - dependants, err := pkgInfo.GetDependants(rootDir) + dependants, err := GetPackageDependants(pkgInfo.Name, rootDir) if err == nil { appendArray("Dependant packages", dependants) } @@ -681,7 +681,7 @@ func installPackage(filename, rootDir string, verbose, force bool) error { // Check if package is installed and remove current files if packageInstalled { // Fetching and reversing package file entry list - fileEntries := GetPackageFiles(bpmpkg.PkgInfo.Name, rootDir) + fileEntries := GetPackage(bpmpkg.PkgInfo.Name, rootDir).PkgFiles sort.Slice(fileEntries, func(i, j int) bool { return fileEntries[i].Path < fileEntries[j].Path }) @@ -873,312 +873,6 @@ func installPackage(filename, rootDir string, verbose, force bool) error { return nil } -func (pkgInfo *PackageInfo) GetAllDependencies(checkMake, checkOptional bool) []string { - allDepends := make([]string, 0) - allDepends = append(allDepends, pkgInfo.Depends...) - if checkMake { - allDepends = append(allDepends, pkgInfo.MakeDepends...) - } - if checkOptional { - allDepends = append(allDepends, pkgInfo.OptionalDepends...) - } - return allDepends -} - -func (pkgInfo *PackageInfo) CheckDependencies(checkMake, checkOptional bool, rootDir string) []string { - var ret []string - for _, dependency := range pkgInfo.Depends { - if !IsPackageProvided(dependency, rootDir) { - ret = append(ret, dependency) - } - } - if checkMake { - for _, dependency := range pkgInfo.MakeDepends { - if !IsPackageProvided(dependency, rootDir) { - ret = append(ret, dependency) - } - } - } - if checkOptional { - for _, dependency := range pkgInfo.OptionalDepends { - if !IsPackageProvided(dependency, rootDir) { - ret = append(ret, dependency) - } - } - } - - return ret -} - -func (pkgInfo *PackageInfo) GetDependants(rootDir string) ([]string, error) { - ret := make([]string, 0) - - pkgs, err := GetInstalledPackages(rootDir) - if err != nil { - return nil, errors.New("could not get installed packages") - } - for _, pkg := range pkgs { - bpmpkg := GetPackage(pkg, rootDir) - if bpmpkg == nil { - return nil, errors.New("package not found: " + pkg) - } - if bpmpkg.PkgInfo.Name == pkgInfo.Name { - continue - } - - dependencies := bpmpkg.PkgInfo.GetAllDependencies(false, true) - - if slices.Contains(dependencies, pkgInfo.Name) { - ret = append(ret, pkg) - continue - } - for _, vpkg := range pkgInfo.Provides { - if slices.Contains(dependencies, vpkg) { - ret = append(ret, pkg) - break - } - } - } - - return ret, nil -} - -func (pkgInfo *PackageInfo) CheckConflicts(rootDir string) []string { - var ret []string - for _, conflict := range pkgInfo.Conflicts { - if IsPackageInstalled(conflict, rootDir) { - ret = append(ret, conflict) - } - } - return ret -} - -func (pkgInfo *PackageInfo) ResolveDependencies(resolved, unresolved *[]string, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) ([]string, []string) { - *unresolved = append(*unresolved, pkgInfo.Name) - for _, depend := range pkgInfo.GetAllDependencies(checkMake, checkOptional) { - depend = strings.TrimSpace(depend) - depend = strings.ToLower(depend) - if !slices.Contains(*resolved, depend) { - if slices.Contains(*unresolved, depend) { - if verbose { - fmt.Printf("Circular dependency was detected (%s -> %s). Installing %s first\n", pkgInfo.Name, depend, depend) - } - if !slices.Contains(*resolved, depend) { - *resolved = append(*resolved, depend) - } - continue - } else if ignoreInstalled && IsPackageProvided(depend, rootDir) { - continue - } - var err error - var entry *RepositoryEntry - entry, _, err = GetRepositoryEntry(depend) - if err != nil { - if entry = ResolveVirtualPackage(depend); entry == nil { - if !slices.Contains(*unresolved, depend) { - *unresolved = append(*unresolved, depend) - } - continue - } - } - entry.Info.ResolveDependencies(resolved, unresolved, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) - } - } - if !slices.Contains(*resolved, pkgInfo.Name) { - *resolved = append(*resolved, pkgInfo.Name) - } - *unresolved = stringSliceRemove(*unresolved, pkgInfo.Name) - return *resolved, *unresolved -} - -func IsPackageInstalled(pkg, rootDir string) bool { - installedDir := path.Join(rootDir, "var/lib/bpm/installed/") - pkgDir := path.Join(installedDir, pkg) - if _, err := os.Stat(pkgDir); err != nil { - return false - } - return true -} - -func IsVirtualPackage(pkg, rootDir string) (bool, string) { - pkgs, err := GetInstalledPackages(rootDir) - if err != nil { - return false, "" - } - for _, p := range pkgs { - if p == pkg { - return false, "" - } - i := GetPackageInfo(p, rootDir) - if i == nil { - continue - } - if slices.Contains(i.Provides, pkg) { - return true, p - } - } - return false, "" -} - -func IsPackageProvided(pkg, rootDir string) bool { - pkgs, err := GetInstalledPackages(rootDir) - if err != nil { - return false - } - for _, p := range pkgs { - if p == pkg { - return true - } - i := GetPackageInfo(p, rootDir) - if i == nil { - continue - } - if slices.Contains(i.Provides, pkg) { - return true - } - } - return false -} - -func GetInstalledPackages(rootDir string) ([]string, error) { - installedDir := path.Join(rootDir, "var/lib/bpm/installed/") - items, err := os.ReadDir(installedDir) - if os.IsNotExist(err) { - return nil, nil - } - if err != nil { - return nil, err - } - var ret []string - for _, item := range items { - ret = append(ret, item.Name()) - } - return ret, nil -} - -func GetPackageFiles(pkg, rootDir string) []*PackageFileEntry { - var pkgFiles []*PackageFileEntry - installedDir := path.Join(rootDir, "var/lib/bpm/installed/") - pkgDir := path.Join(installedDir, pkg) - files := path.Join(pkgDir, "files") - if _, err := os.Stat(installedDir); os.IsNotExist(err) { - return nil - } - if _, err := os.Stat(pkgDir); os.IsNotExist(err) { - return nil - } - bs, err := os.ReadFile(files) - if err != nil { - return nil - } - - for _, line := range strings.Split(string(bs), "\n") { - if strings.TrimSpace(line) == "" { - continue - } - stringEntry := strings.Split(strings.TrimSpace(line), " ") - if len(stringEntry) < 5 { - pkgFiles = append(pkgFiles, &PackageFileEntry{ - Path: strings.TrimSuffix(line, "/"), - OctalPerms: 0, - UserID: 0, - GroupID: 0, - SizeInBytes: 0, - }) - continue - } - uid, err := strconv.ParseInt(stringEntry[len(stringEntry)-4], 0, 32) - if err != nil { - return nil - } - octalPerms, err := strconv.ParseInt(stringEntry[len(stringEntry)-3], 0, 32) - if err != nil { - return nil - } - gid, err := strconv.ParseInt(stringEntry[len(stringEntry)-2], 0, 32) - if err != nil { - return nil - } - size, err := strconv.ParseUint(stringEntry[len(stringEntry)-1], 0, 64) - if err != nil { - return nil - } - pkgFiles = append(pkgFiles, &PackageFileEntry{ - Path: strings.TrimSuffix(strings.Join(stringEntry[:len(stringEntry)-4], " "), "/"), - OctalPerms: uint32(octalPerms), - UserID: int(uid), - GroupID: int(gid), - SizeInBytes: size, - }) - } - - return pkgFiles -} - -func GetPackageInfo(pkg, rootDir string) *PackageInfo { - installedDir := path.Join(rootDir, "var/lib/bpm/installed/") - pkgDir := path.Join(installedDir, pkg) - files := path.Join(pkgDir, "info") - if _, err := os.Stat(installedDir); os.IsNotExist(err) { - return nil - } - if _, err := os.Stat(pkgDir); os.IsNotExist(err) { - return nil - } - file, err := os.Open(files) - if err != nil { - return nil - } - bs, err := io.ReadAll(file) - if err != nil { - return nil - } - info, err := ReadPackageInfo(string(bs)) - if err != nil { - return nil - } - return info -} - -func GetPackage(pkg, rootDir string) *BPMPackage { - if !IsPackageInstalled(pkg, rootDir) { - return nil - } - - return &BPMPackage{ - PkgInfo: GetPackageInfo(pkg, rootDir), - PkgFiles: GetPackageFiles(pkg, rootDir), - } -} - -func GetAllPackageFiles(rootDir string, excludePackages ...string) (map[string][]*BPMPackage, error) { - ret := make(map[string][]*BPMPackage) - - pkgNames, err := GetInstalledPackages(rootDir) - if err != nil { - return nil, err - } - - for _, pkgName := range pkgNames { - if slices.Contains(excludePackages, pkgName) { - continue - } - bpmpkg := GetPackage(pkgName, rootDir) - if bpmpkg == nil { - return nil, errors.New(fmt.Sprintf("could not get BPM package (%s)", pkgName)) - } - for _, entry := range bpmpkg.PkgFiles { - if _, ok := ret[entry.Path]; ok { - ret[entry.Path] = append(ret[entry.Path], bpmpkg) - } else { - ret[entry.Path] = []*BPMPackage{bpmpkg} - } - } - } - - return ret, nil -} - func removePackage(pkg string, verbose bool, rootDir string) error { installedDir := path.Join(rootDir, "var/lib/bpm/installed/") pkgDir := path.Join(installedDir, pkg) @@ -1206,7 +900,7 @@ func removePackage(pkg string, verbose bool, rootDir string) error { } // Fetching and reversing package file entry list - fileEntries := GetPackageFiles(pkg, rootDir) + fileEntries := GetPackage(pkg, rootDir).PkgFiles sort.Slice(fileEntries, func(i, j int) bool { return fileEntries[i].Path < fileEntries[j].Path }) From f20d7b66d1791511e175272e06fcb6f6374dbf02 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Tue, 13 May 2025 20:33:20 +0300 Subject: [PATCH 68/88] Remove unused verbose parameters --- src/bpm/main.go | 4 ++-- src/bpmlib/general.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index 92b4dd8..eaba541 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -415,7 +415,7 @@ func resolveCommand() { } // Create remove operation - operation, err := bpmlib.RemovePackages(rootDir, removeUnused, doCleanup, verbose, subcommandArgs...) + operation, err := bpmlib.RemovePackages(rootDir, removeUnused, doCleanup, subcommandArgs...) if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { log.Fatalf("Error: %s", err) } else if err != nil { @@ -473,7 +473,7 @@ func resolveCommand() { } // Create cleanup operation - operation, err := bpmlib.CleanupPackages(rootDir, verbose) + operation, err := bpmlib.CleanupPackages(rootDir) if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { log.Fatalf("Error: %s", err) } else if err != nil { diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index c41cac5..fd15a55 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -133,7 +133,7 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein } // RemovePackages removes the specified packages from the given root directory -func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencies, verbose bool, packages ...string) (operation *BPMOperation, err error) { +func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencies bool, packages ...string) (operation *BPMOperation, err error) { operation = &BPMOperation{ Actions: make([]OperationAction, 0), UnresolvedDepends: make([]string, 0), @@ -170,7 +170,7 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie } // CleanupPackages finds packages installed as dependencies which are no longer required by the rest of the system in the given root directory -func CleanupPackages(rootDir string, verbose bool) (operation *BPMOperation, err error) { +func CleanupPackages(rootDir string) (operation *BPMOperation, err error) { operation = &BPMOperation{ Actions: make([]OperationAction, 0), UnresolvedDepends: make([]string, 0), From d26d878308f69bdfd44ae024510a8d53158062d3 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Tue, 13 May 2025 20:44:16 +0300 Subject: [PATCH 69/88] Fix error when installing, updating or remove packages --- src/bpmlib/hooks.go | 5 ++++- src/bpmlib/packages.go | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/bpmlib/hooks.go b/src/bpmlib/hooks.go index cd966cd..d6e6d23 100644 --- a/src/bpmlib/hooks.go +++ b/src/bpmlib/hooks.go @@ -99,7 +99,10 @@ func (hook *BPMHook) Execute(packageChanges map[string]string, verbose bool, roo // Get modified files slice modifiedFiles := make([]*PackageFileEntry, 0) for pkg := range packageChanges { - modifiedFiles = append(modifiedFiles, GetPackage(pkg, rootDir).PkgFiles...) + if GetPackage(pkg, rootDir) != nil { + modifiedFiles = append(modifiedFiles, GetPackage(pkg, rootDir).PkgFiles...) + } + } // Check if any targets are met diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index 6ea2b56..eda206b 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -870,6 +870,16 @@ func installPackage(filename, rootDir string, verbose, force bool) error { return err } } + + // Ensure local package information has been initialized for rootDir + err = initializeLocalPackageInformation(rootDir) + if err != nil { + return err + } + + // Add or update package information for rootDir + localPackageInformation[rootDir][bpmpkg.PkgInfo.Name] = bpmpkg + return nil } @@ -999,5 +1009,14 @@ func removePackage(pkg string, verbose bool, rootDir string) error { return err } + // Ensure local package information has been initialized for rootDir + err = initializeLocalPackageInformation(rootDir) + if err != nil { + return err + } + + // Add or update package information for rootDir + delete(localPackageInformation[rootDir], pkgInfo.Name) + return nil } From bf2b4e95ac8fd26c9fb68c0ba8eaed5d97e19f0d Mon Sep 17 00:00:00 2001 From: EnumDev Date: Wed, 14 May 2025 15:01:28 +0300 Subject: [PATCH 70/88] Fix installation reason showing in 'info' subcommand when viewing not-installed package, database entry or BPM file --- src/bpm/main.go | 6 ++++-- src/bpmlib/packages.go | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index eaba541..32ef64b 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -124,6 +124,7 @@ func resolveCommand() { for n, pkg := range packages { var info *bpmlib.PackageInfo isFile := false + showInstallationReason := false if showRepoInfo { var err error var entry *bpmlib.RepositoryEntry @@ -147,6 +148,7 @@ func resolveCommand() { } else { info = bpmlib.GetPackageInfo(pkg, rootDir) } + showInstallationReason = true } if info == nil { log.Fatalf("Error: package (%s) is not installed\n", pkg) @@ -161,7 +163,7 @@ func resolveCommand() { } fmt.Println("File: " + abs) } - fmt.Println(bpmlib.CreateReadableInfo(true, true, true, info, rootDir)) + fmt.Println(bpmlib.CreateReadableInfo(true, true, true, showInstallationReason, info, rootDir)) } case list: // Read local databases @@ -195,7 +197,7 @@ func resolveCommand() { if n != 0 { fmt.Println() } - fmt.Println(bpmlib.CreateReadableInfo(true, true, true, info, rootDir)) + fmt.Println(bpmlib.CreateReadableInfo(true, true, true, true, info, rootDir)) } } case search: diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index eda206b..5c4471d 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -484,7 +484,7 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) { return pkgInfo, nil } -func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, pkgInfo *PackageInfo, rootDir string) string { +func CreateReadableInfo(showArchitecture, showType, showPackageRelations, showInstallationReason bool, pkgInfo *PackageInfo, rootDir string) string { ret := make([]string, 0) appendArray := func(label string, array []string) { if len(array) == 0 { @@ -524,7 +524,9 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p } appendArray("Split Packages", splitPkgs) } - ret = append(ret, "Installation Reason: "+string(GetInstallationReason(pkgInfo.Name, rootDir))) + if IsPackageInstalled(pkgInfo.Name, rootDir) && showInstallationReason { + ret = append(ret, "Installation Reason: "+string(GetInstallationReason(pkgInfo.Name, rootDir))) + } return strings.Join(ret, "\n") } From 815d0bb29a8fe027290c13b3bf319e9f6666a28d Mon Sep 17 00:00:00 2001 From: EnumDev Date: Wed, 14 May 2025 16:33:30 +0300 Subject: [PATCH 71/88] Rename BPM Repositories to BPM Databases --- README.md | 4 +- config/bpm.conf | 6 +- src/bpm/main.go | 46 +++---- src/bpmlib/config.go | 14 +- src/bpmlib/{repositories.go => databases.go} | 128 +++++++++---------- src/bpmlib/dependencies.go | 4 +- src/bpmlib/errors.go | 4 +- src/bpmlib/general.go | 30 ++--- src/bpmlib/operations.go | 44 +++---- 9 files changed, 140 insertions(+), 140 deletions(-) rename src/bpmlib/{repositories.go => databases.go} (55%) diff --git a/README.md b/README.md index d49f03b..527b23d 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ You are able to install bpm packages by typing the following: ```sh bpm install /path/to/package.bpm ``` -You can also use the package name directly if using repositories +You can also use the package name directly if using databases ```sh bpm install package_name ``` @@ -50,7 +50,7 @@ To remove all unused dependencies and clean cached files try using the cleanup c bpm cleanup ``` -If using repositories, all packages can be updated using this simple command +If using databases, all packages can be updated using this simple command ```sh bpm update ``` diff --git a/config/bpm.conf b/config/bpm.conf index 6d31c3e..f32bc41 100644 --- a/config/bpm.conf +++ b/config/bpm.conf @@ -1,7 +1,7 @@ ignore_packages: [] privilege_escalator_cmd: "sudo" compilation_env: [] -repositories: - - name: example-repository - source: https://my-repo.xyz/ +databases: + - name: example-database + source: https://my-database.xyz/ disabled: true \ No newline at end of file diff --git a/src/bpm/main.go b/src/bpm/main.go index 32ef64b..329d8af 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -39,7 +39,7 @@ var installationReason = "" var nosync = true var removeUnused = false var doCleanup = false -var showRepoInfo = false +var showDatabaseInfo = false var installSrcPkgDepends = false var skipChecks = false var outputDirectory = "" @@ -116,7 +116,7 @@ func resolveCommand() { } // Read local databases - err := bpmlib.ReadLocalDatabases() + err := bpmlib.ReadLocalDatabaseFiles() if err != nil { log.Fatalf("Error: could not read local databases: %s", err) } @@ -125,13 +125,13 @@ func resolveCommand() { var info *bpmlib.PackageInfo isFile := false showInstallationReason := false - if showRepoInfo { + if showDatabaseInfo { var err error - var entry *bpmlib.RepositoryEntry - entry, _, err = bpmlib.GetRepositoryEntry(pkg) + var entry *bpmlib.BPMDatabaseEntry + entry, _, err = bpmlib.GetDatabaseEntry(pkg) if err != nil { if entry = bpmlib.ResolveVirtualPackage(pkg); entry == nil { - log.Fatalf("Error: could not find package (%s) in any repository\n", pkg) + log.Fatalf("Error: could not find package (%s) in any database\n", pkg) } } info = entry.Info @@ -167,7 +167,7 @@ func resolveCommand() { } case list: // Read local databases - err := bpmlib.ReadLocalDatabases() + err := bpmlib.ReadLocalDatabaseFiles() if err != nil { log.Fatalf("Error: could not read local databases: %s", err) } @@ -207,7 +207,7 @@ func resolveCommand() { } // Read local databases - err := bpmlib.ReadLocalDatabases() + err := bpmlib.ReadLocalDatabaseFiles() if err != nil { log.Fatalf("Error: could not read local databases: %s", err) } @@ -215,8 +215,8 @@ func resolveCommand() { for i, term := range searchTerms { nameResults := make([]*bpmlib.PackageInfo, 0) descResults := make([]*bpmlib.PackageInfo, 0) - for _, repo := range bpmlib.BPMConfig.Repositories { - for _, entry := range repo.Entries { + for _, db := range bpmlib.BPMConfig.Databases { + for _, entry := range db.Entries { if strings.Contains(entry.Info.Name, term) { nameResults = append(nameResults, entry.Info) } else if strings.Contains(entry.Info.Description, term) { @@ -271,7 +271,7 @@ func resolveCommand() { } // Read local databases - err := bpmlib.ReadLocalDatabases() + err := bpmlib.ReadLocalDatabaseFiles() if err != nil { log.Fatalf("Error: could not read local databases: %s", err) } @@ -329,7 +329,7 @@ func resolveCommand() { // Read local databases if no sync if nosync { - err := bpmlib.ReadLocalDatabases() + err := bpmlib.ReadLocalDatabaseFiles() if err != nil { log.Fatalf("Error: could not read local databases: %s", err) } @@ -411,7 +411,7 @@ func resolveCommand() { } // Read local databases - err := bpmlib.ReadLocalDatabases() + err := bpmlib.ReadLocalDatabaseFiles() if err != nil { log.Fatalf("Error: could not read local databases: %s", err) } @@ -469,7 +469,7 @@ func resolveCommand() { if cleanupDependencies { // Read local databases - err := bpmlib.ReadLocalDatabases() + err := bpmlib.ReadLocalDatabaseFiles() if err != nil { log.Fatalf("Error: could not read local databases: %s", err) } @@ -571,7 +571,7 @@ func resolveCommand() { } // Read local databases - err := bpmlib.ReadLocalDatabases() + err := bpmlib.ReadLocalDatabaseFiles() if err != nil { log.Fatalf("Error: could not read local databases: %s", err) } @@ -643,7 +643,7 @@ func resolveCommand() { } else { // Ensure the required dependencies are installed if len(unmetDepends) != 0 { - log.Fatalf("Error: could not resolve dependencies: the following dependencies were not found in any repositories: " + strings.Join(unmetDepends, ", ")) + log.Fatalf("Error: could not resolve dependencies: the following dependencies were not found in any databases: " + strings.Join(unmetDepends, ", ")) } } @@ -738,14 +738,14 @@ func printHelp() { fmt.Println("-> flags will be read if passed right after the subcommand otherwise they will be read as subcommand arguments") fmt.Println("\033[1m---- Command List ----\033[0m") fmt.Println("-> bpm version | shows information on the installed version of bpm") - fmt.Println("-> bpm info [-R, --repos] | shows information on an installed package") + fmt.Println("-> bpm info [-R, --databases] | shows information on an installed package") fmt.Println(" -R= lets you define the root path which will be used") - fmt.Println(" --repos show information on package in repository") + fmt.Println(" --databases show information on package in configured databases") fmt.Println("-> bpm list [-R, -c, -n] | lists all installed packages") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -c lists the amount of installed packages") fmt.Println(" -n lists only the names of installed packages") - fmt.Println("-> bpm search | Searches for packages through declared repositories") + fmt.Println("-> bpm search | Searches for packages through configured databases") fmt.Println("-> bpm install [-R, -v, -y, -f, --reinstall, --reinstall-all, --no-optional, --installation-reason] | installs the following files") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -v Show additional information about what BPM is doing") @@ -755,7 +755,7 @@ func printHelp() { fmt.Println(" --reinstall-all Same as --reinstall but also reinstalls dependencies") fmt.Println(" --no-optional Prevents installation of optional dependencies") fmt.Println(" --installation-reason= sets the installation reason for all newly installed packages") - fmt.Println("-> bpm update [-R, -v, -y, -f, --reinstall, --no-sync] | updates all packages that are available in the repositories") + fmt.Println("-> bpm update [-R, -v, -y, -f, --reinstall, --no-sync] | updates all packages that are available in the configured databases") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -v Show additional information about what BPM is doing") fmt.Println(" -y skips the confirmation prompt") @@ -779,7 +779,7 @@ func printHelp() { fmt.Println(" --depends performs a dependency cleanup") fmt.Println(" --compilation-files performs a cleanup of compilation files") fmt.Println(" --compiled-pkgs performs a cleanup of compilation compiled binary packages") - fmt.Println(" --fetched-pkgs performs a cleanup of fetched packages from repositories") + fmt.Println(" --fetched-pkgs performs a cleanup of fetched packages from databases") fmt.Println("-> bpm file [-R] | shows what packages the following packages are managed by") fmt.Println(" -R= lets you define the root path which will be used") fmt.Println("-> bpm compile [-d, -s, -o] | Compile source BPM package") @@ -802,7 +802,7 @@ func resolveFlags() { // Info flags infoFlagSet := flag.NewFlagSet("Info flags", flag.ExitOnError) infoFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root") - infoFlagSet.BoolVar(&showRepoInfo, "repos", false, "Show information on package in repository") + infoFlagSet.BoolVar(&showDatabaseInfo, "databases", false, "Show information on package in configured databases") infoFlagSet.Usage = printHelp // Install flags installFlagSet := flag.NewFlagSet("Install flags", flag.ExitOnError) @@ -845,7 +845,7 @@ func resolveFlags() { cleanupFlagSet.BoolVar(&cleanupDependencies, "depends", false, "Perform a dependency cleanup") cleanupFlagSet.BoolVar(&cleanupCompilationFiles, "compilation-files", false, "Perform a cleanup of compilation files") cleanupFlagSet.BoolVar(&cleanupCompiledPackages, "compiled-pkgs", false, "Perform a cleanup of compilation compiled binary packages") - cleanupFlagSet.BoolVar(&cleanupFetchedPackages, "fetched-pkgs", false, "Perform a cleanup of fetched packages from repositories") + cleanupFlagSet.BoolVar(&cleanupFetchedPackages, "fetched-pkgs", false, "Perform a cleanup of fetched packages from databases") cleanupFlagSet.Usage = printHelp // File flags fileFlagSet := flag.NewFlagSet("Remove flags", flag.ExitOnError) diff --git a/src/bpmlib/config.go b/src/bpmlib/config.go index fbf5fda..0c24e57 100644 --- a/src/bpmlib/config.go +++ b/src/bpmlib/config.go @@ -6,10 +6,10 @@ import ( ) type BPMConfigStruct struct { - IgnorePackages []string `yaml:"ignore_packages"` - PrivilegeEscalatorCmd string `yaml:"privilege_escalator_cmd"` - CompilationEnvironment []string `yaml:"compilation_env"` - Repositories []*Repository `yaml:"repositories"` + IgnorePackages []string `yaml:"ignore_packages"` + PrivilegeEscalatorCmd string `yaml:"privilege_escalator_cmd"` + CompilationEnvironment []string `yaml:"compilation_env"` + Databases []*BPMDatabase `yaml:"databases"` } var BPMConfig BPMConfigStruct @@ -30,9 +30,9 @@ func ReadConfig() (err error) { return err } - for i := len(BPMConfig.Repositories) - 1; i >= 0; i-- { - if BPMConfig.Repositories[i].Disabled != nil && *BPMConfig.Repositories[i].Disabled { - BPMConfig.Repositories = append(BPMConfig.Repositories[:i], BPMConfig.Repositories[i+1:]...) + for i := len(BPMConfig.Databases) - 1; i >= 0; i-- { + if BPMConfig.Databases[i].Disabled != nil && *BPMConfig.Databases[i].Disabled { + BPMConfig.Databases = append(BPMConfig.Databases[:i], BPMConfig.Databases[i+1:]...) } } diff --git a/src/bpmlib/repositories.go b/src/bpmlib/databases.go similarity index 55% rename from src/bpmlib/repositories.go rename to src/bpmlib/databases.go index cafc6b1..6b14195 100644 --- a/src/bpmlib/repositories.go +++ b/src/bpmlib/databases.go @@ -12,41 +12,41 @@ import ( "strings" ) -type Repository struct { +type BPMDatabase struct { Name string `yaml:"name"` Source string `yaml:"source"` Disabled *bool `yaml:"disabled"` - Entries map[string]*RepositoryEntry + Entries map[string]*BPMDatabaseEntry VirtualPackages map[string][]string } -type RepositoryEntry struct { +type BPMDatabaseEntry struct { Info *PackageInfo `yaml:"info"` Download string `yaml:"download"` DownloadSize uint64 `yaml:"download_size"` InstalledSize uint64 `yaml:"installed_size"` - Repository *Repository + Database *BPMDatabase } -func (repo *Repository) ContainsPackage(pkg string) bool { - _, ok := repo.Entries[pkg] +func (db *BPMDatabase) ContainsPackage(pkg string) bool { + _, ok := db.Entries[pkg] return ok } -func (repo *Repository) ReadLocalDatabase() error { - repoFile := "/var/lib/bpm/repositories/" + repo.Name + ".bpmdb" - if _, err := os.Stat(repoFile); err != nil { +func (db *BPMDatabase) ReadLocalDatabase() error { + dbFile := "/var/lib/bpm/databases/" + db.Name + ".bpmdb" + if _, err := os.Stat(dbFile); err != nil { return nil } - bytes, err := os.ReadFile(repoFile) + bytes, err := os.ReadFile(dbFile) if err != nil { return err } data := string(bytes) for _, b := range strings.Split(data, "---") { - entry := RepositoryEntry{ + entry := BPMDatabaseEntry{ Info: &PackageInfo{ Name: "", Description: "", @@ -66,14 +66,14 @@ func (repo *Repository) ReadLocalDatabase() error { Download: "", DownloadSize: 0, InstalledSize: 0, - Repository: repo, + Database: db, } err := yaml.Unmarshal([]byte(b), &entry) if err != nil { return err } - // Create repository entries + // Create database entries if entry.Info.IsSplitPackage() { for _, splitPkg := range entry.Info.SplitPackages { // Turn split package into json data @@ -100,26 +100,26 @@ func (repo *Repository) ReadLocalDatabase() error { splitPkgClone.Url = entry.Info.Url // Create entry for split package - repo.Entries[splitPkg.Name] = &RepositoryEntry{ + db.Entries[splitPkg.Name] = &BPMDatabaseEntry{ Info: &splitPkgClone, Download: entry.Download, DownloadSize: entry.DownloadSize, InstalledSize: 0, - Repository: repo, + Database: db, } - // Add virtual packages to repository + // Add virtual packages to database for _, p := range splitPkg.Provides { - repo.VirtualPackages[p] = append(repo.VirtualPackages[p], splitPkg.Name) + db.VirtualPackages[p] = append(db.VirtualPackages[p], splitPkg.Name) } } } else { // Create entry for package - repo.Entries[entry.Info.Name] = &entry + db.Entries[entry.Info.Name] = &entry - // Add virtual packages to repository + // Add virtual packages to database for _, p := range entry.Info.Provides { - repo.VirtualPackages[p] = append(repo.VirtualPackages[p], entry.Info.Name) + db.VirtualPackages[p] = append(db.VirtualPackages[p], entry.Info.Name) } } @@ -128,11 +128,11 @@ func (repo *Repository) ReadLocalDatabase() error { return nil } -func (repo *Repository) SyncLocalDatabase() error { - repoFile := "/var/lib/bpm/repositories/" + repo.Name + ".bpmdb" +func (db *BPMDatabase) SyncLocalDatabaseFile() error { + dbFile := "/var/lib/bpm/databases/" + db.Name + ".bpmdb" // Get URL to database - u, err := url.JoinPath(repo.Source, "database.bpmdb") + u, err := url.JoinPath(db.Source, "database.bpmdb") if err != nil { return err } @@ -147,20 +147,20 @@ func (repo *Repository) SyncLocalDatabase() error { // Load data into byte buffer buffer, err := io.ReadAll(resp.Body) - // Unmarshal data to ensure it is a valid BPM repository - err = yaml.Unmarshal(buffer, &Repository{}) + // Unmarshal data to ensure it is a valid BPM database + err = yaml.Unmarshal(buffer, &BPMDatabase{}) if err != nil { - return fmt.Errorf("could not decode repository: %s", err) + return fmt.Errorf("could not decode database: %s", err) } - // Create parent directories to repository file - err = os.MkdirAll(path.Dir(repoFile), 0755) + // Create parent directories to database file + err = os.MkdirAll(path.Dir(dbFile), 0755) if err != nil { return err } - // Create file and save repository data - out, err := os.Create(repoFile) + // Create file and save database data + out, err := os.Create(dbFile) if err != nil { return err } @@ -171,14 +171,14 @@ func (repo *Repository) SyncLocalDatabase() error { return nil } -func ReadLocalDatabases() (err error) { - for _, repo := range BPMConfig.Repositories { +func ReadLocalDatabaseFiles() (err error) { + for _, db := range BPMConfig.Databases { // Initialize struct values - repo.Entries = make(map[string]*RepositoryEntry) - repo.VirtualPackages = make(map[string][]string) + db.Entries = make(map[string]*BPMDatabaseEntry) + db.VirtualPackages = make(map[string][]string) // Read database - err = repo.ReadLocalDatabase() + err = db.ReadLocalDatabase() if err != nil { return err } @@ -187,47 +187,47 @@ func ReadLocalDatabases() (err error) { return nil } -func GetRepository(name string) *Repository { - for _, repo := range BPMConfig.Repositories { - if repo.Name == name { - return repo +func GetDatabase(name string) *BPMDatabase { + for _, db := range BPMConfig.Databases { + if db.Name == name { + return db } } return nil } -func GetRepositoryEntry(str string) (*RepositoryEntry, *Repository, error) { +func GetDatabaseEntry(str string) (*BPMDatabaseEntry, *BPMDatabase, error) { split := strings.Split(str, "/") if len(split) == 1 { pkgName := strings.TrimSpace(split[0]) if pkgName == "" { - return nil, nil, errors.New("could not find repository entry for this package") + return nil, nil, errors.New("could not find database entry for this package") } - for _, repo := range BPMConfig.Repositories { - if repo.ContainsPackage(pkgName) { - return repo.Entries[pkgName], repo, nil + for _, db := range BPMConfig.Databases { + if db.ContainsPackage(pkgName) { + return db.Entries[pkgName], db, nil } } - return nil, nil, errors.New("could not find repository entry for this package") + return nil, nil, errors.New("could not find database entry for this package") } else if len(split) == 2 { - repoName := strings.TrimSpace(split[0]) + dbName := strings.TrimSpace(split[0]) pkgName := strings.TrimSpace(split[1]) - if repoName == "" || pkgName == "" { - return nil, nil, errors.New("could not find repository entry for this package") + if dbName == "" || pkgName == "" { + return nil, nil, errors.New("could not find database entry for this package") } - repo := GetRepository(repoName) - if repo == nil || !repo.ContainsPackage(pkgName) { - return nil, nil, errors.New("could not find repository entry for this package") + db := GetDatabase(dbName) + if db == nil || !db.ContainsPackage(pkgName) { + return nil, nil, errors.New("could not find database entry for this package") } - return repo.Entries[pkgName], repo, nil + return db.Entries[pkgName], db, nil } else { - return nil, nil, errors.New("could not find repository entry for this package") + return nil, nil, errors.New("could not find database entry for this package") } } -func FindReplacement(pkg string) *RepositoryEntry { - for _, repo := range BPMConfig.Repositories { - for _, entry := range repo.Entries { +func FindReplacement(pkg string) *BPMDatabaseEntry { + for _, db := range BPMConfig.Databases { + for _, entry := range db.Entries { for _, replaced := range entry.Info.Replaces { if replaced == pkg { return entry @@ -239,11 +239,11 @@ func FindReplacement(pkg string) *RepositoryEntry { return nil } -func ResolveVirtualPackage(vpkg string) *RepositoryEntry { - for _, repo := range BPMConfig.Repositories { - if v, ok := repo.VirtualPackages[vpkg]; ok { +func ResolveVirtualPackage(vpkg string) *BPMDatabaseEntry { + for _, db := range BPMConfig.Databases { + if v, ok := db.VirtualPackages[vpkg]; ok { for _, pkg := range v { - return repo.Entries[pkg] + return db.Entries[pkg] } } } @@ -251,12 +251,12 @@ func ResolveVirtualPackage(vpkg string) *RepositoryEntry { return nil } -func (repo *Repository) FetchPackage(pkg string) (string, error) { - if !repo.ContainsPackage(pkg) { +func (db *BPMDatabase) FetchPackage(pkg string) (string, error) { + if !db.ContainsPackage(pkg) { return "", errors.New("could not fetch package '" + pkg + "'") } - entry := repo.Entries[pkg] - URL, err := url.JoinPath(repo.Source, entry.Download) + entry := db.Entries[pkg] + URL, err := url.JoinPath(db.Source, entry.Download) if err != nil { return "", err } diff --git a/src/bpmlib/dependencies.go b/src/bpmlib/dependencies.go index d71a105..f349903 100644 --- a/src/bpmlib/dependencies.go +++ b/src/bpmlib/dependencies.go @@ -94,8 +94,8 @@ func resolvePackageDependenciesFromDatabase(resolved, unresolved *[]string, pkgI continue } var err error - var entry *RepositoryEntry - entry, _, err = GetRepositoryEntry(depend) + var entry *BPMDatabaseEntry + entry, _, err = GetDatabaseEntry(depend) if err != nil { if entry = ResolveVirtualPackage(depend); entry == nil { if !slices.Contains(*unresolved, depend) { diff --git a/src/bpmlib/errors.go b/src/bpmlib/errors.go index b9d193e..618251a 100644 --- a/src/bpmlib/errors.go +++ b/src/bpmlib/errors.go @@ -10,7 +10,7 @@ type PackageNotFoundErr struct { } func (e PackageNotFoundErr) Error() string { - return "The following packages were not found in any repositories: " + strings.Join(e.packages, ", ") + return "The following packages were not found in any databases: " + strings.Join(e.packages, ", ") } type DependencyNotFoundErr struct { @@ -18,7 +18,7 @@ type DependencyNotFoundErr struct { } func (e DependencyNotFoundErr) Error() string { - return "The following dependencies were not found in any repositories: " + strings.Join(e.dependencies, ", ") + return "The following dependencies were not found in any databases: " + strings.Join(e.dependencies, ", ") } type PackageConflictErr struct { diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index fd15a55..8e28e05 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -17,7 +17,7 @@ const ( ReinstallMethodAll ReinstallMethod = iota ) -// InstallPackages installs the specified packages into the given root directory by fetching them from repositories 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, installationReason InstallationReason, reinstallMethod ReinstallMethod, installOptionalDependencies, forceInstallation, verbose bool, packages ...string) (operation *BPMOperation, err error) { // Setup operation struct operation = &BPMOperation{ @@ -64,12 +64,12 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein BpmPackage: bpmpkg, }) } else { - var entry *RepositoryEntry + var entry *BPMDatabaseEntry - if e, _, err := GetRepositoryEntry(pkg); err == nil { + if e, _, err := GetDatabaseEntry(pkg); err == nil { entry = e } else if isVirtual, p := IsVirtualPackage(pkg, rootDir); isVirtual { - entry, _, err = GetRepositoryEntry(p) + entry, _, err = GetDatabaseEntry(p) if err != nil { pkgsNotFound = append(pkgsNotFound, pkg) continue @@ -85,8 +85,8 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein } operation.AppendAction(&FetchPackageAction{ - IsDependency: false, - RepositoryEntry: entry, + IsDependency: false, + DatabaseEntry: entry, }) } } @@ -272,7 +272,7 @@ func CleanupCache(rootDir string, cleanupCompilationFiles, cleanupCompiledPackag // UpdatePackages fetches the newest versions of all installed packages from func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependencies, forceInstallation, verbose bool) (operation *BPMOperation, err error) { - // Sync repositories + // Sync databases if syncDatabase { err := SyncDatabase(verbose) if err != nil { @@ -287,7 +287,7 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci if err != nil { return nil, fmt.Errorf("could not read BPM config: %s", err) } - err = ReadLocalDatabases() + err = ReadLocalDatabaseFiles() if err != nil { return nil, fmt.Errorf("could not read local databases: %s", err) } @@ -313,11 +313,11 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci if slices.Contains(BPMConfig.IgnorePackages, pkg) { continue } - var entry *RepositoryEntry + var entry *BPMDatabaseEntry // Check if installed package can be replaced and install that instead if e := FindReplacement(pkg); e != nil { entry = e - } else if entry, _, err = GetRepositoryEntry(pkg); err != nil { + } else if entry, _, err = GetDatabaseEntry(pkg); err != nil { continue } @@ -328,8 +328,8 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci comparison := ComparePackageVersions(*entry.Info, *installedInfo) if comparison > 0 { operation.AppendAction(&FetchPackageAction{ - IsDependency: false, - RepositoryEntry: entry, + IsDependency: false, + DatabaseEntry: entry, }) } } @@ -355,12 +355,12 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci // SyncDatabase syncs all databases declared in /etc/bpm.conf func SyncDatabase(verbose bool) (err error) { - for _, repo := range BPMConfig.Repositories { + for _, db := range BPMConfig.Databases { if verbose { - fmt.Printf("Fetching package database for repository (%s)...\n", repo.Name) + fmt.Printf("Fetching package database file for database (%s)...\n", db.Name) } - err := repo.SyncLocalDatabase() + err := db.SyncLocalDatabaseFile() if err != nil { return err } diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index 0a50e41..de03e30 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -26,7 +26,7 @@ func (operation *BPMOperation) ActionsContainPackage(pkg string) bool { return true } } else if action.GetActionType() == "fetch" { - if action.(*FetchPackageAction).RepositoryEntry.Info.Name == pkg { + if action.(*FetchPackageAction).DatabaseEntry.Info.Name == pkg { return true } } else if action.GetActionType() == "remove" { @@ -58,7 +58,7 @@ func (operation *BPMOperation) InsertActionAt(index int, action OperationAction) operation.Changes[pkgInfo.Name] = "upgrade" } } else if action.GetActionType() == "fetch" { - pkgInfo := action.(*FetchPackageAction).RepositoryEntry.Info + pkgInfo := action.(*FetchPackageAction).DatabaseEntry.Info if !IsPackageInstalled(pkgInfo.Name, operation.RootDir) { operation.Changes[pkgInfo.Name] = "install" } else { @@ -77,7 +77,7 @@ func (operation *BPMOperation) RemoveAction(pkg, actionType string) { if a.GetActionType() == "install" { return a.(*InstallPackageAction).BpmPackage.PkgInfo.Name == pkg } else if a.GetActionType() == "fetch" { - return a.(*FetchPackageAction).RepositoryEntry.Info.Name == pkg + return a.(*FetchPackageAction).DatabaseEntry.Info.Name == pkg } else if a.GetActionType() == "remove" { return a.(*RemovePackageAction).BpmPackage.PkgInfo.Name == pkg } @@ -89,7 +89,7 @@ func (operation *BPMOperation) GetTotalDownloadSize() uint64 { var ret uint64 = 0 for _, action := range operation.Actions { if action.GetActionType() == "fetch" { - ret += action.(*FetchPackageAction).RepositoryEntry.DownloadSize + ret += action.(*FetchPackageAction).DatabaseEntry.DownloadSize } } return ret @@ -101,7 +101,7 @@ func (operation *BPMOperation) GetTotalInstalledSize() uint64 { if action.GetActionType() == "install" { ret += action.(*InstallPackageAction).BpmPackage.GetInstalledSize() } else if action.GetActionType() == "fetch" { - ret += action.(*FetchPackageAction).RepositoryEntry.InstalledSize + ret += action.(*FetchPackageAction).DatabaseEntry.InstalledSize } } return ret @@ -116,7 +116,7 @@ func (operation *BPMOperation) GetFinalActionSize(rootDir string) int64 { ret -= int64(GetPackage(action.(*InstallPackageAction).BpmPackage.PkgInfo.Name, rootDir).GetInstalledSize()) } } else if action.GetActionType() == "fetch" { - ret += int64(action.(*FetchPackageAction).RepositoryEntry.InstalledSize) + ret += int64(action.(*FetchPackageAction).DatabaseEntry.InstalledSize) } else if action.GetActionType() == "remove" { ret -= int64(action.(*RemovePackageAction).BpmPackage.GetInstalledSize()) } @@ -133,7 +133,7 @@ func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, instal pkgInfo = action.BpmPackage.PkgInfo } else if value.GetActionType() == "fetch" { action := value.(*FetchPackageAction) - pkgInfo = action.RepositoryEntry.Info + pkgInfo = action.DatabaseEntry.Info } else { pos++ continue @@ -148,13 +148,13 @@ func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, instal if !reinstallDependencies && IsPackageInstalled(depend, operation.RootDir) { continue } - entry, _, err := GetRepositoryEntry(depend) + entry, _, err := GetDatabaseEntry(depend) if err != nil { - return errors.New("could not get repository entry for package (" + depend + ")") + return errors.New("could not get database entry for package (" + depend + ")") } operation.InsertActionAt(pos, &FetchPackageAction{ - IsDependency: true, - RepositoryEntry: entry, + IsDependency: true, + DatabaseEntry: entry, }) pos++ } @@ -263,7 +263,7 @@ func (operation *BPMOperation) ReplaceObsoletePackages() { } else if value.GetActionType() == "fetch" { action := value.(*FetchPackageAction) - pkgInfo = action.RepositoryEntry.Info + pkgInfo = action.DatabaseEntry.Info } else { continue } @@ -301,7 +301,7 @@ func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error) allPackages = append(allPackages, pkgInfo) } else if value.GetActionType() == "fetch" { action := value.(*FetchPackageAction) - pkgInfo := action.RepositoryEntry.Info + pkgInfo := action.DatabaseEntry.Info allPackages = append(allPackages, pkgInfo) } else if value.GetActionType() == "remove" { action := value.(*RemovePackageAction) @@ -342,7 +342,7 @@ func (operation *BPMOperation) ShowOperationSummary() { pkgInfo = pkgInfo.GetSplitPackageInfo(value.(*InstallPackageAction).SplitPackageToInstall) } } else if value.GetActionType() == "fetch" { - pkgInfo = value.(*FetchPackageAction).RepositoryEntry.Info + pkgInfo = value.(*FetchPackageAction).DatabaseEntry.Info } else { pkgInfo = value.(*RemovePackageAction).BpmPackage.PkgInfo fmt.Printf("%s: %s (Remove)\n", pkgInfo.Name, pkgInfo.GetFullVersion()) @@ -414,11 +414,11 @@ func (operation *BPMOperation) RunHooks(verbose bool) error { } func (operation *BPMOperation) Execute(verbose, force bool) (err error) { - // Fetch packages from repositories + // Fetch packages from databases if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool { return action.GetActionType() == "fetch" }) { - fmt.Println("Fetching packages from available repositories...") + fmt.Println("Fetching packages from available databases...") // Create map for fetched packages fetchedPackages := make(map[string]string) @@ -428,16 +428,16 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) { continue } - // Get repository entry - entry := action.(*FetchPackageAction).RepositoryEntry + // Get database entry + entry := action.(*FetchPackageAction).DatabaseEntry // Create bpmpkg variable var bpmpkg *BPMPackage // Check if package has already been fetched from download link if _, ok := fetchedPackages[entry.Download]; !ok { - // Fetch package from repository - fetchedPackage, err := entry.Repository.FetchPackage(entry.Info.Name) + // Fetch package from database + fetchedPackage, err := entry.Database.FetchPackage(entry.Info.Name) if err != nil { return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err)) } @@ -596,8 +596,8 @@ func (action *InstallPackageAction) GetActionType() string { } type FetchPackageAction struct { - IsDependency bool - RepositoryEntry *RepositoryEntry + IsDependency bool + DatabaseEntry *BPMDatabaseEntry } func (action *FetchPackageAction) GetActionType() string { From 966d351a801652da944f4ca3c9672230cdf890f7 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 15 May 2025 16:07:15 +0300 Subject: [PATCH 72/88] Readd make depends field to binary packages --- src/bpmlib/compilation.go | 3 +-- src/bpmlib/packages.go | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index f35b431..f8434e4 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -264,8 +264,7 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo pkgInfo.Arch = GetArch() } - // Remove source package specific fields - pkgInfo.MakeDepends = nil + // Remove split package field pkgInfo.SplitPackages = nil // Marshal package info diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index 5c4471d..5cd21d6 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -466,8 +466,7 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) { pkgInfoClone := *pkgInfo pkgInfo.SplitPackages[i] = &pkgInfoClone - // Set make depends and split package field of split package to nil - pkgInfo.SplitPackages[i].MakeDepends = nil + // Set split package field of split package to nil pkgInfo.SplitPackages[i].SplitPackages = nil // Unmarshal json data back to struct From 458c091ac2495e618ece4109d22631ba3e6e2236 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Fri, 23 May 2025 14:16:52 +0300 Subject: [PATCH 73/88] Add "make_dependency" installation reason --- config/bpm.conf | 1 + src/bpm/main.go | 30 ++++++++++++---- src/bpmlib/config.go | 13 ++++--- src/bpmlib/dependencies.go | 63 ++++++++++++++++++-------------- src/bpmlib/general.go | 44 +++++++++++------------ src/bpmlib/operations.go | 74 +++++++++++++++++++++----------------- src/bpmlib/packages.go | 23 +++++++++--- 7 files changed, 149 insertions(+), 99 deletions(-) diff --git a/config/bpm.conf b/config/bpm.conf index f32bc41..d594d35 100644 --- a/config/bpm.conf +++ b/config/bpm.conf @@ -1,6 +1,7 @@ ignore_packages: [] privilege_escalator_cmd: "sudo" compilation_env: [] +cleanup_make_dependencies: true databases: - name: example-database source: https://my-database.xyz/ diff --git a/src/bpm/main.go b/src/bpm/main.go index 329d8af..8de0e8e 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -44,6 +44,7 @@ var installSrcPkgDepends = false var skipChecks = false var outputDirectory = "" var cleanupDependencies = false +var cleanupMakeDependencies = false var cleanupCompilationFiles = false var cleanupCompiledPackages = false var cleanupFetchedPackages = false @@ -249,12 +250,14 @@ func resolveCommand() { } // Check if installationReason argument is valid - ir := bpmlib.InstallationReasonUnknown + ir := bpmlib.InstallationReasonManual switch installationReason { case "manual": ir = bpmlib.InstallationReasonManual case "dependency": ir = bpmlib.InstallationReasonDependency + case "make-dependency": + ir = bpmlib.InstallationReasonMakeDependency case "": default: log.Fatalf("Error: %s is not a valid installation reason", installationReason) @@ -467,7 +470,7 @@ func resolveCommand() { log.Fatalf("Error: could not complete cache cleanup: %s", err) } - if cleanupDependencies { + if cleanupDependencies || cleanupMakeDependencies { // Read local databases err := bpmlib.ReadLocalDatabaseFiles() if err != nil { @@ -475,7 +478,7 @@ func resolveCommand() { } // Create cleanup operation - operation, err := bpmlib.CleanupPackages(rootDir) + operation, err := bpmlib.CleanupPackages(cleanupMakeDependencies, rootDir) if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { log.Fatalf("Error: %s", err) } else if err != nil { @@ -595,7 +598,7 @@ func resolveCommand() { // Get direct runtime and make dependencies totalDepends := make([]string, 0) - for _, depend := range bpmpkg.PkgInfo.GetDependencies(true, false) { + for depend := range bpmpkg.PkgInfo.GetDependencies(true, false) { if !slices.Contains(totalDepends, depend) { totalDepends = append(totalDepends, depend) } @@ -624,7 +627,7 @@ func resolveCommand() { } // Run 'bpm install' using the set privilege escalator command - args := []string{executable, "install", "--installation-reason=dependency"} + args := []string{executable, "install", "--installation-reason=make-dependency"} args = append(args, unmetDepends...) cmd := exec.Command(bpmlib.BPMConfig.PrivilegeEscalatorCmd, args...) if yesAll { @@ -777,6 +780,7 @@ func printHelp() { fmt.Println(" -R= lets you define the root path which will be used") fmt.Println(" -y skips the confirmation prompt") fmt.Println(" --depends performs a dependency cleanup") + fmt.Println(" --make-depends performs a make dependency cleanup") fmt.Println(" --compilation-files performs a cleanup of compilation files") fmt.Println(" --compiled-pkgs performs a cleanup of compilation compiled binary packages") fmt.Println(" --fetched-pkgs performs a cleanup of fetched packages from databases") @@ -843,6 +847,7 @@ func resolveFlags() { cleanupFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing") cleanupFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts") cleanupFlagSet.BoolVar(&cleanupDependencies, "depends", false, "Perform a dependency cleanup") + cleanupFlagSet.BoolVar(&cleanupMakeDependencies, "make-depends", false, "Perform a make dependency cleanup") cleanupFlagSet.BoolVar(&cleanupCompilationFiles, "compilation-files", false, "Perform a cleanup of compilation files") cleanupFlagSet.BoolVar(&cleanupCompiledPackages, "compiled-pkgs", false, "Perform a cleanup of compilation compiled binary packages") cleanupFlagSet.BoolVar(&cleanupFetchedPackages, "fetched-pkgs", false, "Perform a cleanup of fetched packages from databases") @@ -858,8 +863,18 @@ func resolveFlags() { compileFlagSet.StringVar(&outputDirectory, "o", "", "Set output directory") compileFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing") compileFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts") - compileFlagSet.Usage = printHelp + + isFlagSet := func(flagSet *flag.FlagSet, name string) bool { + found := false + flagSet.Visit(func(f *flag.Flag) { + if f.Name == name { + found = true + } + }) + return found + } + if len(os.Args[1:]) <= 0 { subcommand = "help" } else { @@ -906,8 +921,9 @@ func resolveFlags() { if err != nil { return } - if !cleanupDependencies && !cleanupCompilationFiles && !cleanupCompiledPackages && !cleanupFetchedPackages { + if !isFlagSet(cleanupFlagSet, "depends") && !isFlagSet(cleanupFlagSet, "make-depends") && !isFlagSet(cleanupFlagSet, "compilation-files") && !isFlagSet(cleanupFlagSet, "compiled-pkgs") && !isFlagSet(cleanupFlagSet, "fetched-pkgs") { cleanupDependencies = true + cleanupMakeDependencies = bpmlib.BPMConfig.CleanupMakeDependencies cleanupCompilationFiles = true cleanupCompiledPackages = true cleanupFetchedPackages = true diff --git a/src/bpmlib/config.go b/src/bpmlib/config.go index 0c24e57..e5f33c8 100644 --- a/src/bpmlib/config.go +++ b/src/bpmlib/config.go @@ -6,10 +6,11 @@ import ( ) type BPMConfigStruct struct { - IgnorePackages []string `yaml:"ignore_packages"` - PrivilegeEscalatorCmd string `yaml:"privilege_escalator_cmd"` - CompilationEnvironment []string `yaml:"compilation_env"` - Databases []*BPMDatabase `yaml:"databases"` + IgnorePackages []string `yaml:"ignore_packages"` + PrivilegeEscalatorCmd string `yaml:"privilege_escalator_cmd"` + CompilationEnvironment []string `yaml:"compilation_env"` + CleanupMakeDependencies bool `yaml:"cleanup_make_dependencies"` + Databases []*BPMDatabase `yaml:"databases"` } var BPMConfig BPMConfigStruct @@ -24,7 +25,9 @@ func ReadConfig() (err error) { return err } - BPMConfig = BPMConfigStruct{} + BPMConfig = BPMConfigStruct{ + CleanupMakeDependencies: true, + } err = yaml.Unmarshal(bytes, &BPMConfig) if err != nil { return err diff --git a/src/bpmlib/dependencies.go b/src/bpmlib/dependencies.go index f349903..d55a6a0 100644 --- a/src/bpmlib/dependencies.go +++ b/src/bpmlib/dependencies.go @@ -6,14 +6,25 @@ import ( "slices" ) -func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeOptionalDepends bool) []string { - allDepends := make([]string, 0) - allDepends = append(allDepends, pkgInfo.Depends...) - if includeMakeDepends { - allDepends = append(allDepends, pkgInfo.MakeDepends...) +func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeOptionalDepends bool) map[string]InstallationReason { + allDepends := make(map[string]InstallationReason) + + for _, depend := range pkgInfo.Depends { + allDepends[depend] = InstallationReasonDependency } if includeOptionalDepends { - allDepends = append(allDepends, pkgInfo.OptionalDepends...) + for _, depend := range pkgInfo.OptionalDepends { + if _, ok := allDepends[depend]; !ok { + allDepends[depend] = InstallationReasonDependency + } + } + } + if includeMakeDepends { + for _, depend := range pkgInfo.MakeDepends { + if _, ok := allDepends[depend]; !ok { + allDepends[depend] = InstallationReasonMakeDependency + } + } } return allDepends } @@ -34,7 +45,10 @@ func (pkgInfo *PackageInfo) getAllDependencies(resolved *[]string, unresolved *[ *unresolved = append(*unresolved, pkgInfo.Name) // Loop through all dependencies - for _, depend := range pkgInfo.GetDependencies(includeMakeDepends, includeOptionalDepends) { + for depend := range pkgInfo.GetDependencies(includeMakeDepends, includeOptionalDepends) { + if isVirtual, p := IsVirtualPackage(depend, rootDir); isVirtual { + depend = p + } if !slices.Contains(*resolved, depend) { // Add current dependency to resolved slice when circular dependency is detected if slices.Contains(*unresolved, depend) { @@ -44,13 +58,7 @@ func (pkgInfo *PackageInfo) getAllDependencies(resolved *[]string, unresolved *[ continue } - var dependInfo *PackageInfo - - if isVirtual, p := IsVirtualPackage(depend, rootDir); isVirtual { - dependInfo = GetPackageInfo(p, rootDir) - } else { - dependInfo = GetPackageInfo(depend, rootDir) - } + dependInfo := GetPackageInfo(depend, rootDir) if dependInfo != nil { dependInfo.getAllDependencies(resolved, unresolved, includeMakeDepends, includeOptionalDepends, rootDir) @@ -63,31 +71,31 @@ func (pkgInfo *PackageInfo) getAllDependencies(resolved *[]string, unresolved *[ *unresolved = stringSliceRemove(*unresolved, pkgInfo.Name) } -func ResolvePackageDependenciesFromDatabases(pkgInfo *PackageInfo, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) (resolved []string, unresolved []string) { +func ResolveAllPackageDependenciesFromDatabases(pkgInfo *PackageInfo, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) (resolved map[string]InstallationReason, unresolved []string) { // Initialize slices - resolved = make([]string, 0) + resolved = make(map[string]InstallationReason) unresolved = make([]string, 0) // Call unexported function - resolvePackageDependenciesFromDatabase(&resolved, &unresolved, pkgInfo, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) + resolvePackageDependenciesFromDatabase(resolved, &unresolved, pkgInfo, InstallationReasonDependency, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) return resolved, unresolved } -func resolvePackageDependenciesFromDatabase(resolved, unresolved *[]string, pkgInfo *PackageInfo, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) { +func resolvePackageDependenciesFromDatabase(resolved map[string]InstallationReason, unresolved *[]string, pkgInfo *PackageInfo, installationReason InstallationReason, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) { // Add current package name to unresolved slice *unresolved = append(*unresolved, pkgInfo.Name) // Loop through all dependencies - for _, depend := range pkgInfo.GetDependencies(checkMake, checkOptional) { - if !slices.Contains(*resolved, depend) { + for depend, ir := range pkgInfo.GetDependencies(pkgInfo.Type == "source", checkOptional) { + if _, ok := resolved[depend]; !ok { // Add current dependency to resolved slice when circular dependency is detected if slices.Contains(*unresolved, depend) { if verbose { fmt.Printf("Circular dependency was detected (%s -> %s). Installing %s first\n", pkgInfo.Name, depend, depend) } - if !slices.Contains(*resolved, depend) { - *resolved = append(*resolved, depend) + if _, ok := resolved[depend]; !ok { + resolved[depend] = ir } continue } else if ignoreInstalled && IsPackageProvided(depend, rootDir) { @@ -104,11 +112,12 @@ func resolvePackageDependenciesFromDatabase(resolved, unresolved *[]string, pkgI continue } } - resolvePackageDependenciesFromDatabase(resolved, unresolved, entry.Info, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) + resolvePackageDependenciesFromDatabase(resolved, unresolved, entry.Info, ir, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) } } - if !slices.Contains(*resolved, pkgInfo.Name) { - *resolved = append(*resolved, pkgInfo.Name) + + if _, ok := resolved[pkgInfo.Name]; !ok { + resolved[pkgInfo.Name] = installationReason } *unresolved = stringSliceRemove(*unresolved, pkgInfo.Name) } @@ -145,7 +154,7 @@ func GetPackageDependants(pkgName string, rootDir string) ([]string, error) { dependencies := installedPkg.PkgInfo.GetDependencies(false, true) // Add installed package to list if its dependencies include pkgName - if slices.Contains(dependencies, pkgName) { + if _, ok := dependencies[pkgName]; ok { ret = append(ret, installedPkgName) continue } @@ -153,7 +162,7 @@ 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.Contains(dependencies, vpkg) { + if _, ok := dependencies[vpkg]; ok { ret = append(ret, installedPkgName) break } diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index 8e28e05..ff80caa 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -21,12 +21,11 @@ const ( func InstallPackages(rootDir string, installationReason InstallationReason, reinstallMethod ReinstallMethod, installOptionalDependencies, forceInstallation, verbose bool, packages ...string) (operation *BPMOperation, err error) { // Setup operation struct operation = &BPMOperation{ - Actions: make([]OperationAction, 0), - UnresolvedDepends: make([]string, 0), - Changes: make(map[string]string), - RootDir: rootDir, - ForceInstallationReason: installationReason, - compiledPackages: make(map[string]string), + Actions: make([]OperationAction, 0), + UnresolvedDepends: make([]string, 0), + Changes: make(map[string]string), + RootDir: rootDir, + compiledPackages: make(map[string]string), } // Resolve packages @@ -46,7 +45,7 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein operation.AppendAction(&InstallPackageAction{ File: pkg, - IsDependency: false, + InstallationReason: installationReason, BpmPackage: bpmpkg, SplitPackageToInstall: splitPkg.Name, }) @@ -59,9 +58,9 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein } operation.AppendAction(&InstallPackageAction{ - File: pkg, - IsDependency: false, - BpmPackage: bpmpkg, + File: pkg, + InstallationReason: installationReason, + BpmPackage: bpmpkg, }) } else { var entry *BPMDatabaseEntry @@ -85,8 +84,8 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein } operation.AppendAction(&FetchPackageAction{ - IsDependency: false, - DatabaseEntry: entry, + InstallationReason: installationReason, + DatabaseEntry: entry, }) } } @@ -161,7 +160,7 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie // Do package cleanup if cleanupDependencies { - err := operation.Cleanup() + err := operation.Cleanup(true) if err != nil { return nil, fmt.Errorf("could not perform cleanup for operation: %s", err) } @@ -170,7 +169,7 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie } // CleanupPackages finds packages installed as dependencies which are no longer required by the rest of the system in the given root directory -func CleanupPackages(rootDir string) (operation *BPMOperation, err error) { +func CleanupPackages(cleanupMakeDepends bool, rootDir string) (operation *BPMOperation, err error) { operation = &BPMOperation{ Actions: make([]OperationAction, 0), UnresolvedDepends: make([]string, 0), @@ -180,7 +179,7 @@ func CleanupPackages(rootDir string) (operation *BPMOperation, err error) { } // Do package cleanup - err = operation.Cleanup() + err = operation.Cleanup(cleanupMakeDepends) if err != nil { return nil, fmt.Errorf("could not perform cleanup for operation: %s", err) } @@ -300,12 +299,11 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci } operation = &BPMOperation{ - Actions: make([]OperationAction, 0), - UnresolvedDepends: make([]string, 0), - Changes: make(map[string]string), - RootDir: rootDir, - ForceInstallationReason: InstallationReasonUnknown, - compiledPackages: make(map[string]string), + Actions: make([]OperationAction, 0), + UnresolvedDepends: make([]string, 0), + Changes: make(map[string]string), + RootDir: rootDir, + compiledPackages: make(map[string]string), } // Search for packages @@ -328,8 +326,8 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci comparison := ComparePackageVersions(*entry.Info, *installedInfo) if comparison > 0 { operation.AppendAction(&FetchPackageAction{ - IsDependency: false, - DatabaseEntry: entry, + InstallationReason: GetInstallationReason(pkg, rootDir), + DatabaseEntry: entry, }) } } diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index de03e30..8e4ef23 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -11,12 +11,11 @@ import ( ) type BPMOperation struct { - Actions []OperationAction - UnresolvedDepends []string - Changes map[string]string - RootDir string - ForceInstallationReason InstallationReason - compiledPackages map[string]string + Actions []OperationAction + UnresolvedDepends []string + Changes map[string]string + RootDir string + compiledPackages map[string]string } func (operation *BPMOperation) ActionsContainPackage(pkg string) bool { @@ -139,11 +138,11 @@ func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, instal continue } - resolved, unresolved := ResolvePackageDependenciesFromDatabases(pkgInfo, pkgInfo.Type == "source", installOptionalDependencies, !reinstallDependencies, verbose, operation.RootDir) + resolved, unresolved := ResolveAllPackageDependenciesFromDatabases(pkgInfo, pkgInfo.Type == "source", installOptionalDependencies, !reinstallDependencies, verbose, operation.RootDir) operation.UnresolvedDepends = append(operation.UnresolvedDepends, unresolved...) - for _, depend := range resolved { + for depend, installationReason := range resolved { if !operation.ActionsContainPackage(depend) && depend != pkgInfo.Name { if !reinstallDependencies && IsPackageInstalled(depend, operation.RootDir) { continue @@ -153,8 +152,8 @@ func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, instal return errors.New("could not get database entry for package (" + depend + ")") } operation.InsertActionAt(pos, &FetchPackageAction{ - IsDependency: true, - DatabaseEntry: entry, + InstallationReason: installationReason, + DatabaseEntry: entry, }) pos++ } @@ -192,7 +191,7 @@ func (operation *BPMOperation) RemoveNeededPackages() error { return nil } -func (operation *BPMOperation) Cleanup() error { +func (operation *BPMOperation) Cleanup(cleanupMakeDepends bool) error { // Get all installed packages installedPackageNames, err := GetInstalledPackages(operation.RootDir) if err != nil { @@ -228,7 +227,7 @@ func (operation *BPMOperation) Cleanup() error { } keepPackages = append(keepPackages, pkg.Name) - resolved := pkg.GetAllDependencies(false, true, operation.RootDir) + resolved := pkg.GetAllDependencies(!cleanupMakeDepends, true, operation.RootDir) for _, value := range resolved { if !slices.Contains(keepPackages, value) { keepPackages = append(keepPackages, value) @@ -336,12 +335,15 @@ func (operation *BPMOperation) ShowOperationSummary() { for _, value := range operation.Actions { var pkgInfo *PackageInfo + var installationReason = InstallationReasonUnknown if value.GetActionType() == "install" { + installationReason = value.(*InstallPackageAction).InstallationReason pkgInfo = value.(*InstallPackageAction).BpmPackage.PkgInfo if value.(*InstallPackageAction).SplitPackageToInstall != "" { pkgInfo = pkgInfo.GetSplitPackageInfo(value.(*InstallPackageAction).SplitPackageToInstall) } } else if value.GetActionType() == "fetch" { + installationReason = value.(*FetchPackageAction).InstallationReason pkgInfo = value.(*FetchPackageAction).DatabaseEntry.Info } else { pkgInfo = value.(*RemovePackageAction).BpmPackage.PkgInfo @@ -350,21 +352,32 @@ func (operation *BPMOperation) ShowOperationSummary() { } installedInfo := GetPackageInfo(pkgInfo.Name, operation.RootDir) - sourceInfo := "" + additionalInfo := "" + switch installationReason { + case InstallationReasonManual: + additionalInfo = "(Manual)" + case InstallationReasonDependency: + additionalInfo = "(Dependency)" + case InstallationReasonMakeDependency: + additionalInfo = "(Make dependency)" + default: + additionalInfo = "(Unknown)" + } + if pkgInfo.Type == "source" { - sourceInfo = "(From Source)" + additionalInfo += " (From Source)" } if installedInfo == nil { - fmt.Printf("%s: %s (Install) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), sourceInfo) + fmt.Printf("%s: %s (Install) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), additionalInfo) } else { comparison := ComparePackageVersions(*pkgInfo, *installedInfo) if comparison < 0 { - fmt.Printf("%s: %s -> %s (Downgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), sourceInfo) + fmt.Printf("%s: %s -> %s (Downgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), additionalInfo) } else if comparison > 0 { - fmt.Printf("%s: %s -> %s (Upgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), sourceInfo) + fmt.Printf("%s: %s -> %s (Upgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), additionalInfo) } else { - fmt.Printf("%s: %s (Reinstall) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), sourceInfo) + fmt.Printf("%s: %s (Reinstall) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), additionalInfo) } } } @@ -465,15 +478,15 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) { if bpmpkg.PkgInfo.IsSplitPackage() { operation.Actions[i] = &InstallPackageAction{ File: fetchedPackages[entry.Download], - IsDependency: action.(*FetchPackageAction).IsDependency, + InstallationReason: action.(*FetchPackageAction).InstallationReason, BpmPackage: bpmpkg, SplitPackageToInstall: entry.Info.Name, } } else { operation.Actions[i] = &InstallPackageAction{ - File: fetchedPackages[entry.Download], - IsDependency: action.(*FetchPackageAction).IsDependency, - BpmPackage: bpmpkg, + File: fetchedPackages[entry.Download], + InstallationReason: action.(*FetchPackageAction).InstallationReason, + BpmPackage: bpmpkg, } } } @@ -553,7 +566,7 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) { } } - if value.IsDependency { + if value.InstallationReason != InstallationReasonManual { err = installPackage(fileToInstall, operation.RootDir, verbose, true) } else { err = installPackage(fileToInstall, operation.RootDir, verbose, force) @@ -561,13 +574,8 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) { if err != nil { return errors.New(fmt.Sprintf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err)) } - if operation.ForceInstallationReason != InstallationReasonUnknown && !value.IsDependency { - err := SetInstallationReason(bpmpkg.PkgInfo.Name, operation.ForceInstallationReason, operation.RootDir) - if err != nil { - return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err)) - } - } else if value.IsDependency && !isReinstall { - err := SetInstallationReason(bpmpkg.PkgInfo.Name, InstallationReasonDependency, operation.RootDir) + if !isReinstall { + err := SetInstallationReason(bpmpkg.PkgInfo.Name, value.InstallationReason, operation.RootDir) if err != nil { return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err)) } @@ -586,7 +594,7 @@ type OperationAction interface { type InstallPackageAction struct { File string - IsDependency bool + InstallationReason InstallationReason SplitPackageToInstall string BpmPackage *BPMPackage } @@ -596,8 +604,8 @@ func (action *InstallPackageAction) GetActionType() string { } type FetchPackageAction struct { - IsDependency bool - DatabaseEntry *BPMDatabaseEntry + InstallationReason InstallationReason + DatabaseEntry *BPMDatabaseEntry } func (action *FetchPackageAction) GetActionType() string { diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index 5cd21d6..ce69858 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -92,9 +92,10 @@ func (pkgInfo *PackageInfo) GetSplitPackageInfo(splitPkg string) *PackageInfo { type InstallationReason string const ( - InstallationReasonManual InstallationReason = "manual" - InstallationReasonDependency InstallationReason = "dependency" - InstallationReasonUnknown InstallationReason = "unknown" + InstallationReasonManual InstallationReason = "manual" + InstallationReasonDependency InstallationReason = "dependency" + InstallationReasonMakeDependency InstallationReason = "make_dependency" + InstallationReasonUnknown InstallationReason = "unknown" ) func ComparePackageVersions(info1, info2 PackageInfo) int { @@ -119,6 +120,8 @@ func GetInstallationReason(pkg, rootDir string) InstallationReason { return InstallationReasonManual } else if reason == "dependency" { return InstallationReasonDependency + } else if reason == "make_dependency" { + return InstallationReasonMakeDependency } return InstallationReasonUnknown } @@ -524,7 +527,19 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations, showIn appendArray("Split Packages", splitPkgs) } if IsPackageInstalled(pkgInfo.Name, rootDir) && showInstallationReason { - ret = append(ret, "Installation Reason: "+string(GetInstallationReason(pkgInfo.Name, rootDir))) + installationReason := GetInstallationReason(pkgInfo.Name, rootDir) + var installationReasonString string + switch installationReason { + case InstallationReasonManual: + installationReasonString = "Manual" + case InstallationReasonDependency: + installationReasonString = "Dependency" + case InstallationReasonMakeDependency: + installationReasonString = "Make dependency" + default: + installationReasonString = "Unknown" + } + ret = append(ret, "Installation Reason: "+installationReasonString) } return strings.Join(ret, "\n") } From 8a627254c96b80f747f3fdd2bd46fbc9b8969d5b Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sun, 6 Jul 2025 21:14:50 +0300 Subject: [PATCH 74/88] Fix dependency installation order --- src/bpm/main.go | 6 +- src/bpmlib/dependencies.go | 140 +++++++++++++++++++++++++------------ src/bpmlib/operations.go | 12 ++-- 3 files changed, 105 insertions(+), 53 deletions(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index 8de0e8e..bf6e727 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -598,9 +598,9 @@ func resolveCommand() { // Get direct runtime and make dependencies totalDepends := make([]string, 0) - for depend := range bpmpkg.PkgInfo.GetDependencies(true, false) { - if !slices.Contains(totalDepends, depend) { - totalDepends = append(totalDepends, depend) + for _, depend := range bpmpkg.PkgInfo.GetDependencies(true, false) { + if !slices.Contains(totalDepends, depend.PkgName) { + totalDepends = append(totalDepends, depend.PkgName) } } diff --git a/src/bpmlib/dependencies.go b/src/bpmlib/dependencies.go index d55a6a0..9cbb9c8 100644 --- a/src/bpmlib/dependencies.go +++ b/src/bpmlib/dependencies.go @@ -6,23 +6,45 @@ import ( "slices" ) -func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeOptionalDepends bool) map[string]InstallationReason { - allDepends := make(map[string]InstallationReason) +type pkgInstallationReason struct { + PkgName string + InstallationReason InstallationReason +} + +func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeOptionalDepends bool) []pkgInstallationReason { + allDepends := make([]pkgInstallationReason, 0) for _, depend := range pkgInfo.Depends { - allDepends[depend] = InstallationReasonDependency + if !slices.ContainsFunc(allDepends, func(p pkgInstallationReason) bool { + return p.PkgName == depend + }) { + allDepends = append(allDepends, pkgInstallationReason{ + PkgName: depend, + InstallationReason: InstallationReasonDependency, + }) + } } if includeOptionalDepends { for _, depend := range pkgInfo.OptionalDepends { - if _, ok := allDepends[depend]; !ok { - allDepends[depend] = InstallationReasonDependency + if !slices.ContainsFunc(allDepends, func(p pkgInstallationReason) bool { + return p.PkgName == depend + }) { + allDepends = append(allDepends, pkgInstallationReason{ + PkgName: depend, + InstallationReason: InstallationReasonDependency, + }) } } } if includeMakeDepends { for _, depend := range pkgInfo.MakeDepends { - if _, ok := allDepends[depend]; !ok { - allDepends[depend] = InstallationReasonMakeDependency + if !slices.ContainsFunc(allDepends, func(p pkgInstallationReason) bool { + return p.PkgName == depend + }) { + allDepends = append(allDepends, pkgInstallationReason{ + PkgName: depend, + InstallationReason: InstallationReasonMakeDependency, + }) } } } @@ -45,10 +67,13 @@ func (pkgInfo *PackageInfo) getAllDependencies(resolved *[]string, unresolved *[ *unresolved = append(*unresolved, pkgInfo.Name) // Loop through all dependencies - for depend := range pkgInfo.GetDependencies(includeMakeDepends, includeOptionalDepends) { + for _, pkgIR := range pkgInfo.GetDependencies(includeMakeDepends, includeOptionalDepends) { + depend := pkgIR.PkgName + if isVirtual, p := IsVirtualPackage(depend, rootDir); isVirtual { depend = p } + if !slices.Contains(*resolved, depend) { // Add current dependency to resolved slice when circular dependency is detected if slices.Contains(*unresolved, depend) { @@ -71,55 +96,78 @@ func (pkgInfo *PackageInfo) getAllDependencies(resolved *[]string, unresolved *[ *unresolved = stringSliceRemove(*unresolved, pkgInfo.Name) } -func ResolveAllPackageDependenciesFromDatabases(pkgInfo *PackageInfo, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) (resolved map[string]InstallationReason, unresolved []string) { +func ResolveAllPackageDependenciesFromDatabases(pkgInfo *PackageInfo, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) (resolved []pkgInstallationReason, unresolved []string) { // Initialize slices - resolved = make(map[string]InstallationReason) + resolved = make([]pkgInstallationReason, 0) unresolved = make([]string, 0) // Call unexported function - resolvePackageDependenciesFromDatabase(resolved, &unresolved, pkgInfo, InstallationReasonDependency, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) + resolvePackageDependenciesFromDatabase(&resolved, &unresolved, pkgInfo, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) + + // Remove main package from unresolved slice + unresolved = stringSliceRemove(unresolved, pkgInfo.Name) return resolved, unresolved } -func resolvePackageDependenciesFromDatabase(resolved map[string]InstallationReason, unresolved *[]string, pkgInfo *PackageInfo, installationReason InstallationReason, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) { +func resolvePackageDependenciesFromDatabase(resolved *[]pkgInstallationReason, unresolved *[]string, pkgInfo *PackageInfo, checkMake, checkOptional, ignoreInstalled, verbose bool, rootDir string) { // Add current package name to unresolved slice *unresolved = append(*unresolved, pkgInfo.Name) // Loop through all dependencies - for depend, ir := range pkgInfo.GetDependencies(pkgInfo.Type == "source", checkOptional) { - if _, ok := resolved[depend]; !ok { - // Add current dependency to resolved slice when circular dependency is detected - if slices.Contains(*unresolved, depend) { - if verbose { - fmt.Printf("Circular dependency was detected (%s -> %s). Installing %s first\n", pkgInfo.Name, depend, depend) - } - if _, ok := resolved[depend]; !ok { - resolved[depend] = ir - } - continue - } else if ignoreInstalled && IsPackageProvided(depend, rootDir) { - continue - } - var err error - var entry *BPMDatabaseEntry - entry, _, err = GetDatabaseEntry(depend) - if err != nil { - if entry = ResolveVirtualPackage(depend); entry == nil { - if !slices.Contains(*unresolved, depend) { - *unresolved = append(*unresolved, depend) - } - continue - } - } - resolvePackageDependenciesFromDatabase(resolved, unresolved, entry.Info, ir, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) + for _, pkgIR := range pkgInfo.GetDependencies(pkgInfo.Type == "source", checkOptional) { + // Skip dependency if it has already been resolved + if slices.ContainsFunc(*resolved, func(p pkgInstallationReason) bool { + return p.PkgName == pkgIR.PkgName + }) { + continue } - } - if _, ok := resolved[pkgInfo.Name]; !ok { - resolved[pkgInfo.Name] = installationReason + // Add current dependency to resolved slice when circular dependency is detected + if slices.Contains(*unresolved, pkgIR.PkgName) { + if verbose { + fmt.Printf("Circular dependency was detected (%s -> %s). Installing %s first\n", pkgInfo.Name, pkgIR.PkgName, pkgIR.PkgName) + } + + *resolved = append(*resolved, pkgInstallationReason{ + PkgName: pkgIR.PkgName, + InstallationReason: pkgIR.InstallationReason, + }) + continue + } + + // Skip dependency if it is already installed or provided + if ignoreInstalled && IsPackageProvided(pkgIR.PkgName, rootDir) { + continue + } + + // Get database entry for dependency + var err error + var entry *BPMDatabaseEntry + entry, _, err = GetDatabaseEntry(pkgIR.PkgName) + if err != nil { + if entry = ResolveVirtualPackage(pkgIR.PkgName); entry == nil { + if !slices.Contains(*unresolved, pkgIR.PkgName) { + *unresolved = append(*unresolved, pkgIR.PkgName) + } + continue + } + } + + // Resolve the dependencies of this dependency + resolvePackageDependenciesFromDatabase(resolved, unresolved, entry.Info, checkMake, checkOptional, ignoreInstalled, verbose, rootDir) + + // Move dependency from the unresolved slice to the resolved slice + if !slices.ContainsFunc(*resolved, func(p pkgInstallationReason) bool { + return p.PkgName == entry.Info.Name + }) { + *resolved = append(*resolved, pkgInstallationReason{ + PkgName: entry.Info.Name, + InstallationReason: pkgIR.InstallationReason, + }) + } + *unresolved = stringSliceRemove(*unresolved, entry.Info.Name) } - *unresolved = stringSliceRemove(*unresolved, pkgInfo.Name) } func GetPackageDependants(pkgName string, rootDir string) ([]string, error) { @@ -154,7 +202,9 @@ func GetPackageDependants(pkgName string, rootDir string) ([]string, error) { dependencies := installedPkg.PkgInfo.GetDependencies(false, true) // Add installed package to list if its dependencies include pkgName - if _, ok := dependencies[pkgName]; ok { + if slices.ContainsFunc(dependencies, func(p pkgInstallationReason) bool { + return p.PkgName == pkgName + }) { ret = append(ret, installedPkgName) continue } @@ -162,7 +212,9 @@ 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 _, ok := dependencies[vpkg]; ok { + if slices.ContainsFunc(dependencies, func(p pkgInstallationReason) bool { + return p.PkgName == vpkg + }) { ret = append(ret, installedPkgName) break } diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index 8e4ef23..786a233 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -142,17 +142,17 @@ func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, instal operation.UnresolvedDepends = append(operation.UnresolvedDepends, unresolved...) - for depend, installationReason := range resolved { - if !operation.ActionsContainPackage(depend) && depend != pkgInfo.Name { - if !reinstallDependencies && IsPackageInstalled(depend, operation.RootDir) { + for _, resolvedPkg := range resolved { + if !operation.ActionsContainPackage(resolvedPkg.PkgName) && resolvedPkg.PkgName != pkgInfo.Name { + if !reinstallDependencies && IsPackageInstalled(resolvedPkg.PkgName, operation.RootDir) { continue } - entry, _, err := GetDatabaseEntry(depend) + entry, _, err := GetDatabaseEntry(resolvedPkg.PkgName) if err != nil { - return errors.New("could not get database entry for package (" + depend + ")") + return errors.New("could not get database entry for package (" + resolvedPkg.PkgName + ")") } operation.InsertActionAt(pos, &FetchPackageAction{ - InstallationReason: installationReason, + InstallationReason: resolvedPkg.InstallationReason, DatabaseEntry: entry, }) pos++ From 4bfc325c66cb03699060021a423fde358f25540a Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sun, 6 Jul 2025 22:01:57 +0300 Subject: [PATCH 75/88] Show error when compiling source packages in a different root directory --- src/bpmlib/general.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index ff80caa..fe4d6e5 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -7,6 +7,7 @@ import ( "os" "path" "slices" + "strings" ) type ReinstallMethod uint8 @@ -128,6 +129,28 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein } } + // Check whether compiling source packages on different root directory + if rootDir != "/" { + sourcePackages := make([]string, 0) + for _, action := range operation.Actions { + switch action.(type) { + case *InstallPackageAction: + if action.(*InstallPackageAction).BpmPackage.PkgInfo.Type == "source" { + sourcePackages = append(sourcePackages, action.(*InstallPackageAction).BpmPackage.PkgInfo.Name) + } + case *FetchPackageAction: + if action.(*FetchPackageAction).DatabaseEntry.Info.Type == "source" { + sourcePackages = append(sourcePackages, action.(*FetchPackageAction).DatabaseEntry.Info.Name) + } + } + } + + // Return error if source packages are present in the operation + if len(sourcePackages) != 0 { + return nil, fmt.Errorf("cannot compile source packages in different root directory: %s", strings.Join(sourcePackages, ", ")) + } + } + return operation, nil } From 6492fd028faf92176be663987954c0df0e775fde Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 17 Jul 2025 17:09:36 +0300 Subject: [PATCH 76/88] Fix directories not being created with correct permissions and improve debug messages --- src/bpmlib/packages.go | 59 ++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index ce69858..2ac878d 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -16,6 +16,7 @@ import ( "sort" "strconv" "strings" + "syscall" ) type BPMPackage struct { @@ -584,14 +585,25 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) extractFilename := path.Join(rootDir, header.Name) switch header.Typeflag { case tar.TypeDir: - if err := os.Mkdir(extractFilename, 0755); err != nil { - if !os.IsExist(err) { - return err - } - } else { + if _, err := os.Stat(extractFilename); err == nil { if verbose { - fmt.Println("Creating Directory: " + extractFilename) + fmt.Printf("Skipping Directory: %s (Directory already exists)\n", extractFilename) } + continue + } + + if err := os.Mkdir(extractFilename, 0755); err != nil && !os.IsExist(err) { + return err + } + + // Using syscall instead of os.Chmod because it seems to strip the setuid, setgid and sticky bits + err := syscall.Chmod(extractFilename, uint32(header.Mode)) + if err != nil { + return err + } + + if verbose { + fmt.Printf("Created directory %s (%o)\n", extractFilename, header.Mode) } case tar.TypeReg: skip := false @@ -600,7 +612,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) if strings.HasSuffix(k, "/") { if strings.HasPrefix(header.Name, k) { if verbose { - fmt.Println("Skipping File: " + extractFilename + " (Containing directory is set to be kept during reinstalls/updates)") + fmt.Printf("Skipping File: %s (Containing directory is set to be kept during reinstalls/updates)\n", extractFilename) } skip = true continue @@ -608,7 +620,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) } else { if header.Name == k { if verbose { - fmt.Println("Skipping File: " + extractFilename + " (File is configured to be kept during reinstalls/updates)") + fmt.Printf("Skipping File: %s (File is configured to be kept during reinstalls/updates)\n", extractFilename) } skip = true continue @@ -624,34 +636,40 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) return err } outFile, err := os.Create(extractFilename) - if verbose { - fmt.Println("Creating File: " + extractFilename) - } if err != nil { return err } if _, err := io.Copy(outFile, packageFilesReader); err != nil { return err } - if err := os.Chmod(extractFilename, header.FileInfo().Mode()); err != nil { - return err - } err = outFile.Close() if err != nil { return err } - case tar.TypeSymlink: - if verbose { - fmt.Println("Creating Symlink: " + extractFilename + " -> " + header.Linkname) + + // Using syscall instead of os.Chmod because it seems to strip the setuid, setgid and sticky bits + err = syscall.Chmod(extractFilename, uint32(header.Mode)) + if err != nil { + return err } + + if verbose { + fmt.Printf("Created File: %s (%o)\n", extractFilename, header.Mode) + } + case tar.TypeSymlink: err := os.Remove(extractFilename) if err != nil && !os.IsNotExist(err) { return err } + err = os.Symlink(header.Linkname, extractFilename) if err != nil { return err } + + if verbose { + fmt.Println("Created Symlink: " + extractFilename + " -> " + header.Linkname) + } case tar.TypeLink: if verbose { fmt.Println("Detected Hard Link: " + extractFilename + " -> " + path.Join(rootDir, strings.TrimPrefix(header.Linkname, "files/"))) @@ -666,13 +684,14 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) } } for extractFilename, destination := range seenHardlinks { - if verbose { - fmt.Println("Creating Hard Link: " + extractFilename + " -> " + path.Join(rootDir, destination)) - } err := os.Link(path.Join(rootDir, destination), extractFilename) if err != nil { return err } + + if verbose { + fmt.Println("Created Hard Link: " + extractFilename + " -> " + path.Join(rootDir, destination)) + } } defer archive.Close() defer file.Close() From e63398c853c32f336993306699be81288186a87a Mon Sep 17 00:00:00 2001 From: EnumDev Date: Fri, 1 Aug 2025 19:19:08 +0300 Subject: [PATCH 77/88] Set correct ownership during package extraction --- src/bpmlib/packages.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index 2ac878d..fd4a368 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -596,6 +596,11 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) return err } + err = os.Chown(extractFilename, header.Uid, header.Gid) + if err != nil { + return err + } + // Using syscall instead of os.Chmod because it seems to strip the setuid, setgid and sticky bits err := syscall.Chmod(extractFilename, uint32(header.Mode)) if err != nil { @@ -647,6 +652,11 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) return err } + err = os.Chown(extractFilename, header.Uid, header.Gid) + if err != nil { + return err + } + // Using syscall instead of os.Chmod because it seems to strip the setuid, setgid and sticky bits err = syscall.Chmod(extractFilename, uint32(header.Mode)) if err != nil { From ef5697321b04b4d588b30928397db6828c03205a Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sun, 3 Aug 2025 09:20:37 +0300 Subject: [PATCH 78/88] Default to not installing optional dependencies --- src/bpm/main.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index bf6e727..0ad19e9 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -34,7 +34,7 @@ var pkgListNumbers = false var pkgListNames = false var reinstall = false var reinstallAll = false -var noOptional = false +var installOptional = false var installationReason = "" var nosync = true var removeUnused = false @@ -280,7 +280,7 @@ func resolveCommand() { } // Create installation operation - operation, err := bpmlib.InstallPackages(rootDir, ir, reinstallMethod, !noOptional, force, verbose, subcommandArgs...) + operation, err := bpmlib.InstallPackages(rootDir, ir, reinstallMethod, installOptional, force, verbose, subcommandArgs...) if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { log.Fatalf("Error: %s", err) } else if err != nil { @@ -339,7 +339,7 @@ func resolveCommand() { } // Create update operation - operation, err := bpmlib.UpdatePackages(rootDir, !nosync, !noOptional, force, verbose) + operation, err := bpmlib.UpdatePackages(rootDir, !nosync, installOptional, force, verbose) if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) { log.Fatalf("Error: %s", err) } else if err != nil { @@ -756,7 +756,7 @@ func printHelp() { fmt.Println(" -f skips dependency, conflict and architecture checking") fmt.Println(" --reinstall Reinstalls packages even if they do not have a newer version available") fmt.Println(" --reinstall-all Same as --reinstall but also reinstalls dependencies") - fmt.Println(" --no-optional Prevents installation of optional dependencies") + fmt.Println(" --optional Installs all optional dependencies") fmt.Println(" --installation-reason= sets the installation reason for all newly installed packages") fmt.Println("-> bpm update [-R, -v, -y, -f, --reinstall, --no-sync] | updates all packages that are available in the configured databases") fmt.Println(" -R= lets you define the root path which will be used") @@ -816,7 +816,7 @@ func resolveFlags() { installFlagSet.BoolVar(&force, "f", false, "Force installation by skipping architecture and dependency resolution") installFlagSet.BoolVar(&reinstall, "reinstall", false, "Reinstalls packages even if they do not have a newer version available") installFlagSet.BoolVar(&reinstallAll, "reinstall-all", false, "Same as --reinstall but also reinstalls dependencies") - installFlagSet.BoolVar(&noOptional, "no-optional", false, "Prevents installation of optional dependencies") + installFlagSet.BoolVar(&installOptional, "optional", false, "Installs all optional dependencies") installFlagSet.StringVar(&installationReason, "installation-reason", "", "Set the installation reason for all newly installed packages") installFlagSet.Usage = printHelp // Update flags From 38b6ea0175da9d4607ff60670ad4fe20549c971f Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sun, 3 Aug 2025 09:26:43 +0300 Subject: [PATCH 79/88] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 527b23d..25250e1 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ BPM is a simple package manager for Linux systems ## Features - Simple to use subcommands -- Can install binary packages (and source packages in the future) +- Can install both binary and source packages ## Information BPM is still in very early development. Do not install it without knowing what you are doing. I would only recommend using it in a Virtual Machine for testing @@ -64,4 +64,4 @@ bpm help Package creation is simplified using the bpm-utils package which contains helper scripts for creating and archiving packages -Learn more here: https://git.enumerated.dev/bubble-package-manager/bpm-utils \ No newline at end of file +Learn more here: https://git.enumerated.dev/bubble-package-manager/bpm-utils From 713eafb7082a895de2015e9e777baf531ee44b39 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Wed, 6 Aug 2025 21:07:07 +0300 Subject: [PATCH 80/88] Remove dependency on 'which' --- Makefile | 4 ++-- README.md | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 7c8a82e..57d22dc 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ BINDIR ?= $(PREFIX)/bin SYSCONFDIR := $(PREFIX)/etc # Compilers and tools -GO ?= $(shell which go) +GO ?= go # Build-time variables ROOT_COMPILATION_UID ?= 65534 @@ -29,4 +29,4 @@ uninstall: clean: rm -r build/ -.PHONY: build \ No newline at end of file +.PHONY: build diff --git a/README.md b/README.md index 25250e1..5813e5b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ BPM is still in very early development. Do not install it without knowing what y - Download `go` from your package manager or from the go website - Download `make` from your package manager -- Download `which` from your package manager - Run the following command to compile the project. You may need to set the `GO` environment variable if your Go installation is not in your PATH ```sh make From 57231fdec5bcaf9042ddac7eee8b019b1149381c Mon Sep 17 00:00:00 2001 From: EnumDev Date: Mon, 11 Aug 2025 12:23:45 +0300 Subject: [PATCH 81/88] Fix package installation reason not being displayed or set correctly --- src/bpm/main.go | 5 +++-- src/bpmlib/general.go | 33 ++++++++++++++++++++++++++++++++- src/bpmlib/operations.go | 24 ++++++++++++------------ 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/bpm/main.go b/src/bpm/main.go index 0ad19e9..ffb0698 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -5,7 +5,6 @@ import ( "errors" "flag" "fmt" - "git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib" "log" "os" "os/exec" @@ -13,6 +12,8 @@ import ( "path/filepath" "slices" "strings" + + "git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib" ) /* -------------BPM | Bubble Package Manager-------------- */ @@ -250,7 +251,7 @@ func resolveCommand() { } // Check if installationReason argument is valid - ir := bpmlib.InstallationReasonManual + ir := bpmlib.InstallationReasonUnknown switch installationReason { case "manual": ir = bpmlib.InstallationReasonManual diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index fe4d6e5..b3ba3ad 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -19,7 +19,8 @@ const ( ) // 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, installationReason 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 operation = &BPMOperation{ Actions: make([]OperationAction, 0), @@ -44,6 +45,16 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein continue } + // Set package installation reason + installationReason := forceInstallationReason + if installationReason == InstallationReasonUnknown { + if IsPackageInstalled(splitPkg.Name, rootDir) { + installationReason = GetInstallationReason(splitPkg.Name, rootDir) + } else { + installationReason = InstallationReasonManual + } + } + operation.AppendAction(&InstallPackageAction{ File: pkg, InstallationReason: installationReason, @@ -58,6 +69,16 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein continue } + // Set package installation reason + installationReason := forceInstallationReason + if installationReason == InstallationReasonUnknown { + if IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { + installationReason = GetInstallationReason(bpmpkg.PkgInfo.Name, rootDir) + } else { + installationReason = InstallationReasonManual + } + } + operation.AppendAction(&InstallPackageAction{ File: pkg, InstallationReason: installationReason, @@ -84,6 +105,16 @@ func InstallPackages(rootDir string, installationReason InstallationReason, rein continue } + // Set package installation reason + installationReason := forceInstallationReason + if installationReason == InstallationReasonUnknown { + if IsPackageInstalled(entry.Info.Name, rootDir) { + installationReason = GetInstallationReason(entry.Info.Name, rootDir) + } else { + installationReason = InstallationReasonManual + } + } + operation.AppendAction(&FetchPackageAction{ InstallationReason: installationReason, DatabaseEntry: entry, diff --git a/src/bpmlib/operations.go b/src/bpmlib/operations.go index 786a233..2e0b4a7 100644 --- a/src/bpmlib/operations.go +++ b/src/bpmlib/operations.go @@ -287,7 +287,7 @@ func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error) for i, value := range installedPackages { bpmpkg := GetPackage(value, operation.RootDir) if bpmpkg == nil { - return nil, errors.New(fmt.Sprintf("could not find installed package (%s)", value)) + return nil, fmt.Errorf("could not find installed package (%s)", value) } allPackages[i] = bpmpkg.PkgInfo } @@ -452,13 +452,13 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) { // Fetch package from database fetchedPackage, err := entry.Database.FetchPackage(entry.Info.Name) if err != nil { - return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err)) + return fmt.Errorf("could not fetch package (%s): %s\n", entry.Info.Name, err) } // Read fetched package bpmpkg, err = ReadPackage(fetchedPackage) if err != nil { - return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err)) + return fmt.Errorf("could not fetch package (%s): %s\n", entry.Info.Name, err) } // Add fetched package to map @@ -469,7 +469,7 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) { // Read fetched package bpmpkg, err = ReadPackage(fetchedPackages[entry.Download]) if err != nil { - return errors.New(fmt.Sprintf("could not read package (%s): %s\n", entry.Info.Name, err)) + return fmt.Errorf("could not read package (%s): %s\n", entry.Info.Name, err) } fmt.Printf("Package (%s) was successfully fetched!\n", entry.Info.Name) @@ -517,13 +517,12 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) { pkgInfo := action.(*RemovePackageAction).BpmPackage.PkgInfo err := removePackage(pkgInfo.Name, verbose, operation.RootDir) if err != nil { - return errors.New(fmt.Sprintf("could not remove package (%s): %s\n", pkgInfo.Name, err)) + return fmt.Errorf("could not remove package (%s): %s\n", pkgInfo.Name, err) } } else if action.GetActionType() == "install" { value := action.(*InstallPackageAction) fileToInstall := value.File bpmpkg := value.BpmPackage - isReinstall := IsPackageInstalled(bpmpkg.PkgInfo.Name, operation.RootDir) var err error // Compile package if type is 'source' @@ -572,14 +571,15 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) { err = installPackage(fileToInstall, operation.RootDir, verbose, force) } if err != nil { - return errors.New(fmt.Sprintf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err)) + return fmt.Errorf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err) } - if !isReinstall { - err := SetInstallationReason(bpmpkg.PkgInfo.Name, value.InstallationReason, operation.RootDir) - if err != nil { - return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err)) - } + + // Set installed package's installation reason + err = SetInstallationReason(bpmpkg.PkgInfo.Name, value.InstallationReason, operation.RootDir) + if err != nil { + return fmt.Errorf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err) } + fmt.Printf("Package (%s) was successfully installed\n", bpmpkg.PkgInfo.Name) } } From c01ca62e34be122ac150021cf989ff6a793ffdde Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 14 Aug 2025 14:48:41 +0300 Subject: [PATCH 82/88] Use chroot for package scripts --- src/bpmlib/packages.go | 44 ++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index fd4a368..f8a8ce1 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -5,8 +5,6 @@ import ( "compress/gzip" "errors" "fmt" - version "github.com/knqyf263/go-rpm-version" - "gopkg.in/yaml.v3" "io" "log" "os" @@ -17,6 +15,9 @@ import ( "strconv" "strings" "syscall" + + version "github.com/knqyf263/go-rpm-version" + "gopkg.in/yaml.v3" ) type BPMPackage struct { @@ -321,19 +322,16 @@ func executePackageScripts(filename, rootDir string, operation packageOperation, } run := func(name, content string) error { - temp, err := os.CreateTemp("", name) - if err != nil { - return err + cmd := exec.Command("/bin/bash", "-c", content) + // Setup subprocess environment + cmd.Dir = "/" + // Run hook in chroot if using the -R flag + if rootDir != "/" { + cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: rootDir} } - _, err = temp.WriteString(content) - if err != nil { - return err - } - - cmd := exec.Command("/bin/bash", temp.Name()) - cmd.Dir = rootDir + // Setup command environment cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir)) + cmd.Env = append(cmd.Env, "BPM_ROOT=/") // Setting to "/" for backwards compatibility cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", pkgInfo.PkgInfo.Name)) cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DESC=%s", pkgInfo.PkgInfo.Description)) cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", pkgInfo.PkgInfo.Version)) @@ -939,9 +937,15 @@ func removePackage(pkg string, verbose bool, rootDir string) error { // Executing pre_remove script if _, err := os.Stat(path.Join(pkgDir, "pre_remove.sh")); err == nil { cmd := exec.Command("/bin/bash", path.Join(pkgDir, "pre_remove.sh")) - cmd.Dir = rootDir + // Setup subprocess environment + cmd.Dir = "/" + // Run hook in chroot if using the -R flag + if rootDir != "/" { + cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: rootDir} + } + // Setup command environment cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir)) + cmd.Env = append(cmd.Env, "BPM_ROOT=/") // Setting to "/" for backwards compatibility cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", pkgInfo.Name)) cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", pkgInfo.Version)) cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", pkgInfo.Revision)) @@ -1030,9 +1034,15 @@ func removePackage(pkg string, verbose bool, rootDir string) error { // Executing post_remove script if _, err := os.Stat(path.Join(pkgDir, "post_remove.sh")); err == nil { cmd := exec.Command("/bin/bash", path.Join(pkgDir, "post_remove.sh")) - cmd.Dir = rootDir + // Setup subprocess environment + cmd.Dir = "/" + // Run hook in chroot if using the -R flag + if rootDir != "/" { + cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: rootDir} + } + // Setup command environment cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir)) + cmd.Env = append(cmd.Env, "BPM_ROOT=/") // Setting to "/" for backwards compatibility cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", pkgInfo.Name)) cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", pkgInfo.Version)) cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", pkgInfo.Revision)) From 6501105dd19b99ea07620750b4dc94759e3796fe Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 14 Aug 2025 17:07:42 +0300 Subject: [PATCH 83/88] Fix remove package scripts not working with chroot --- src/bpmlib/packages.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index f8a8ce1..b304f8f 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -927,15 +927,14 @@ func installPackage(filename, rootDir string, verbose, force bool) error { } func removePackage(pkg string, verbose bool, rootDir string) error { - installedDir := path.Join(rootDir, "var/lib/bpm/installed/") - pkgDir := path.Join(installedDir, pkg) + pkgDir := path.Join("/var/lib/bpm/installed/", pkg) pkgInfo := GetPackageInfo(pkg, rootDir) if pkgInfo == nil { return errors.New("could not get package info") } // Executing pre_remove script - if _, err := os.Stat(path.Join(pkgDir, "pre_remove.sh")); err == nil { + if _, err := os.Stat(path.Join(rootDir, pkgDir, "pre_remove.sh")); err == nil { cmd := exec.Command("/bin/bash", path.Join(pkgDir, "pre_remove.sh")) // Setup subprocess environment cmd.Dir = "/" @@ -1032,7 +1031,7 @@ func removePackage(pkg string, verbose bool, rootDir string) error { } // Executing post_remove script - if _, err := os.Stat(path.Join(pkgDir, "post_remove.sh")); err == nil { + if _, err := os.Stat(path.Join(rootDir, pkgDir, "post_remove.sh")); err == nil { cmd := exec.Command("/bin/bash", path.Join(pkgDir, "post_remove.sh")) // Setup subprocess environment cmd.Dir = "/" @@ -1051,15 +1050,15 @@ func removePackage(pkg string, verbose bool, rootDir string) error { err = cmd.Run() if err != nil { - log.Printf("Warning: could not run pre_remove.sh package script: %s", err) + log.Printf("Warning: could not run post_remove.sh package script: %s", err) } } // Removing package directory if verbose { - fmt.Println("Removing: " + pkgDir) + fmt.Println("Removing: " + path.Join(rootDir, pkgDir)) } - err = os.RemoveAll(pkgDir) + err = os.RemoveAll(path.Join(rootDir, pkgDir)) if err != nil { return err } From 17af8dbaaf3d86c12f0caff14306212be12a217a Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 14 Aug 2025 17:09:18 +0300 Subject: [PATCH 84/88] Turn package script fatal errors into warnings --- src/bpmlib/errors.go | 11 +++++++++++ src/bpmlib/packages.go | 20 ++++++++++---------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/bpmlib/errors.go b/src/bpmlib/errors.go index 618251a..8b46b94 100644 --- a/src/bpmlib/errors.go +++ b/src/bpmlib/errors.go @@ -28,4 +28,15 @@ type PackageConflictErr struct { func (e PackageConflictErr) Error() string { return fmt.Sprintf("Package (%s) is in conflict with the following packages: %s", e.pkg, strings.Join(e.conflicts, ", ")) + +} + +type PackageScriptErr struct { + err error + packageName string + packageScript 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) } diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index b304f8f..a908eef 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -368,39 +368,39 @@ func executePackageScripts(filename, rootDir string, operation packageOperation, if val, ok := scripts["pre_install.sh"]; !postOperation && ok { err := run("pre_install.sh", val) if err != nil { - return err + return PackageScriptErr{err: err, packageName: pkgInfo.PkgInfo.Name, packageScript: "pre_install.sh"} } } if val, ok := scripts["post_install.sh"]; postOperation && ok { err := run("post_install.sh", val) if err != nil { - return err + return PackageScriptErr{err: err, packageName: pkgInfo.PkgInfo.Name, packageScript: "post_install.sh"} } } } else if operation == packageOperationUpdate { if val, ok := scripts["pre_update.sh"]; !postOperation && ok { err := run("pre_update.sh", val) if err != nil { - return err + return PackageScriptErr{err: err, packageName: pkgInfo.PkgInfo.Name, packageScript: "pre_update.sh"} } } if val, ok := scripts["post_update.sh"]; postOperation && ok { err := run("post_update.sh", val) if err != nil { - return err + return PackageScriptErr{err: err, packageName: pkgInfo.PkgInfo.Name, packageScript: "post_update.sh"} } } } else if operation == packageOperationRemove { if val, ok := scripts["pre_remove.sh"]; !postOperation && ok { err := run("pre_remove.sh", val) if err != nil { - return err + return PackageScriptErr{err: err, packageName: pkgInfo.PkgInfo.Name, packageScript: "pre_remove.sh"} } } if val, ok := scripts["post_remove.sh"]; postOperation && ok { err := run("post_remove.sh", val) if err != nil { - return err + return PackageScriptErr{err: err, packageName: pkgInfo.PkgInfo.Name, packageScript: "post_remove.sh"} } } } @@ -547,12 +547,12 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { err := executePackageScripts(filename, rootDir, packageOperationInstall, false) if err != nil { - return err + log.Printf("Warning: %s\n", err) } } else { err := executePackageScripts(filename, rootDir, packageOperationUpdate, false) if err != nil { - return err + log.Printf("Warning: %s\n", err) } } seenHardlinks := make(map[string]string) @@ -905,12 +905,12 @@ func installPackage(filename, rootDir string, verbose, force bool) error { if !packageInstalled { err = executePackageScripts(filename, rootDir, packageOperationInstall, true) if err != nil { - return err + log.Printf("Warning: %s\n", err) } } else { err = executePackageScripts(filename, rootDir, packageOperationUpdate, true) if err != nil { - return err + log.Printf("Warning: %s\n", err) } } From 5181c43ecfe914e5f317283ddc785e9e11225f84 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 14 Aug 2025 18:27:34 +0300 Subject: [PATCH 85/88] Rewrite executePackageScripts function --- src/bpmlib/packages.go | 230 ++++++++++++++++++----------------------- 1 file changed, 99 insertions(+), 131 deletions(-) diff --git a/src/bpmlib/packages.go b/src/bpmlib/packages.go index a908eef..070072d 100644 --- a/src/bpmlib/packages.go +++ b/src/bpmlib/packages.go @@ -311,98 +311,102 @@ const ( packageOperationRemove = 2 ) -func executePackageScripts(filename, rootDir string, operation packageOperation, postOperation bool) error { - pkgInfo, err := ReadPackage(filename) - if err != nil { - return err - } - scripts, err := ReadPackageScripts(filename) - if err != nil { - return err - } +func executePackageScript(pkg, rootDir string, verbose bool, packageScript string) error { + var bpmpkg *BPMPackage + var err error + scripts := make(map[string]string) - run := func(name, content string) error { - cmd := exec.Command("/bin/bash", "-c", content) - // Setup subprocess environment - cmd.Dir = "/" - // Run hook in chroot if using the -R flag - if rootDir != "/" { - cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: rootDir} - } - // Setup command environment - cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, "BPM_ROOT=/") // Setting to "/" for backwards compatibility - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", pkgInfo.PkgInfo.Name)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DESC=%s", pkgInfo.PkgInfo.Description)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", pkgInfo.PkgInfo.Version)) - if operation != packageOperationInstall { - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_OLD_VERSION=%s", GetPackageInfo(pkgInfo.PkgInfo.Name, rootDir).Version)) - } - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", pkgInfo.PkgInfo.Revision)) - if operation != packageOperationInstall { - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_OLD_REVISION=%d", GetPackageInfo(pkgInfo.PkgInfo.Name, rootDir).Revision)) - } - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", pkgInfo.PkgInfo.Url)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", pkgInfo.PkgInfo.Arch)) - depends := make([]string, len(pkgInfo.PkgInfo.Depends)) - copy(depends, pkgInfo.PkgInfo.Depends) - for i := 0; i < len(depends); i++ { - depends[i] = fmt.Sprintf("\"%s\"", depends[i]) - } - makeDepends := make([]string, len(pkgInfo.PkgInfo.MakeDepends)) - copy(makeDepends, pkgInfo.PkgInfo.MakeDepends) - for i := 0; i < len(makeDepends); i++ { - makeDepends[i] = fmt.Sprintf("\"%s\"", makeDepends[i]) - } - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DEPENDS=(%s)", strings.Join(depends, " "))) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_MAKE_DEPENDS=(%s)", strings.Join(makeDepends, " "))) - cmd.Env = append(cmd.Env, "BPM_PKG_TYPE=source") - err = cmd.Run() + // Fetch bpmpkg variable from file or installed package + if strings.HasSuffix(pkg, ".bpm") { + bpmpkg, err = ReadPackage(pkg) if err != nil { return err } + + // Read package scripts from tarball + scripts, err = ReadPackageScripts(pkg) + if err != nil { + return err + } + } else { + bpmpkg = GetPackage(pkg, rootDir) + if bpmpkg == nil { + return fmt.Errorf("Package not found: %s", pkg) + } + } + + // Read installed remove package scripts + if IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { + pkgDir := path.Join(rootDir, "var/lib/bpm/installed", bpmpkg.PkgInfo.Name) + if _, err := os.Stat(path.Join(pkgDir, "pre_remove.sh")); err == nil { + data, err := os.ReadFile(path.Join(pkgDir, "pre_remove.sh")) + if err != nil { + return err + } + scripts["pre_remove.sh"] = string(data) + } + if _, err := os.Stat(path.Join(pkgDir, "post_remove.sh")); err == nil { + data, err := os.ReadFile(path.Join(pkgDir, "post_remove.sh")) + if err != nil { + return err + } + scripts["post_remove.sh"] = string(data) + } + } + + // Ensure package script exists + content, ok := scripts[packageScript] + if !ok { return nil } - if operation == packageOperationInstall { - if val, ok := scripts["pre_install.sh"]; !postOperation && ok { - err := run("pre_install.sh", val) - if err != nil { - return PackageScriptErr{err: err, packageName: pkgInfo.PkgInfo.Name, packageScript: "pre_install.sh"} - } - } - if val, ok := scripts["post_install.sh"]; postOperation && ok { - err := run("post_install.sh", val) - if err != nil { - return PackageScriptErr{err: err, packageName: pkgInfo.PkgInfo.Name, packageScript: "post_install.sh"} - } - } - } else if operation == packageOperationUpdate { - if val, ok := scripts["pre_update.sh"]; !postOperation && ok { - err := run("pre_update.sh", val) - if err != nil { - return PackageScriptErr{err: err, packageName: pkgInfo.PkgInfo.Name, packageScript: "pre_update.sh"} - } - } - if val, ok := scripts["post_update.sh"]; postOperation && ok { - err := run("post_update.sh", val) - if err != nil { - return PackageScriptErr{err: err, packageName: pkgInfo.PkgInfo.Name, packageScript: "post_update.sh"} - } - } - } else if operation == packageOperationRemove { - if val, ok := scripts["pre_remove.sh"]; !postOperation && ok { - err := run("pre_remove.sh", val) - if err != nil { - return PackageScriptErr{err: err, packageName: pkgInfo.PkgInfo.Name, packageScript: "pre_remove.sh"} - } - } - if val, ok := scripts["post_remove.sh"]; postOperation && ok { - err := run("post_remove.sh", val) - if err != nil { - return PackageScriptErr{err: err, packageName: pkgInfo.PkgInfo.Name, packageScript: "post_remove.sh"} - } - } + cmd := exec.Command("/bin/bash", "-c", content) + // Setup subprocess environment + cmd.Dir = "/" + // Run hook in chroot if using the -R flag + if rootDir != "/" { + cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: rootDir} + } + // Show output if verbose + if verbose { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } + // Setup command environment + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, "BPM_ROOT=/") // Setting to "/" for backwards compatibility + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", bpmpkg.PkgInfo.Name)) + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DESC=%s", bpmpkg.PkgInfo.Description)) + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", bpmpkg.PkgInfo.Version)) + if IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_OLD_VERSION=%s", GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).Version)) + } + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", bpmpkg.PkgInfo.Revision)) + if IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_OLD_REVISION=%d", GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).Revision)) + } + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", bpmpkg.PkgInfo.Url)) + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", bpmpkg.PkgInfo.Arch)) + depends := make([]string, len(bpmpkg.PkgInfo.Depends)) + copy(depends, bpmpkg.PkgInfo.Depends) + for i := 0; i < len(depends); i++ { + depends[i] = fmt.Sprintf("\"%s\"", depends[i]) + } + makeDepends := make([]string, len(bpmpkg.PkgInfo.MakeDepends)) + copy(makeDepends, bpmpkg.PkgInfo.MakeDepends) + for i := 0; i < len(makeDepends); i++ { + makeDepends[i] = fmt.Sprintf("\"%s\"", makeDepends[i]) + } + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DEPENDS=(%s)", strings.Join(depends, " "))) + cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_MAKE_DEPENDS=(%s)", strings.Join(makeDepends, " "))) + cmd.Env = append(cmd.Env, "BPM_PKG_TYPE=source") + // Run command + if verbose { + fmt.Printf("Running package script (%s) for package (%s)\n", packageScript, bpmpkg.PkgInfo.Name) + } + err = cmd.Run() + if err != nil { + return PackageScriptErr{err: err, packageName: bpmpkg.PkgInfo.Name, packageScript: packageScript} } return nil } @@ -545,12 +549,12 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations, showIn func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) error { if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) { - err := executePackageScripts(filename, rootDir, packageOperationInstall, false) + err := executePackageScript(filename, rootDir, verbose, "pre_install.sh") if err != nil { log.Printf("Warning: %s\n", err) } } else { - err := executePackageScripts(filename, rootDir, packageOperationUpdate, false) + err := executePackageScript(filename, rootDir, verbose, "pre_update.sh") if err != nil { log.Printf("Warning: %s\n", err) } @@ -903,12 +907,12 @@ func installPackage(filename, rootDir string, verbose, force bool) error { } if !packageInstalled { - err = executePackageScripts(filename, rootDir, packageOperationInstall, true) + err = executePackageScript(filename, rootDir, verbose, "post_install.sh") if err != nil { log.Printf("Warning: %s\n", err) } } else { - err = executePackageScripts(filename, rootDir, packageOperationUpdate, true) + err = executePackageScript(filename, rootDir, verbose, "post_update.sh") if err != nil { log.Printf("Warning: %s\n", err) } @@ -934,27 +938,9 @@ func removePackage(pkg string, verbose bool, rootDir string) error { } // Executing pre_remove script - if _, err := os.Stat(path.Join(rootDir, pkgDir, "pre_remove.sh")); err == nil { - cmd := exec.Command("/bin/bash", path.Join(pkgDir, "pre_remove.sh")) - // Setup subprocess environment - cmd.Dir = "/" - // Run hook in chroot if using the -R flag - if rootDir != "/" { - cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: rootDir} - } - // Setup command environment - cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, "BPM_ROOT=/") // Setting to "/" for backwards compatibility - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", pkgInfo.Name)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", pkgInfo.Version)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", pkgInfo.Revision)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", pkgInfo.Url)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", pkgInfo.Arch)) - - err = cmd.Run() - if err != nil { - log.Printf("Warning: could not run pre_remove.sh package script: %s", err) - } + err := executePackageScript(pkg, rootDir, verbose, "pre_remove.sh") + if err != nil { + log.Printf("Warning: %s\n", err) } // Fetching and reversing package file entry list @@ -1031,27 +1017,9 @@ func removePackage(pkg string, verbose bool, rootDir string) error { } // Executing post_remove script - if _, err := os.Stat(path.Join(rootDir, pkgDir, "post_remove.sh")); err == nil { - cmd := exec.Command("/bin/bash", path.Join(pkgDir, "post_remove.sh")) - // Setup subprocess environment - cmd.Dir = "/" - // Run hook in chroot if using the -R flag - if rootDir != "/" { - cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: rootDir} - } - // Setup command environment - cmd.Env = os.Environ() - cmd.Env = append(cmd.Env, "BPM_ROOT=/") // Setting to "/" for backwards compatibility - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", pkgInfo.Name)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", pkgInfo.Version)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", pkgInfo.Revision)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", pkgInfo.Url)) - cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", pkgInfo.Arch)) - - err = cmd.Run() - if err != nil { - log.Printf("Warning: could not run post_remove.sh package script: %s", err) - } + err = executePackageScript(pkg, rootDir, verbose, "post_remove.sh") + if err != nil { + log.Printf("Warning: %s\n", err) } // Removing package directory From 812caa14857a7740d73cc7205e93c0813c3f70fa Mon Sep 17 00:00:00 2001 From: EnumDev Date: Thu, 14 Aug 2025 22:35:11 +0300 Subject: [PATCH 86/88] Split compilation config options into separate config file --- Makefile | 1 + config/bpm-compilation.conf | 2 ++ config/bpm.conf | 4 +-- src/bpm/main.go | 8 +++--- src/bpmlib/compilation.go | 5 ++-- src/bpmlib/config.go | 56 +++++++++++++++++++++++++------------ src/bpmlib/databases.go | 13 +++++---- src/bpmlib/general.go | 4 +-- 8 files changed, 58 insertions(+), 35 deletions(-) create mode 100644 config/bpm-compilation.conf diff --git a/Makefile b/Makefile index 57d22dc..4ebcf61 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ install: build/bpm config/ # Install files install -Dm755 build/bpm $(DESTDIR)$(BINDIR)/bpm install -Dm644 config/bpm.conf $(DESTDIR)$(SYSCONFDIR)/bpm.conf + install -Dm644 config/bpm-compilation.conf $(DESTDIR)$(SYSCONFDIR)/bpm-compilation.conf uninstall: rm $(DESTDIR)$(BINDIR)/bpm diff --git a/config/bpm-compilation.conf b/config/bpm-compilation.conf new file mode 100644 index 0000000..cd28321 --- /dev/null +++ b/config/bpm-compilation.conf @@ -0,0 +1,2 @@ +privilege_escalator_cmd: "sudo" +compilation_env: [] diff --git a/config/bpm.conf b/config/bpm.conf index d594d35..8c64765 100644 --- a/config/bpm.conf +++ b/config/bpm.conf @@ -1,8 +1,6 @@ ignore_packages: [] -privilege_escalator_cmd: "sudo" -compilation_env: [] cleanup_make_dependencies: true databases: - name: example-database source: https://my-database.xyz/ - disabled: true \ No newline at end of file + disabled: true diff --git a/src/bpm/main.go b/src/bpm/main.go index ffb0698..41b3440 100644 --- a/src/bpm/main.go +++ b/src/bpm/main.go @@ -217,7 +217,7 @@ func resolveCommand() { for i, term := range searchTerms { nameResults := make([]*bpmlib.PackageInfo, 0) descResults := make([]*bpmlib.PackageInfo, 0) - for _, db := range bpmlib.BPMConfig.Databases { + for _, db := range bpmlib.MainBPMConfig.Databases { for _, entry := range db.Entries { if strings.Contains(entry.Info.Name, term) { nameResults = append(nameResults, entry.Info) @@ -630,7 +630,7 @@ func resolveCommand() { // Run 'bpm install' using the set privilege escalator command args := []string{executable, "install", "--installation-reason=make-dependency"} args = append(args, unmetDepends...) - cmd := exec.Command(bpmlib.BPMConfig.PrivilegeEscalatorCmd, args...) + cmd := exec.Command(bpmlib.CompilationBPMConfig.PrivilegeEscalatorCmd, args...) if yesAll { cmd.Args = slices.Insert(cmd.Args, 3, "-y") } @@ -714,7 +714,7 @@ func resolveCommand() { } // Run 'bpm cleanup' using the set privilege escalator command - cmd := exec.Command(bpmlib.BPMConfig.PrivilegeEscalatorCmd, executable, "cleanup") + cmd := exec.Command(bpmlib.CompilationBPMConfig.PrivilegeEscalatorCmd, executable, "cleanup") if yesAll { cmd.Args = slices.Insert(cmd.Args, 3, "-y") } @@ -924,7 +924,7 @@ func resolveFlags() { } if !isFlagSet(cleanupFlagSet, "depends") && !isFlagSet(cleanupFlagSet, "make-depends") && !isFlagSet(cleanupFlagSet, "compilation-files") && !isFlagSet(cleanupFlagSet, "compiled-pkgs") && !isFlagSet(cleanupFlagSet, "fetched-pkgs") { cleanupDependencies = true - cleanupMakeDependencies = bpmlib.BPMConfig.CleanupMakeDependencies + cleanupMakeDependencies = bpmlib.MainBPMConfig.CleanupMakeDependencies cleanupCompilationFiles = true cleanupCompiledPackages = true cleanupFetchedPackages = true diff --git a/src/bpmlib/compilation.go b/src/bpmlib/compilation.go index f8434e4..1e2d2e1 100644 --- a/src/bpmlib/compilation.go +++ b/src/bpmlib/compilation.go @@ -3,13 +3,14 @@ package bpmlib import ( "errors" "fmt" - "gopkg.in/yaml.v3" "os" "os/exec" "path" "strconv" "strings" "syscall" + + "gopkg.in/yaml.v3" ) var rootCompilationUID = "65534" @@ -136,7 +137,7 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo } else { env = append(env, "BPM_PKG_ARCH="+GetArch()) } - env = append(env, BPMConfig.CompilationEnvironment...) + env = append(env, CompilationBPMConfig.CompilationEnvironment...) // Execute prepare and build functions in source.sh script cmd := exec.Command("bash", "-c", diff --git a/src/bpmlib/config.go b/src/bpmlib/config.go index e5f33c8..34a4762 100644 --- a/src/bpmlib/config.go +++ b/src/bpmlib/config.go @@ -1,41 +1,61 @@ package bpmlib import ( - "gopkg.in/yaml.v3" "os" + + "gopkg.in/yaml.v3" ) -type BPMConfigStruct struct { +type MainBPMConfigStruct struct { IgnorePackages []string `yaml:"ignore_packages"` - PrivilegeEscalatorCmd string `yaml:"privilege_escalator_cmd"` - CompilationEnvironment []string `yaml:"compilation_env"` CleanupMakeDependencies bool `yaml:"cleanup_make_dependencies"` Databases []*BPMDatabase `yaml:"databases"` } -var BPMConfig BPMConfigStruct +type CompilationBPMConfigStruct struct { + PrivilegeEscalatorCmd string `yaml:"privilege_escalator_cmd"` + CompilationEnvironment []string `yaml:"compilation_env"` +} + +var MainBPMConfig MainBPMConfigStruct +var CompilationBPMConfig CompilationBPMConfigStruct func ReadConfig() (err error) { - if _, err = os.Stat("/etc/bpm.conf"); os.IsNotExist(err) { - return err - } + var file *os.File - bytes, err := os.ReadFile("/etc/bpm.conf") - if err != nil { - return err - } - - BPMConfig = BPMConfigStruct{ + // Set default config options + MainBPMConfig = MainBPMConfigStruct{ CleanupMakeDependencies: true, } - err = yaml.Unmarshal(bytes, &BPMConfig) + + // Read main BPM config + file, err = os.Open("/etc/bpm.conf") if err != nil { return err } + err = yaml.NewDecoder(file).Decode(&MainBPMConfig) + if err != nil { + return err + } + file.Close() - for i := len(BPMConfig.Databases) - 1; i >= 0; i-- { - if BPMConfig.Databases[i].Disabled != nil && *BPMConfig.Databases[i].Disabled { - BPMConfig.Databases = append(BPMConfig.Databases[:i], BPMConfig.Databases[i+1:]...) + // Read compilation BPM config + if _, err := os.Stat("/etc/bpm-compilation.conf"); err == nil { + file, err = os.Open("/etc/bpm-compilation.conf") + if err != nil { + return err + } + err = yaml.NewDecoder(file).Decode(&CompilationBPMConfig) + if err != nil { + return err + } + file.Close() + } + + // Remove disabled databases from memory + for i := len(MainBPMConfig.Databases) - 1; i >= 0; i-- { + if MainBPMConfig.Databases[i].Disabled != nil && *MainBPMConfig.Databases[i].Disabled { + MainBPMConfig.Databases = append(MainBPMConfig.Databases[:i], MainBPMConfig.Databases[i+1:]...) } } diff --git a/src/bpmlib/databases.go b/src/bpmlib/databases.go index 6b14195..4d574c6 100644 --- a/src/bpmlib/databases.go +++ b/src/bpmlib/databases.go @@ -3,13 +3,14 @@ package bpmlib import ( "errors" "fmt" - "gopkg.in/yaml.v3" "io" "net/http" "net/url" "os" "path" "strings" + + "gopkg.in/yaml.v3" ) type BPMDatabase struct { @@ -172,7 +173,7 @@ func (db *BPMDatabase) SyncLocalDatabaseFile() error { } func ReadLocalDatabaseFiles() (err error) { - for _, db := range BPMConfig.Databases { + for _, db := range MainBPMConfig.Databases { // Initialize struct values db.Entries = make(map[string]*BPMDatabaseEntry) db.VirtualPackages = make(map[string][]string) @@ -188,7 +189,7 @@ func ReadLocalDatabaseFiles() (err error) { } func GetDatabase(name string) *BPMDatabase { - for _, db := range BPMConfig.Databases { + for _, db := range MainBPMConfig.Databases { if db.Name == name { return db } @@ -203,7 +204,7 @@ func GetDatabaseEntry(str string) (*BPMDatabaseEntry, *BPMDatabase, error) { if pkgName == "" { return nil, nil, errors.New("could not find database entry for this package") } - for _, db := range BPMConfig.Databases { + for _, db := range MainBPMConfig.Databases { if db.ContainsPackage(pkgName) { return db.Entries[pkgName], db, nil } @@ -226,7 +227,7 @@ func GetDatabaseEntry(str string) (*BPMDatabaseEntry, *BPMDatabase, error) { } func FindReplacement(pkg string) *BPMDatabaseEntry { - for _, db := range BPMConfig.Databases { + for _, db := range MainBPMConfig.Databases { for _, entry := range db.Entries { for _, replaced := range entry.Info.Replaces { if replaced == pkg { @@ -240,7 +241,7 @@ func FindReplacement(pkg string) *BPMDatabaseEntry { } func ResolveVirtualPackage(vpkg string) *BPMDatabaseEntry { - for _, db := range BPMConfig.Databases { + for _, db := range MainBPMConfig.Databases { if v, ok := db.VirtualPackages[vpkg]; ok { for _, pkg := range v { return db.Entries[pkg] diff --git a/src/bpmlib/general.go b/src/bpmlib/general.go index b3ba3ad..d5e301a 100644 --- a/src/bpmlib/general.go +++ b/src/bpmlib/general.go @@ -362,7 +362,7 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci // Search for packages for _, pkg := range pkgs { - if slices.Contains(BPMConfig.IgnorePackages, pkg) { + if slices.Contains(MainBPMConfig.IgnorePackages, pkg) { continue } var entry *BPMDatabaseEntry @@ -407,7 +407,7 @@ func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependenci // SyncDatabase syncs all databases declared in /etc/bpm.conf func SyncDatabase(verbose bool) (err error) { - for _, db := range BPMConfig.Databases { + for _, db := range MainBPMConfig.Databases { if verbose { fmt.Printf("Fetching package database file for database (%s)...\n", db.Name) } From da184ecb19a4e7778715e1a4ce709164ec515ea0 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sat, 23 Aug 2025 13:16:30 +0300 Subject: [PATCH 87/88] Update Makefile --- Makefile | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 4ebcf61..2bfb0de 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Installation paths PREFIX ?= /usr/local -BINDIR ?= $(PREFIX)/bin +SBINDIR ?= $(PREFIX)/sbin SYSCONFDIR := $(PREFIX)/etc # Compilers and tools @@ -15,19 +15,26 @@ 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 install: build/bpm config/ - # Create directories - install -dm755 $(DESTDIR)$(BINDIR) + # Create directory + install -dm755 $(DESTDIR)$(SBINDIR) + + # Install binary + install -Dm755 build/bpm $(DESTDIR)$(SBINDIR)/bpm + +install-config: + # Create directory install -dm755 $(DESTDIR)$(SYSCONFDIR) + # Install files - install -Dm755 build/bpm $(DESTDIR)$(BINDIR)/bpm install -Dm644 config/bpm.conf $(DESTDIR)$(SYSCONFDIR)/bpm.conf install -Dm644 config/bpm-compilation.conf $(DESTDIR)$(SYSCONFDIR)/bpm-compilation.conf uninstall: - rm $(DESTDIR)$(BINDIR)/bpm - rm $(DESTDIR)$(SYSCONFDIR)/bpm.conf + -rm -f $(DESTDIR)$(SBINDIR)/bpm + -rm -f $(DESTDIR)$(SYSCONFDIR)/bpm.conf + -rm -f $(DESTDIR)$(SYSCONFDIR)/bpm-compilation.conf clean: rm -r build/ -.PHONY: build +.PHONY: build install install-config uninstall clean From 27713e468c88946588c05cab7db86c7c0268a1d8 Mon Sep 17 00:00:00 2001 From: EnumDev Date: Sat, 23 Aug 2025 13:30:16 +0300 Subject: [PATCH 88/88] Update README.md --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5813e5b..7fa9a95 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,12 @@ # Bubble Package Manager (BPM) -## _A simple package manager_ - -BPM is a simple package manager for Linux systems +## _The official package manager for Tide Linux_ ## Features - Simple to use subcommands - Can install both binary and source packages ## Information -BPM is still in very early development. Do not install it without knowing what you are doing. I would only recommend using it in a Virtual Machine for testing +BPM is still in very early development. There may be bugs that could completely break installations. For now it is recommended you use this only in Virtual Machines, containers or in a Tide Linux installation ## Build from source @@ -21,6 +19,7 @@ make - Run the following command to install BPM into your system. You may also append a DESTDIR variable at the end of this line if you wish to install in a different location ```sh make install PREFIX=/usr SYSCONFDIR=/etc +make install-config PREFIX=/usr SYSCONFDIR=/etc ``` ## How to use @@ -61,6 +60,6 @@ bpm help ## Package Creation -Package creation is simplified using the bpm-utils package which contains helper scripts for creating and archiving packages +Package creation is simplified using the bpm-utils package which contains helper scripts for creating packages Learn more here: https://git.enumerated.dev/bubble-package-manager/bpm-utils