mirror of
https://github.com/EnumeratedDev/bpm.git
synced 2026-09-26 15:36:13 +00:00
Compare commits
16
Commits
a7c03c272e
..
0.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45c536b326
|
||
|
|
705aec8e6e
|
||
|
|
34fb7ce1d6
|
||
|
|
fee0e6a96f
|
||
|
|
c5f48becf8
|
||
|
|
ac1ba2ada3
|
||
|
|
5fb8211933
|
||
|
|
4110ad9c5c
|
||
|
|
dcff59d560
|
||
|
|
1594eec5ce
|
||
|
|
0c30d85589
|
||
|
|
768c75c9bd
|
||
|
|
abc6674b97
|
||
|
|
1d858076fd
|
||
|
|
5909370407
|
||
|
|
5a78fd3002
|
@@ -12,7 +12,7 @@ ROOT_COMPILATION_GID ?= 65534
|
||||
|
||||
build:
|
||||
mkdir -p build
|
||||
cd src/bpm; $(GO) build -ldflags "-w -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationUID=$(ROOT_COMPILATION_UID)' -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationGID=$(ROOT_COMPILATION_GID)'" -o ../../build/bpm git.enumerated.dev/bubble-package-manager/bpm/src/bpm
|
||||
cd src/bpm; $(GO) build $(GOFLAGS) -ldflags "-w -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationUID=$(ROOT_COMPILATION_UID)' -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationGID=$(ROOT_COMPILATION_GID)'" -o ../../build/bpm git.enumerated.dev/bubble-package-manager/bpm/src/bpm
|
||||
|
||||
install: build/bpm config/
|
||||
# Create directory
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
ignore_packages: []
|
||||
show_source_package_contents: always
|
||||
cleanup_make_dependencies: true
|
||||
databases:
|
||||
- name: example-database
|
||||
|
||||
+163
-59
@@ -92,8 +92,7 @@ func main() {
|
||||
currentFlagSet.BoolP("verbose", "v", false, "Show additional information about the current operation")
|
||||
currentFlagSet.BoolP("force", "f", false, "Bypass warnings during package removal")
|
||||
currentFlagSet.BoolP("yes", "y", false, "Enter 'yes' in all prompts")
|
||||
currentFlagSet.BoolP("unused", "u", false, "Remove packages only if they are not required as dependencies")
|
||||
currentFlagSet.BoolP("cleanup", "c", false, "Additionally remove all unused dependencies")
|
||||
currentFlagSet.BoolP("cleanup", "n", false, "Additionally remove all unused dependencies")
|
||||
setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Remove the specified packages", os.Args[2:])
|
||||
|
||||
removePackages()
|
||||
@@ -103,6 +102,8 @@ func main() {
|
||||
currentFlagSet.StringP("root", "R", "/", "Operate on specified root directory")
|
||||
currentFlagSet.BoolP("verbose", "v", false, "Show additional information about the current operation")
|
||||
currentFlagSet.BoolP("force", "f", false, "Bypass warnings during package cleanup")
|
||||
currentFlagSet.BoolP("yes", "y", false, "Enter 'yes' in all prompts")
|
||||
currentFlagSet.BoolP("all", "a", false, "Perform all types of cleanup")
|
||||
currentFlagSet.BoolP("depends", "d", false, "Perform a dependency cleanup")
|
||||
currentFlagSet.BoolP("make-depends", "m", false, "Perform a make dependency cleanup")
|
||||
currentFlagSet.BoolP("compilation-files", "c", false, "Perform a cleanup of compilation files")
|
||||
@@ -431,21 +432,45 @@ func installPackages() {
|
||||
|
||||
// Confirmation Prompt
|
||||
if !yesAll {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
if len(operation.Actions) == 1 {
|
||||
fmt.Printf("Do you wish to install this package? [y\\N] ")
|
||||
} else {
|
||||
fmt.Printf("Do you wish to install these %d packages? [y\\N] ", len(operation.Actions))
|
||||
prompt := "Do you wish to install this package?"
|
||||
if len(operation.Actions) != 1 {
|
||||
prompt = fmt.Sprintf("Do you wish to install all %d packages?", len(operation.Actions))
|
||||
}
|
||||
|
||||
text, _ := reader.ReadString('\n')
|
||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
||||
if !showConfirmationPrompt(prompt, false) {
|
||||
fmt.Println("Cancelling package installation...")
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch packages
|
||||
err = operation.FetchPackages()
|
||||
if err != nil {
|
||||
log.Printf("Error: could not fetch packages for operation: %s\n", err)
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
if bpmlib.MainBPMConfig.ShowSourcePackageContents == "always" || bpmlib.MainBPMConfig.ShowSourcePackageContents == "install-only" {
|
||||
// Show source package contents
|
||||
sourcePackagesShown, err := operation.ShowSourcePackageContent()
|
||||
if err != nil {
|
||||
log.Printf("Error: could not show source package content: %s\n", err)
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
// Confirmation Prompt
|
||||
if sourcePackagesShown > 0 && !yesAll {
|
||||
if !showConfirmationPrompt("Do you wish to continue?", false) {
|
||||
fmt.Println("Cancelling package installation...")
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute operation
|
||||
err = operation.Execute(verbose, force)
|
||||
if err != nil {
|
||||
@@ -470,15 +495,10 @@ func removePackages() {
|
||||
verbose, _ := currentFlagSet.GetBool("verbose")
|
||||
force, _ := currentFlagSet.GetBool("force")
|
||||
yesAll, _ := currentFlagSet.GetBool("yes")
|
||||
removeUnused, _ := currentFlagSet.GetBool("unused")
|
||||
cleanupPackages, _ := currentFlagSet.GetBool("cleanup")
|
||||
|
||||
// Get packages
|
||||
packages := currentFlagSet.Args()
|
||||
if len(packages) == 0 {
|
||||
fmt.Println("No packages were given to remove")
|
||||
return
|
||||
}
|
||||
|
||||
// Check for required permissions
|
||||
if os.Getuid() != 0 {
|
||||
@@ -505,8 +525,16 @@ func removePackages() {
|
||||
}
|
||||
|
||||
// Create remove operation
|
||||
operation, err := bpmlib.RemovePackages(rootDir, removeUnused, cleanupPackages, packages...)
|
||||
operation, err := bpmlib.RemovePackages(rootDir, force, cleanupPackages, packages...)
|
||||
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
|
||||
log.Printf("Error: %s", err)
|
||||
exitCode = 1
|
||||
return
|
||||
} else if errors.As(err, &bpmlib.PackageRemovalDependencyErr{}) {
|
||||
for pkg, dependants := range err.(bpmlib.PackageRemovalDependencyErr).RequiredPackages {
|
||||
fmt.Printf("The following packages depend on package (%s): %s\n", pkg, strings.Join(dependants, ", "))
|
||||
}
|
||||
|
||||
log.Printf("Error: %s", err)
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -527,10 +555,12 @@ func removePackages() {
|
||||
|
||||
// Confirmation Prompt
|
||||
if !yesAll {
|
||||
fmt.Printf("Are you sure you wish to remove all %d packages? [y\\N] ", len(operation.Actions))
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
text, _ := reader.ReadString('\n')
|
||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
||||
prompt := "Do you wish to remove this package?"
|
||||
if len(operation.Actions) != 1 {
|
||||
prompt = fmt.Sprintf("Do you wish to remove all %d packages?", len(operation.Actions))
|
||||
}
|
||||
|
||||
if !showConfirmationPrompt(prompt, false) {
|
||||
fmt.Println("Cancelling package removal...")
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -561,6 +591,7 @@ func doCleanup() {
|
||||
verbose, _ := currentFlagSet.GetBool("verbose")
|
||||
force, _ := currentFlagSet.GetBool("force")
|
||||
yesAll, _ := currentFlagSet.GetBool("yes")
|
||||
all, _ := currentFlagSet.GetBool("all")
|
||||
cleanupDepends, _ := currentFlagSet.GetBool("depends")
|
||||
cleanupMakeDepends, _ := currentFlagSet.GetBool("make-depends")
|
||||
cleanupCompilationFiles, _ := currentFlagSet.GetBool("compilation-files")
|
||||
@@ -568,7 +599,13 @@ func doCleanup() {
|
||||
cleanupFetchedPackages, _ := currentFlagSet.GetBool("fetched-packages")
|
||||
|
||||
// Set default behaviour
|
||||
if !isFlagSet(currentFlagSet, "depends") && !isFlagSet(currentFlagSet, "make-depends") && !isFlagSet(currentFlagSet, "compilation-files") && !isFlagSet(currentFlagSet, "binary-packages") && !isFlagSet(currentFlagSet, "fetched-packages") {
|
||||
if all {
|
||||
cleanupDepends = true
|
||||
cleanupMakeDepends = bpmlib.MainBPMConfig.CleanupMakeDependencies
|
||||
cleanupCompilationFiles = true
|
||||
cleanupBinaryPackages = true
|
||||
cleanupFetchedPackages = true
|
||||
} else if !isFlagSet(currentFlagSet, "depends") && !isFlagSet(currentFlagSet, "make-depends") && !isFlagSet(currentFlagSet, "compilation-files") && !isFlagSet(currentFlagSet, "binary-packages") && !isFlagSet(currentFlagSet, "fetched-packages") {
|
||||
cleanupDepends = true
|
||||
cleanupMakeDepends = bpmlib.MainBPMConfig.CleanupMakeDependencies
|
||||
cleanupCompilationFiles = false
|
||||
@@ -631,10 +668,12 @@ func doCleanup() {
|
||||
|
||||
// Confirmation Prompt
|
||||
if !yesAll {
|
||||
fmt.Printf("Are you sure you wish to remove all %d packages? [y\\N] ", len(operation.Actions))
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
text, _ := reader.ReadString('\n')
|
||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
||||
prompt := "Do you wish to remove this package?"
|
||||
if len(operation.Actions) != 1 {
|
||||
prompt = fmt.Sprintf("Do you wish to remove all %d packages?", len(operation.Actions))
|
||||
}
|
||||
|
||||
if !showConfirmationPrompt(prompt, false) {
|
||||
fmt.Println("Cancelling package removal...")
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -684,10 +723,7 @@ func syncDatabases() {
|
||||
|
||||
// Confirmation Prompt
|
||||
if !yesAll {
|
||||
fmt.Printf("Are you sure you wish to sync all databases? [y\\N] ")
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
text, _ := reader.ReadString('\n')
|
||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
||||
if !showConfirmationPrompt("Do you wish to sync all databases?", false) {
|
||||
fmt.Println("Cancelling database synchronization...")
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -761,16 +797,45 @@ func updatePackages() {
|
||||
|
||||
// Confirmation Prompt
|
||||
if !yesAll {
|
||||
fmt.Printf("Are you sure you wish to update all %d packages? [y\\N] ", len(operation.Actions))
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
text, _ := reader.ReadString('\n')
|
||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
||||
prompt := "Do you wish to update this package?"
|
||||
if len(operation.Actions) != 1 {
|
||||
prompt = fmt.Sprintf("Do you wish to update all %d packages?", len(operation.Actions))
|
||||
}
|
||||
|
||||
if !showConfirmationPrompt(prompt, false) {
|
||||
fmt.Println("Cancelling package update...")
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch packages
|
||||
err = operation.FetchPackages()
|
||||
if err != nil {
|
||||
log.Printf("Error: could not fetch packages for operation: %s\n", err)
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
if bpmlib.MainBPMConfig.ShowSourcePackageContents == "always" {
|
||||
// Show source package contents
|
||||
sourcePackagesShown, err := operation.ShowSourcePackageContent()
|
||||
if err != nil {
|
||||
log.Printf("Error: could not show source package content: %s\n", err)
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
// Confirmation Prompt
|
||||
if sourcePackagesShown > 0 && !yesAll {
|
||||
if !showConfirmationPrompt("Do you wish to continue?", false) {
|
||||
fmt.Println("Cancelling package installation...")
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute operation
|
||||
err = operation.Execute(verbose, force)
|
||||
if err != nil {
|
||||
@@ -982,9 +1047,39 @@ func compilePackage() {
|
||||
}
|
||||
}
|
||||
|
||||
// Setup cleanup function
|
||||
cleanupFunc := func() {
|
||||
if installSrcPkgDepends && len(unmetDepends) > 0 {
|
||||
// Get path to current executable
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Printf("Warning: could not get path to executable: %s\n", err)
|
||||
}
|
||||
|
||||
// Run 'bpm cleanup' using the set privilege escalator command
|
||||
cmd := exec.Command(bpmlib.CompilationBPMConfig.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.Printf("Warning: dependency cleanup command failed: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get current working directory
|
||||
workdir, err := os.Getwd()
|
||||
if err != nil {
|
||||
// Remove unused packages
|
||||
cleanupFunc()
|
||||
|
||||
log.Printf("Error: could not get working directory: %s", err)
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -993,6 +1088,9 @@ func compilePackage() {
|
||||
// Get user home directory
|
||||
homedir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
// Remove unused packages
|
||||
cleanupFunc()
|
||||
|
||||
log.Printf("Error: could not get user home directory: %s", err)
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -1025,11 +1123,17 @@ func compilePackage() {
|
||||
// Ensure output directory exists and is a directory
|
||||
stat, err := os.Stat(outputDirectory)
|
||||
if err != nil {
|
||||
// Remove unused packages
|
||||
cleanupFunc()
|
||||
|
||||
log.Printf("Error: could not stat output directory (%s): %s", outputDirectory, err)
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
if !stat.IsDir() {
|
||||
// Remove unused packages
|
||||
cleanupFunc()
|
||||
|
||||
log.Printf("Error: output directory (%s) is not a directory", outputDirectory)
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -1037,6 +1141,9 @@ func compilePackage() {
|
||||
|
||||
outputBpmPackages, err := bpmlib.CompileSourcePackage(sourcePackage, outputDirectory, skipChecks)
|
||||
if err != nil {
|
||||
// Remove unused packages
|
||||
cleanupFunc()
|
||||
|
||||
log.Printf("Error: could not compile source package (%s): %s", sourcePackage, err)
|
||||
exitCode = 1
|
||||
return
|
||||
@@ -1057,33 +1164,7 @@ func compilePackage() {
|
||||
}
|
||||
|
||||
// Remove unused packages
|
||||
if installSrcPkgDepends && len(unmetDepends) > 0 {
|
||||
// Get path to current executable
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Printf("Error: could not get path to executable: %s\n", err)
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
|
||||
// Run 'bpm cleanup' using the set privilege escalator command
|
||||
cmd := exec.Command(bpmlib.CompilationBPMConfig.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.Printf("Error: dependency cleanup command failed: %s\n", err)
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
}
|
||||
cleanupFunc()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1125,3 +1206,26 @@ func isFlagSet(flagSet *flag.FlagSet, name string) bool {
|
||||
})
|
||||
return found
|
||||
}
|
||||
|
||||
func showConfirmationPrompt(prompt string, defaultTo bool) bool {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
if defaultTo {
|
||||
fmt.Printf("%s [Y/n] ", prompt)
|
||||
} else {
|
||||
fmt.Printf("%s [y/N] ", prompt)
|
||||
}
|
||||
|
||||
text, _ := reader.ReadString('\n')
|
||||
text = strings.TrimSpace(text)
|
||||
|
||||
if len(text) > 0 {
|
||||
switch text[0] {
|
||||
case 'y', 'Y':
|
||||
return true
|
||||
case 'n', 'N':
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return defaultTo
|
||||
}
|
||||
|
||||
+96
-23
@@ -132,7 +132,6 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
|
||||
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))
|
||||
@@ -188,6 +187,21 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
|
||||
packagesToCompile = append(packagesToCompile, bpmpkg.PkgInfo)
|
||||
}
|
||||
|
||||
// Create output directories for each package
|
||||
for _, pkg := range packagesToCompile {
|
||||
// Create new output directory
|
||||
err = os.Mkdir(path.Join(tempDirectory, "output_"+pkg.Name), 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Change output directory owner
|
||||
err = os.Chown(path.Join(tempDirectory, "output_"+pkg.Name), uid, gid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Compile each package
|
||||
for _, pkg := range packagesToCompile {
|
||||
// Get package function name
|
||||
@@ -196,26 +210,6 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
|
||||
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 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
|
||||
@@ -224,10 +218,11 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
|
||||
"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.Env = append(env, "BPM_OUTPUT="+path.Join(tempDirectory, "output_"+pkg.Name))
|
||||
if os.Getuid() == 0 {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
|
||||
@@ -238,7 +233,7 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
|
||||
}
|
||||
|
||||
// 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 = exec.Command("bash", "-c", fmt.Sprintf("find %s -printf \"%%P\\n\" | fakeroot -i %s/fakeroot_file tar -czf files.tar.gz --no-recursion -C %s -T -", "output_"+pkg.Name, tempDirectory, "output_"+pkg.Name))
|
||||
cmd.Dir = tempDirectory
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
@@ -330,6 +325,12 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Remove output directory
|
||||
err = os.RemoveAll(path.Join(tempDirectory, "output_"+pkg.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputBpmPackages[pkgInfo.Name] = outputFilename
|
||||
}
|
||||
|
||||
@@ -515,3 +516,75 @@ func downloadPackageFiles(pkgInfo *PackageInfo, tempDirectory string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func showPackageFiles(archiveFilename string) error {
|
||||
// Read BPM archive
|
||||
bpmpkg, err := ReadPackage(archiveFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure package type is 'source'
|
||||
if bpmpkg.PkgInfo.Type != "source" {
|
||||
return fmt.Errorf("cannot compile a non-source package")
|
||||
}
|
||||
|
||||
printTarballContent := func(filename string) error {
|
||||
// Get tarball reader for file
|
||||
reader, err := readTarballFile(archiveFilename, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read file content
|
||||
data, err := io.ReadAll(reader.tarReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Print file name and content
|
||||
fmt.Println("==============================")
|
||||
fmt.Printf("%s: %s\n", bpmpkg.PkgInfo.Name, filename)
|
||||
fmt.Println("==============================")
|
||||
fmt.Printf("%s\n", data)
|
||||
|
||||
// Close reader
|
||||
err = reader.file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Print pkg.info content
|
||||
err = printTarballContent("pkg.info")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Print source files contents
|
||||
files, err := listTarballContent(archiveFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range files {
|
||||
if !strings.HasPrefix(file, "source-files/") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Print source file content
|
||||
err = printTarballContent(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Print source.sh content
|
||||
err = printTarballContent("source.sh")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
type MainBPMConfigStruct struct {
|
||||
IgnorePackages []string `yaml:"ignore_packages"`
|
||||
ShowSourcePackageContents string `yaml:"show_source_package_contents"`
|
||||
CleanupMakeDependencies bool `yaml:"cleanup_make_dependencies"`
|
||||
Databases []configDatabase `yaml:"databases"`
|
||||
}
|
||||
@@ -31,6 +32,7 @@ func ReadConfig() (err error) {
|
||||
|
||||
// Set default config options
|
||||
MainBPMConfig = MainBPMConfigStruct{
|
||||
ShowSourcePackageContents: "always",
|
||||
CleanupMakeDependencies: true,
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -217,9 +218,8 @@ func FindReplacement(pkg string) *BPMDatabaseEntry {
|
||||
func ResolveVirtualPackage(vpkg string) *BPMDatabaseEntry {
|
||||
for _, db := range BPMDatabases {
|
||||
if v, ok := db.VirtualPackages[vpkg]; ok {
|
||||
for _, pkg := range v {
|
||||
return db.Entries[pkg]
|
||||
}
|
||||
slices.Sort(v)
|
||||
return db.Entries[v[0]]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-14
@@ -31,7 +31,7 @@ func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeOptionalD
|
||||
}) {
|
||||
allDepends = append(allDepends, pkgInstallationReason{
|
||||
PkgName: depend,
|
||||
InstallationReason: InstallationReasonDependency,
|
||||
InstallationReason: InstallationReasonManual,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -51,23 +51,23 @@ func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeOptionalD
|
||||
return allDepends
|
||||
}
|
||||
|
||||
func (pkgInfo *PackageInfo) GetAllDependencies(includeMakeDepends, includeOptionalDepends bool, rootDir string) (resolved []string) {
|
||||
func (pkgInfo *PackageInfo) GetDependenciesRecursive(includeMakeDepends 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)
|
||||
pkgInfo.getDependenciesRecursive(&resolved, &unresolved, includeMakeDepends, rootDir)
|
||||
|
||||
return resolved
|
||||
}
|
||||
|
||||
func (pkgInfo *PackageInfo) getAllDependencies(resolved *[]string, unresolved *[]string, includeMakeDepends, includeOptionalDepends bool, rootDir string) {
|
||||
func (pkgInfo *PackageInfo) getDependenciesRecursive(resolved *[]string, unresolved *[]string, includeMakeDepends bool, rootDir string) {
|
||||
// Add current package name to unresolved slice
|
||||
*unresolved = append(*unresolved, pkgInfo.Name)
|
||||
|
||||
// Loop through all dependencies
|
||||
for _, pkgIR := range pkgInfo.GetDependencies(includeMakeDepends, includeOptionalDepends) {
|
||||
for _, pkgIR := range pkgInfo.GetDependencies(includeMakeDepends, false) {
|
||||
depend := pkgIR.PkgName
|
||||
|
||||
if isVirtual, p := IsVirtualPackage(depend, rootDir); isVirtual {
|
||||
@@ -86,7 +86,7 @@ func (pkgInfo *PackageInfo) getAllDependencies(resolved *[]string, unresolved *[
|
||||
dependInfo := GetPackageInfo(depend, rootDir)
|
||||
|
||||
if dependInfo != nil {
|
||||
dependInfo.getAllDependencies(resolved, unresolved, includeMakeDepends, includeOptionalDepends, rootDir)
|
||||
dependInfo.getDependenciesRecursive(resolved, unresolved, includeMakeDepends, rootDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,7 +155,7 @@ func resolvePackageDependenciesFromDatabase(resolved *[]pkgInstallationReason, u
|
||||
}
|
||||
|
||||
// Resolve the dependencies of this dependency
|
||||
resolvePackageDependenciesFromDatabase(resolved, unresolved, entry.Info, checkMake, checkOptional, ignoreInstalled, verbose, rootDir)
|
||||
resolvePackageDependenciesFromDatabase(resolved, unresolved, entry.Info, checkMake, false, ignoreInstalled, verbose, rootDir)
|
||||
|
||||
// Move dependency from the unresolved slice to the resolved slice
|
||||
if !slices.ContainsFunc(*resolved, func(p pkgInstallationReason) bool {
|
||||
@@ -198,12 +198,9 @@ func GetPackageDependants(pkgName string, rootDir string) ([]string, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get installed package dependencies
|
||||
dependencies := installedPkg.PkgInfo.GetDependencies(false, true)
|
||||
|
||||
// Add installed package to list if its dependencies include pkgName
|
||||
if slices.ContainsFunc(dependencies, func(p pkgInstallationReason) bool {
|
||||
return p.PkgName == pkgName
|
||||
if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool {
|
||||
return n == pkgName
|
||||
}) {
|
||||
ret = append(ret, installedPkgName)
|
||||
continue
|
||||
@@ -212,8 +209,8 @@ func GetPackageDependants(pkgName string, rootDir string) ([]string, error) {
|
||||
// Loop through each virtual package
|
||||
for _, vpkg := range pkg.PkgInfo.Provides {
|
||||
// Add installed package to list if its dependencies contain a provided virtual package
|
||||
if slices.ContainsFunc(dependencies, func(p pkgInstallationReason) bool {
|
||||
return p.PkgName == vpkg
|
||||
if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool {
|
||||
return n == vpkg
|
||||
}) {
|
||||
ret = append(ret, installedPkgName)
|
||||
break
|
||||
|
||||
@@ -40,3 +40,11 @@ type PackageScriptErr struct {
|
||||
func (e PackageScriptErr) Error() string {
|
||||
return fmt.Sprintf("could not execute package script (%s) for package (%s): %s", e.packageScript, e.packageName, e.err)
|
||||
}
|
||||
|
||||
type PackageRemovalDependencyErr struct {
|
||||
RequiredPackages map[string][]string
|
||||
}
|
||||
|
||||
func (e PackageRemovalDependencyErr) Error() string {
|
||||
return "removing these package would break other installed packages"
|
||||
}
|
||||
|
||||
+40
-10
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"maps"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
@@ -186,7 +187,7 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
|
||||
}
|
||||
|
||||
// RemovePackages removes the specified packages from the given root directory
|
||||
func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencies bool, packages ...string) (operation *BPMOperation, err error) {
|
||||
func RemovePackages(rootDir string, force, cleanupDependencies bool, packages ...string) (operation *BPMOperation, err error) {
|
||||
operation = &BPMOperation{
|
||||
Actions: make([]OperationAction, 0),
|
||||
UnresolvedDepends: make([]string, 0),
|
||||
@@ -198,27 +199,56 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie
|
||||
// Search for packages
|
||||
for _, pkg := range packages {
|
||||
bpmpkg := GetPackage(pkg, rootDir)
|
||||
if isVirutal, vpkg := IsVirtualPackage(pkg, rootDir); isVirutal {
|
||||
bpmpkg = GetPackage(vpkg, rootDir)
|
||||
}
|
||||
if bpmpkg == nil {
|
||||
continue
|
||||
}
|
||||
operation.AppendAction(&RemovePackageAction{BpmPackage: bpmpkg})
|
||||
}
|
||||
|
||||
// Do not remove packages which other packages depend on
|
||||
if removeUnusedPackagesOnly {
|
||||
err := operation.RemoveNeededPackages()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not skip needed packages: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Do package cleanup
|
||||
if cleanupDependencies {
|
||||
err := operation.Cleanup(true)
|
||||
err := operation.Cleanup(MainBPMConfig.CleanupMakeDependencies)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not perform cleanup for operation: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Return error if other packages depend on removed ones
|
||||
if !force {
|
||||
// Get packages and their dependants
|
||||
packageDepndants := make(map[string][]string, 0)
|
||||
for _, action := range operation.Actions {
|
||||
dependants, err := GetPackageDependants(action.(*RemovePackageAction).BpmPackage.PkgInfo.Name, rootDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get package dependants: %s", err)
|
||||
}
|
||||
|
||||
packageDepndants[action.(*RemovePackageAction).BpmPackage.PkgInfo.Name] = dependants
|
||||
}
|
||||
|
||||
// Remove dependant packages from map if they are to be removed by this operation
|
||||
for pkg, required := range packageDepndants {
|
||||
required = slices.DeleteFunc(required, func(pkgName string) bool {
|
||||
_, ok := packageDepndants[pkgName]
|
||||
return ok
|
||||
})
|
||||
packageDepndants[pkg] = required
|
||||
}
|
||||
|
||||
// Remove empty keys from map
|
||||
maps.DeleteFunc(packageDepndants, func(pkg string, required []string) bool {
|
||||
return len(required) == 0
|
||||
})
|
||||
|
||||
// Return error
|
||||
if len(packageDepndants) != 0 {
|
||||
return nil, PackageRemovalDependencyErr{RequiredPackages: packageDepndants}
|
||||
}
|
||||
}
|
||||
|
||||
return operation, nil
|
||||
}
|
||||
|
||||
|
||||
+63
-17
@@ -8,6 +8,7 @@ import (
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
type BPMOperation struct {
|
||||
@@ -15,7 +16,9 @@ type BPMOperation struct {
|
||||
UnresolvedDepends []string
|
||||
Changes map[string]string
|
||||
RootDir string
|
||||
|
||||
compiledPackages map[string]string
|
||||
hasFetchedPackages bool
|
||||
}
|
||||
|
||||
func (operation *BPMOperation) ActionsContainPackage(pkg string) bool {
|
||||
@@ -227,7 +230,7 @@ func (operation *BPMOperation) Cleanup(cleanupMakeDepends bool) error {
|
||||
}
|
||||
|
||||
keepPackages = append(keepPackages, pkg.Name)
|
||||
resolved := pkg.GetAllDependencies(!cleanupMakeDepends, true, operation.RootDir)
|
||||
resolved := pkg.GetDependenciesRecursive(!cleanupMakeDepends, operation.RootDir)
|
||||
for _, value := range resolved {
|
||||
if !slices.Contains(keepPackages, value) {
|
||||
keepPackages = append(keepPackages, value)
|
||||
@@ -333,6 +336,9 @@ func (operation *BPMOperation) ShowOperationSummary() {
|
||||
return
|
||||
}
|
||||
|
||||
writer := tabwriter.NewWriter(os.Stdout, 6, 4, 6, ' ', 0)
|
||||
fmt.Fprintln(writer, "Name\tVersion\tAction\tInstallation Reason\tFrom Source")
|
||||
|
||||
for _, value := range operation.Actions {
|
||||
var pkgInfo *PackageInfo
|
||||
var installationReason = InstallationReasonUnknown
|
||||
@@ -347,41 +353,40 @@ func (operation *BPMOperation) ShowOperationSummary() {
|
||||
pkgInfo = value.(*FetchPackageAction).DatabaseEntry.Info
|
||||
} else {
|
||||
pkgInfo = value.(*RemovePackageAction).BpmPackage.PkgInfo
|
||||
fmt.Printf("%s: %s (Remove)\n", pkgInfo.Name, pkgInfo.GetFullVersion())
|
||||
fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), "Remove", "-", "-")
|
||||
continue
|
||||
}
|
||||
|
||||
installedInfo := GetPackageInfo(pkgInfo.Name, operation.RootDir)
|
||||
additionalInfo := ""
|
||||
installationReasonStr := ""
|
||||
switch installationReason {
|
||||
case InstallationReasonManual:
|
||||
additionalInfo = "(Manual)"
|
||||
installationReasonStr = "Manual"
|
||||
case InstallationReasonDependency:
|
||||
additionalInfo = "(Dependency)"
|
||||
installationReasonStr = "Dependency"
|
||||
case InstallationReasonMakeDependency:
|
||||
additionalInfo = "(Make dependency)"
|
||||
installationReasonStr = "Make dependency"
|
||||
default:
|
||||
additionalInfo = "(Unknown)"
|
||||
}
|
||||
|
||||
if pkgInfo.Type == "source" {
|
||||
additionalInfo += " (From Source)"
|
||||
installationReasonStr = "Unknown"
|
||||
}
|
||||
|
||||
installedInfo := GetPackageInfo(pkgInfo.Name, operation.RootDir)
|
||||
if installedInfo == nil {
|
||||
fmt.Printf("%s: %s (Install) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), additionalInfo)
|
||||
fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%t\n", pkgInfo.Name, pkgInfo.GetFullVersion(), "Install", installationReasonStr, pkgInfo.Type == "source")
|
||||
} else {
|
||||
comparison := ComparePackageVersions(*pkgInfo, *installedInfo)
|
||||
if comparison < 0 {
|
||||
fmt.Printf("%s: %s -> %s (Downgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), additionalInfo)
|
||||
fmt.Fprintf(writer, "%s\t%s -> %s\t%s\t%s\t%t\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), "Downgrade", installationReasonStr, pkgInfo.Type == "source")
|
||||
} else if comparison > 0 {
|
||||
fmt.Printf("%s: %s -> %s (Upgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), additionalInfo)
|
||||
fmt.Fprintf(writer, "%s\t%s -> %s\t%s\t%s\t%t\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), "Upgrade", installationReasonStr, pkgInfo.Type == "source")
|
||||
} else {
|
||||
fmt.Printf("%s: %s (Reinstall) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), additionalInfo)
|
||||
fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%t\n", pkgInfo.Name, pkgInfo.GetFullVersion(), "Reinstall", installationReasonStr, pkgInfo.Type == "source")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.Flush()
|
||||
fmt.Println()
|
||||
|
||||
if operation.RootDir != "/" {
|
||||
fmt.Println("Warning: Operating in " + operation.RootDir)
|
||||
}
|
||||
@@ -395,6 +400,34 @@ func (operation *BPMOperation) ShowOperationSummary() {
|
||||
}
|
||||
}
|
||||
|
||||
func (operation *BPMOperation) ShowSourcePackageContent() (sourcePackagesShown int, err error) {
|
||||
// Fetch packages
|
||||
if !operation.hasFetchedPackages {
|
||||
err = operation.FetchPackages()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, action := range operation.Actions {
|
||||
if action.GetActionType() != "install" {
|
||||
continue
|
||||
}
|
||||
if action.(*InstallPackageAction).BpmPackage.PkgInfo.Type != "source" {
|
||||
continue
|
||||
}
|
||||
|
||||
err = showPackageFiles(action.(*InstallPackageAction).File)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
sourcePackagesShown++
|
||||
}
|
||||
|
||||
return sourcePackagesShown, nil
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -426,7 +459,7 @@ func (operation *BPMOperation) RunHooks(verbose bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
|
||||
func (operation *BPMOperation) FetchPackages() (err error) {
|
||||
// Fetch packages from databases
|
||||
if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool {
|
||||
return action.GetActionType() == "fetch"
|
||||
@@ -492,6 +525,19 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
operation.hasFetchedPackages = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
|
||||
// Fetch packages
|
||||
if !operation.hasFetchedPackages {
|
||||
err = operation.FetchPackages()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Determine words to be used for the following message
|
||||
words := make([]string, 0)
|
||||
if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool {
|
||||
|
||||
+12
-1
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"regexp"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -461,11 +462,21 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure package name is valid
|
||||
if match, _ := regexp.MatchString("^[a-zA-Z0-9._-]+$", pkgInfo.Name); !match {
|
||||
return nil, fmt.Errorf("package name (%s) is invalid", pkgInfo.Name)
|
||||
}
|
||||
|
||||
// Setup split package information
|
||||
for i, splitPkg := range pkgInfo.SplitPackages {
|
||||
// Ensure split package contains a name
|
||||
if splitPkg.Name == "" {
|
||||
return nil, fmt.Errorf("invalid split package name: %s", splitPkg.Name)
|
||||
return nil, fmt.Errorf("package name (%s) is invalid", splitPkg.Name)
|
||||
}
|
||||
|
||||
// Ensure split package name is valid
|
||||
if match, _ := regexp.MatchString("^[a-zA-Z0-9._-]+$", splitPkg.Name); !match {
|
||||
return nil, fmt.Errorf("package name (%s) is invalid", splitPkg.Name)
|
||||
}
|
||||
|
||||
// Turn split package into yaml data
|
||||
|
||||
Reference in New Issue
Block a user