- Added a -b flag to the install subcommand that turns a source package into a binary one after compilation

- Reformatted the help subcommand
- Updated bpm-utils to add the ability to create source packages
- Source temporary directories will now be removed after installation unless the user passes the -k flag
- BPM will now not remove the old version of a package before installing an update which could cause libraries or programs required for installation to be missing. Obsolete files will now be removed after installation
- The version, list, info and help subcommands can now be run by any user without root permissions. Root permissions are still required for package installation and removal
- Removed installed package info fixing for now. It will be replaced by a better system in the future
This commit is contained in:
CapCreeperGR 2024-03-30 22:22:31 +02:00
parent 43c4a626f1
commit a9283037d8
9 changed files with 184 additions and 31 deletions

View File

@ -130,10 +130,16 @@ func ReadPackageInfo(contents string, defaultValues bool) (*PackageInfo, error)
split[1] = strings.Trim(split[1], " ") split[1] = strings.Trim(split[1], " ")
switch split[0] { switch split[0] {
case "name": case "name":
if strings.Contains(split[1], " ") {
return nil, errors.New("the " + split[0] + " field cannot contain spaces")
}
pkgInfo.Name = split[1] pkgInfo.Name = split[1]
case "description": case "description":
pkgInfo.Description = split[1] pkgInfo.Description = split[1]
case "version": case "version":
if strings.Contains(split[1], " ") {
return nil, errors.New("the " + split[0] + " field cannot contain spaces")
}
pkgInfo.Version = split[1] pkgInfo.Version = split[1]
case "url": case "url":
pkgInfo.Url = split[1] pkgInfo.Url = split[1]
@ -192,7 +198,7 @@ func CreateInfoFile(pkgInfo PackageInfo) string {
return ret return ret
} }
func InstallPackage(filename, installDir string, force bool) error { func InstallPackage(filename, installDir string, force, binaryPkgFromSrc, keepTempDir bool) error {
if _, err := os.Stat(filename); os.IsNotExist(err) { if _, err := os.Stat(filename); os.IsNotExist(err) {
return err return err
} }
@ -205,13 +211,17 @@ func InstallPackage(filename, installDir string, force bool) error {
return err return err
} }
tr := tar.NewReader(archive) tr := tar.NewReader(archive)
var oldFiles []string
var files []string var files []string
pkgInfo, err := ReadPackage(filename) pkgInfo, err := ReadPackage(filename)
if err != nil { if err != nil {
return err return err
} }
if IsPackageInstalled(pkgInfo.Name, installDir) {
oldFiles = GetPackageFiles(pkgInfo.Name, installDir)
}
if !force { if !force {
if pkgInfo.Arch != GetArch() { if pkgInfo.Arch != "any" && pkgInfo.Arch != GetArch() {
return errors.New("cannot install a package with a different architecture") return errors.New("cannot install a package with a different architecture")
} }
if unresolved := CheckDependencies(pkgInfo, installDir); len(unresolved) != 0 { if unresolved := CheckDependencies(pkgInfo, installDir); len(unresolved) != 0 {
@ -307,7 +317,12 @@ func InstallPackage(filename, installDir string, force bool) error {
if err != nil { if err != nil {
return err return err
} }
temp, err := os.MkdirTemp("/var/tmp/", "bpm_source-") temp := "/var/tmp/bpm_source-" + pkgInfo.Name
err = os.RemoveAll(temp)
if err != nil {
return err
}
err = os.Mkdir(temp, 0755)
fmt.Println("Creating temp directory at: " + temp) fmt.Println("Creating temp directory at: " + temp)
if err != nil { if err != nil {
return err return err
@ -352,6 +367,10 @@ func InstallPackage(filename, installDir string, force bool) error {
fmt.Println("Creating Directory: " + extractFilename) fmt.Println("Creating Directory: " + extractFilename)
} }
} else if d.Type().IsRegular() { } else if d.Type().IsRegular() {
err := os.Remove(extractFilename)
if err != nil && !os.IsNotExist(err) {
return err
}
outFile, err := os.Create(extractFilename) outFile, err := os.Create(extractFilename)
fmt.Println("Creating File: " + extractFilename) fmt.Println("Creating File: " + extractFilename)
files = append(files, relFilename) files = append(files, relFilename)
@ -385,6 +404,10 @@ func InstallPackage(filename, installDir string, force bool) error {
if err != nil { if err != nil {
return err return err
} }
err = os.Remove(extractFilename)
if err != nil && !os.IsNotExist(err) {
return err
}
fmt.Println("Creating Symlink: "+extractFilename, " -> "+link) fmt.Println("Creating Symlink: "+extractFilename, " -> "+link)
files = append(files, relFilename) files = append(files, relFilename)
err = os.Symlink(link, extractFilename) err = os.Symlink(link, extractFilename)
@ -397,6 +420,39 @@ func InstallPackage(filename, installDir string, force bool) error {
if err != nil { if err != nil {
return err return err
} }
if binaryPkgFromSrc {
compiledDir := path.Join(installDir, "var/lib/bpm/compiled/")
err = os.MkdirAll(compiledDir, 755)
compiledInfo := PackageInfo{}
compiledInfo = *pkgInfo
compiledInfo.Type = "binary"
compiledInfo.Arch = GetArch()
err = os.WriteFile(path.Join(compiledDir, "pkg.info"), []byte(CreateInfoFile(compiledInfo)), 0644)
if err != nil {
return err
}
sed := fmt.Sprintf("s/%s/files/", strings.Replace(strings.TrimPrefix(path.Join(temp, "/output/"), "/"), "/", `\/`, -1))
cmd := exec.Command("/usr/bin/tar", "-czvf", compiledInfo.Name+".bpm", "pkg.info", path.Join(temp, "/output/"), "--transform", sed)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = compiledDir
fmt.Printf("running command: %s %s\n", cmd.Path, strings.Join(cmd.Args, " "))
err := cmd.Run()
if err != nil {
return err
}
err = os.Remove(path.Join(compiledDir, "pkg.info"))
if err != nil {
return err
}
}
if !keepTempDir {
err := os.RemoveAll(temp)
if err != nil {
return err
}
}
} }
} }
} else { } else {
@ -405,6 +461,10 @@ func InstallPackage(filename, installDir string, force bool) error {
slices.Sort(files) slices.Sort(files)
slices.Reverse(files) slices.Reverse(files)
filesDiff := slices.DeleteFunc(oldFiles, func(f string) bool {
return slices.Contains(files, f)
})
installedDir := path.Join(installDir, "var/lib/bpm/installed/") installedDir := path.Join(installDir, "var/lib/bpm/installed/")
err = os.MkdirAll(installedDir, 755) err = os.MkdirAll(installedDir, 755)
if err != nil { if err != nil {
@ -460,6 +520,12 @@ func InstallPackage(filename, installDir string, force bool) error {
if err != nil { if err != nil {
return err return err
} }
if len(filesDiff) != 0 {
fmt.Println("Removing obsolete files")
for _, f := range filesDiff {
fmt.Println("Removing: " + path.Join(installedDir, f))
}
}
return nil return nil
} }

49
main.go
View File

@ -6,9 +6,7 @@ import (
"fmt" "fmt"
"log" "log"
"os" "os"
"path"
"slices" "slices"
"strconv"
"strings" "strings"
) )
@ -17,11 +15,11 @@ import (
/* A simple-to-use package manager */ /* A simple-to-use package manager */
/* ---------------------------------- */ /* ---------------------------------- */
var bpmVer = "0.0.9" var bpmVer = "0.1.0"
var rootDir = "/" var rootDir = "/"
func main() { func main() {
errs, fixed := bpm_utils.FixInstalledPackages(rootDir) /*errs, fixed := bpm_utils.FixInstalledPackages(rootDir)
if len(errs) != 0 { if len(errs) != 0 {
for pkg, err := range errs { for pkg, err := range errs {
fmt.Printf("Package (%s) could not be read properly\nError: %s\n", pkg, err.Error()) fmt.Printf("Package (%s) could not be read properly\nError: %s\n", pkg, err.Error())
@ -32,11 +30,7 @@ func main() {
if fixed != 0 { if fixed != 0 {
fmt.Println("Fixed " + strconv.Itoa(fixed) + " outdated package info files") fmt.Println("Fixed " + strconv.Itoa(fixed) + " outdated package info files")
} }
} }*/
if os.Getuid() != 0 {
fmt.Println("BPM needs to be run with superuser permissions")
os.Exit(0)
}
resolveCommand() resolveCommand()
} }
@ -135,6 +129,10 @@ func resolveCommand() {
} }
} }
case install: case install:
if os.Getuid() != 0 {
fmt.Println("This subcommand needs to be run with superuser permissions")
os.Exit(0)
}
flags, i := resolveFlags() flags, i := resolveFlags()
files := getArgs()[1+i:] files := getArgs()[1+i:]
if len(files) == 0 { if len(files) == 0 {
@ -153,7 +151,7 @@ func resolveCommand() {
verb = "build" verb = "build"
} }
if !slices.Contains(flags, "f") { if !slices.Contains(flags, "f") {
if pkgInfo.Arch != bpm_utils.GetArch() { if pkgInfo.Arch != "any" && pkgInfo.Arch != bpm_utils.GetArch() {
fmt.Printf("skipping... cannot %s a package with a different architecture\n", verb) fmt.Printf("skipping... cannot %s a package with a different architecture\n", verb)
continue continue
} }
@ -203,10 +201,6 @@ func resolveCommand() {
continue continue
} }
} }
err := bpm_utils.RemovePackage(pkgInfo.Name, rootDir)
if err != nil {
log.Fatalf("Could not remove current version of the package\nError: %s\n", err)
}
} else if !slices.Contains(flags, "y") { } else if !slices.Contains(flags, "y") {
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
fmt.Printf("Do you wish to %s this package? [y\\N] ", verb) fmt.Printf("Do you wish to %s this package? [y\\N] ", verb)
@ -217,19 +211,23 @@ func resolveCommand() {
} }
} }
err = bpm_utils.InstallPackage(file, rootDir, slices.Contains(flags, "f")) err = bpm_utils.InstallPackage(file, rootDir, slices.Contains(flags, "f"), slices.Contains(flags, "b"), slices.Contains(flags, "k"))
if err != nil { if err != nil {
if pkgInfo.Type == "source" { if pkgInfo.Type == "source" && slices.Contains(flags, "k") {
fmt.Println("** It is recommended you delete the temporary bpm folder in /var/tmp **") fmt.Println("BPM temp directory was created at /var/tmp/bpm_source-" + pkgInfo.Name)
} }
log.Fatalf("Could not install package\nError: %s\n", err) log.Fatalf("Could not install package\nError: %s\n", err)
} }
fmt.Printf("Package (%s) was successfully installed!\n", pkgInfo.Name) fmt.Printf("Package (%s) was successfully installed!\n", pkgInfo.Name)
if pkgInfo.Type == "source" { if pkgInfo.Type == "source" && slices.Contains(flags, "k") {
fmt.Println("** It is recommended you delete the temporary bpm folder in /var/tmp **") fmt.Println("** It is recommended you delete the temporary bpm folder in /var/tmp **")
} }
} }
case remove: case remove:
if os.Getuid() != 0 {
fmt.Println("This subcommand needs to be run with superuser permissions")
os.Exit(0)
}
flags, i := resolveFlags() flags, i := resolveFlags()
packages := getArgs()[1+i:] packages := getArgs()[1+i:]
if len(packages) == 0 { if len(packages) == 0 {
@ -268,9 +266,16 @@ func resolveCommand() {
fmt.Println("\033[1m\\ Command List /\033[0m") fmt.Println("\033[1m\\ Command List /\033[0m")
fmt.Println("-> bpm version | shows information on the installed version of bpm") fmt.Println("-> bpm version | shows information on the installed version of bpm")
fmt.Println("-> bpm info | shows information on an installed package") fmt.Println("-> bpm info | shows information on an installed package")
fmt.Println("-> bpm list [-n, -l] | lists all installed packages. -n shows the number of packages. -l lists package names only") fmt.Println("-> bpm list [-n, -l] | lists all installed packages")
fmt.Println("-> bpm install [-y, -f] <files...> | installs the following files. -y skips the confirmation prompt. -f skips dependency and architecture checking") fmt.Println(" -n shows the number of packages")
fmt.Println("-> bpm remove [-y] <packages...> | removes the following packages. -y skips the confirmation prompt") fmt.Println(" -l lists package names only")
fmt.Println("-> bpm install [-y, -f, -b] <files...> | installs the following files")
fmt.Println(" -y skips the confirmation prompt")
fmt.Println(" -f skips dependency and architecture checking")
fmt.Println(" -b creates a binary package for a source package after compilation and saves it in /var/lib/bpm/compiled")
fmt.Println(" -k keeps the temp directory created by BPM after source package installation")
fmt.Println("-> bpm remove [-y] <packages...> | removes the following packages")
fmt.Println(" -y skips the confirmation prompt")
fmt.Println("-> bpm cleanup | removes all unneeded dependencies") fmt.Println("-> bpm cleanup | removes all unneeded dependencies")
fmt.Println("\033[1m----------------\033[0m") fmt.Println("\033[1m----------------\033[0m")
} }
@ -292,7 +297,7 @@ func resolveFlags() ([]string, int) {
} }
ret = append(ret, f) ret = append(ret, f)
case install: case install:
v := [...]string{"y", "f"} v := [...]string{"y", "f", "b", "k"}
if !slices.Contains(v[:], f) { if !slices.Contains(v[:], f) {
log.Fatalf("Invalid flag " + flag) log.Fatalf("Invalid flag " + flag)
} }

View File

@ -7,13 +7,25 @@ fi
output=$1 output=$1
if [[ ! "$output" =~ ^[a-zA-Z0-9_-]{1,}$ ]]; then
echo "Invalid output name! The name must only contain letters, numbers, hyphens or underscores!"
exit 1
fi
type="binary"
echo "Creating package with the name $output..." echo "Creating package with the name $output..."
if [ -d files ]; then if [ -d files ]; then
echo "files/ directory found" echo "files/ directory found"
else else
echo "files/ directory not found in $PWD" if [ -f source.sh ]; then
exit 1 type="source"
echo "source.sh file found"
else
echo "files/ directory or source.sh file not found in $PWD"
exit 1
fi
fi fi
if [ -f pkg.info ]; then if [ -f pkg.info ]; then
@ -23,6 +35,10 @@ else
exit 1 exit 1
fi fi
echo "Creating $output.bpm package..." echo "Creating $type package as $output.bpm"
tar -czf $output.bpm files/ pkg.info if [[ "$type" == "binary" ]]; then
tar -czf "$output".bpm files/ pkg.info
else
tar -czf "$output".bpm source.sh pkg.info
fi

View File

@ -0,0 +1,66 @@
#!/bin/bash
if [ $# -lt 2 ]; then
echo "Arguments missing! Try 'bpm-setup <directory> <binary/source>'"
exit 1
fi
output=$1
type=$2
if [[ ! "$output" =~ ^[a-zA-Z0-9_-]{1,}$ ]]; then
echo "Invalid output name! The name must only contain letters, numbers, hyphens or underscores!"
exit 1
fi
if [[ "$type" != "binary" ]] && [[ "$type" != "source" ]]; then
echo "Invalid package type! Package type must be either 'binary' or 'source'"
exit 1
fi
mkdir -pv $output
cd $output
if [[ "$type" == "binary" ]]; then
cat > pkg.info << EOF
name: package_name
description: Package Description
version: 1.0
url: your package's website/repository url. Optonal
license: your package's license. Optional
architecture: $(uname -m)
type: binary
EOF
mkdir -pv files
echo "Package directory created successfully!"
echo "Make sure to edit the pkg.info file with the appropriate information for your package"
echo "Add your binaries under the files/ directory. For example a binary called 'my_binary' should go under files/usr/bin/my_binary"
echo "You can turn your package into a .bpm file use the 'bpm-create <name>' command"
else
cat > pkg.info << EOF
name: package_name
description: Package Description
version: 1.0
url: your package's website/repository url. Optional
license: your package's license. Optional
architecture: any
type: source
EOF
cat > source.sh << 'EOF'
# This is the source.sh script. It is executed by BPM in a temporary directory when compiling a source package
# BPM expects there to be an 'output' directory under the root of the temporary directory after this script finishes executing, otherwise your program may not be correctly installed
# It is recommended you create the 'output' directory along with a 'source' directory in the root of the temporary directory like this
echo "Compiling my_program..."
# Creating 'source' and 'output' directory variables
source=$(pwd)/source
output=$(pwd)/output
# Creating the 'source' and 'output' directories
mkdir $source
mkdir $output
# Downloading files
git clone https://myrepo.com/repo.git source
EOF
echo "Package directory created successfully!"
echo "Make sure to edit the pkg.info file with the appropriate information for your package"
echo "Add your compilation code in the source.sh file. Follow the instructions on the template file on how to do properly compile your program"
echo "You can turn your package into a .bpm file use the 'bpm-create <name>' command"
fi

View File

@ -1,6 +1,6 @@
name: bpm-utils name: bpm-utils
description: Utilities to create BPM packages description: Utilities to create BPM packages
version: 1.0.0 version: 1.2.0
url: https://gitlab.com/bubble-package-manager/bpm/ url: https://gitlab.com/bubble-package-manager/bpm/
license: GPL3 license: GPL3
architecture: x86_64 architecture: x86_64

Binary file not shown.

View File

@ -1,6 +1,6 @@
name: bpm name: bpm
description: The Bubble Package Manager description: The Bubble Package Manager
version: 0.0.9 version: 0.1.0
url: https://gitlab.com/bubble-package-manager/bpm/ url: https://gitlab.com/bubble-package-manager/bpm/
license: GPL3 license: GPL3
architecture: x86_64 architecture: x86_64