diff --git a/bpm_utils/package_utils.go b/bpm_utils/package_utils.go index 24eaf4b..1fa70a5 100644 --- a/bpm_utils/package_utils.go +++ b/bpm_utils/package_utils.go @@ -130,10 +130,16 @@ func ReadPackageInfo(contents string, defaultValues bool) (*PackageInfo, error) split[1] = strings.Trim(split[1], " ") switch split[0] { case "name": + if strings.Contains(split[1], " ") { + return nil, errors.New("the " + split[0] + " field cannot contain spaces") + } pkgInfo.Name = split[1] case "description": pkgInfo.Description = split[1] case "version": + if strings.Contains(split[1], " ") { + return nil, errors.New("the " + split[0] + " field cannot contain spaces") + } pkgInfo.Version = split[1] case "url": pkgInfo.Url = split[1] @@ -192,7 +198,7 @@ func CreateInfoFile(pkgInfo PackageInfo) string { 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) { return err } @@ -205,13 +211,17 @@ func InstallPackage(filename, installDir string, force bool) error { return err } tr := tar.NewReader(archive) + var oldFiles []string var files []string pkgInfo, err := ReadPackage(filename) if err != nil { return err } + if IsPackageInstalled(pkgInfo.Name, installDir) { + oldFiles = GetPackageFiles(pkgInfo.Name, installDir) + } if !force { - if pkgInfo.Arch != GetArch() { + if pkgInfo.Arch != "any" && pkgInfo.Arch != GetArch() { return errors.New("cannot install a package with a different architecture") } if unresolved := CheckDependencies(pkgInfo, installDir); len(unresolved) != 0 { @@ -307,7 +317,12 @@ func InstallPackage(filename, installDir string, force bool) error { if err != nil { 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) if err != nil { return err @@ -352,6 +367,10 @@ func InstallPackage(filename, installDir string, force bool) error { fmt.Println("Creating Directory: " + extractFilename) } } else if d.Type().IsRegular() { + err := os.Remove(extractFilename) + if err != nil && !os.IsNotExist(err) { + return err + } outFile, err := os.Create(extractFilename) fmt.Println("Creating File: " + extractFilename) files = append(files, relFilename) @@ -385,6 +404,10 @@ func InstallPackage(filename, installDir string, force bool) error { if err != nil { return err } + err = os.Remove(extractFilename) + if err != nil && !os.IsNotExist(err) { + return err + } fmt.Println("Creating Symlink: "+extractFilename, " -> "+link) files = append(files, relFilename) err = os.Symlink(link, extractFilename) @@ -397,6 +420,39 @@ func InstallPackage(filename, installDir string, force bool) error { if err != nil { 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 { @@ -405,6 +461,10 @@ func InstallPackage(filename, installDir string, force bool) error { slices.Sort(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/") err = os.MkdirAll(installedDir, 755) if err != nil { @@ -460,6 +520,12 @@ func InstallPackage(filename, installDir string, force bool) error { if err != nil { return err } + if len(filesDiff) != 0 { + fmt.Println("Removing obsolete files") + for _, f := range filesDiff { + fmt.Println("Removing: " + path.Join(installedDir, f)) + } + } return nil } diff --git a/main.go b/main.go index ba339d6..efed1d5 100644 --- a/main.go +++ b/main.go @@ -6,9 +6,7 @@ import ( "fmt" "log" "os" - "path" "slices" - "strconv" "strings" ) @@ -17,11 +15,11 @@ import ( /* A simple-to-use package manager */ /* ---------------------------------- */ -var bpmVer = "0.0.9" +var bpmVer = "0.1.0" var rootDir = "/" func main() { - errs, fixed := bpm_utils.FixInstalledPackages(rootDir) + /*errs, fixed := bpm_utils.FixInstalledPackages(rootDir) if len(errs) != 0 { for pkg, err := range errs { fmt.Printf("Package (%s) could not be read properly\nError: %s\n", pkg, err.Error()) @@ -32,11 +30,7 @@ func main() { if fixed != 0 { 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() } @@ -135,6 +129,10 @@ func resolveCommand() { } } case install: + if os.Getuid() != 0 { + fmt.Println("This subcommand needs to be run with superuser permissions") + os.Exit(0) + } flags, i := resolveFlags() files := getArgs()[1+i:] if len(files) == 0 { @@ -153,7 +151,7 @@ func resolveCommand() { verb = "build" } 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) continue } @@ -203,10 +201,6 @@ func resolveCommand() { 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") { reader := bufio.NewReader(os.Stdin) 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 pkgInfo.Type == "source" { - fmt.Println("** It is recommended you delete the temporary bpm folder in /var/tmp **") + if pkgInfo.Type == "source" && slices.Contains(flags, "k") { + fmt.Println("BPM temp directory was created at /var/tmp/bpm_source-" + pkgInfo.Name) } log.Fatalf("Could not install package\nError: %s\n", err) } 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 **") } } case remove: + if os.Getuid() != 0 { + fmt.Println("This subcommand needs to be run with superuser permissions") + os.Exit(0) + } flags, i := resolveFlags() packages := getArgs()[1+i:] if len(packages) == 0 { @@ -268,9 +266,16 @@ func resolveCommand() { fmt.Println("\033[1m\\ Command List /\033[0m") 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 list [-n, -l] | lists all installed packages. -n shows the number of packages. -l lists package names only") - fmt.Println("-> bpm install [-y, -f] | installs the following files. -y skips the confirmation prompt. -f skips dependency and architecture checking") - fmt.Println("-> bpm remove [-y] | removes the following packages. -y skips the confirmation prompt") + fmt.Println("-> bpm list [-n, -l] | lists all installed packages") + fmt.Println(" -n shows the number of packages") + fmt.Println(" -l lists package names only") + fmt.Println("-> bpm install [-y, -f, -b] | 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] | removes the following packages") + fmt.Println(" -y skips the confirmation prompt") fmt.Println("-> bpm cleanup | removes all unneeded dependencies") fmt.Println("\033[1m----------------\033[0m") } @@ -292,7 +297,7 @@ func resolveFlags() ([]string, int) { } ret = append(ret, f) case install: - v := [...]string{"y", "f"} + v := [...]string{"y", "f", "b", "k"} if !slices.Contains(v[:], f) { log.Fatalf("Invalid flag " + flag) } diff --git a/test_packages/x86_64/bpm-utils/bpm-utils.bpm b/test_packages/x86_64/bpm-utils/bpm-utils.bpm index e56d284..9f45431 100644 Binary files a/test_packages/x86_64/bpm-utils/bpm-utils.bpm and b/test_packages/x86_64/bpm-utils/bpm-utils.bpm differ diff --git a/test_packages/x86_64/bpm-utils/files/usr/bin/bpm-create b/test_packages/x86_64/bpm-utils/files/usr/bin/bpm-create index b9fd3e8..5396a97 100755 --- a/test_packages/x86_64/bpm-utils/files/usr/bin/bpm-create +++ b/test_packages/x86_64/bpm-utils/files/usr/bin/bpm-create @@ -7,13 +7,25 @@ fi 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..." if [ -d files ]; then echo "files/ directory found" else - echo "files/ directory not found in $PWD" - exit 1 + if [ -f source.sh ]; then + type="source" + echo "source.sh file found" + else + echo "files/ directory or source.sh file not found in $PWD" + exit 1 + fi fi if [ -f pkg.info ]; then @@ -23,6 +35,10 @@ else exit 1 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 diff --git a/test_packages/x86_64/bpm-utils/files/usr/bin/bpm-setup b/test_packages/x86_64/bpm-utils/files/usr/bin/bpm-setup new file mode 100755 index 0000000..2c4948b --- /dev/null +++ b/test_packages/x86_64/bpm-utils/files/usr/bin/bpm-setup @@ -0,0 +1,66 @@ +#!/bin/bash +if [ $# -lt 2 ]; then + echo "Arguments missing! Try 'bpm-setup '" + 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 ' 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 ' command" +fi diff --git a/test_packages/x86_64/bpm-utils/pkg.info b/test_packages/x86_64/bpm-utils/pkg.info index b3f12f5..825a73c 100644 --- a/test_packages/x86_64/bpm-utils/pkg.info +++ b/test_packages/x86_64/bpm-utils/pkg.info @@ -1,6 +1,6 @@ name: bpm-utils description: Utilities to create BPM packages -version: 1.0.0 +version: 1.2.0 url: https://gitlab.com/bubble-package-manager/bpm/ license: GPL3 architecture: x86_64 diff --git a/test_packages/x86_64/bpm/bpm.bpm b/test_packages/x86_64/bpm/bpm.bpm index 2b29caf..d4e648b 100644 Binary files a/test_packages/x86_64/bpm/bpm.bpm and b/test_packages/x86_64/bpm/bpm.bpm differ diff --git a/test_packages/x86_64/bpm/files/usr/bin/bpm b/test_packages/x86_64/bpm/files/usr/bin/bpm index df081ce..acfad35 100755 Binary files a/test_packages/x86_64/bpm/files/usr/bin/bpm and b/test_packages/x86_64/bpm/files/usr/bin/bpm differ diff --git a/test_packages/x86_64/bpm/pkg.info b/test_packages/x86_64/bpm/pkg.info index 973453a..be88d5f 100644 --- a/test_packages/x86_64/bpm/pkg.info +++ b/test_packages/x86_64/bpm/pkg.info @@ -1,6 +1,6 @@ name: bpm description: The Bubble Package Manager -version: 0.0.9 +version: 0.1.0 url: https://gitlab.com/bubble-package-manager/bpm/ license: GPL3 architecture: x86_64