7 Commits
9 changed files with 770 additions and 278 deletions
+7 -6
View File
@@ -77,7 +77,7 @@ mkdir files
5) Either copy the bpm-create script from the bpm-utils test package into your /usr/local/bin directory or install the bpm-utils.bpm package 5) Either copy the bpm-create script from the bpm-utils test package into your /usr/local/bin directory or install the bpm-utils.bpm package
6) Run the following 6) Run the following
``` ```
bpm-create <filename_without_extension> bpm-create <filename.bpm>
``` ```
7) It's done! You now hopefully have a working BPM package! 7) It's done! You now hopefully have a working BPM package!
### Source Packages ### Source Packages
@@ -85,10 +85,11 @@ bpm-create <filename_without_extension>
``` ```
touch source.sh touch source.sh
``` ```
4) You are able to run bash code in this file. BPM will extract this file in a directory under /tmp and it will be ran there 4) If you would like to bundle patches or other files with your source package create a 'source-files' directory and place your files in there. They will be extracted to the same location as the source.sh file during compilation
5) Your goal is to download your program's source code with either git, wget, curl, etc. and put the binaries under a folder called 'output' in the root of the temp directory. There is a simple example script with helpful comments in the htop-src test package 5) You are able to run bash code in source.sh. BPM will extract this file in a directory under /tmp and it will be run there
6) As of this moment there is no script to automate package compression like for binary packages. You will need to create the archive manually 6) Your goal is to download your program's source code with either git, wget, curl, etc. and put the binaries under a folder called 'output' in the root of the temp directory. There is a simple example script with helpful comments in the htop-src test package
7) When you are done making your source.sh script run the following to create a package archive
``` ```
tar -czvf my_package-src.bpm pkg.info source.sh bpm-create <filename.bpm>
``` ```
7) That's it! Your source package should now be compiling correctly! 8) That's it! Your source package should now be compiling correctly!
+437 -63
View File
@@ -8,7 +8,6 @@ import (
"fmt" "fmt"
"io" "io"
"io/fs" "io/fs"
"log"
"os" "os"
"os/exec" "os/exec"
"path" "path"
@@ -101,6 +100,166 @@ func ReadPackage(filename string) (*PackageInfo, error) {
return nil, errors.New("pkg.info not found in archive") return nil, errors.New("pkg.info not found in archive")
} }
func ReadPackageScripts(filename string) (map[string]string, error) {
if _, err := os.Stat(filename); os.IsNotExist(err) {
return nil, err
}
file, err := os.Open(filename)
if err != nil {
return nil, err
}
archive, err := gzip.NewReader(file)
if err != nil {
return nil, err
}
tr := tar.NewReader(archive)
ret := make(map[string]string)
for {
header, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
if header.Name == "pre_install.sh" {
bs, _ := io.ReadAll(tr)
if err != nil {
return nil, err
}
ret[header.Name] = string(bs)
} else if header.Name == "post_install.sh" {
bs, _ := io.ReadAll(tr)
if err != nil {
return nil, err
}
ret[header.Name] = string(bs)
} else if header.Name == "pre_update.sh" {
bs, _ := io.ReadAll(tr)
if err != nil {
return nil, err
}
ret[header.Name] = string(bs)
} else if header.Name == "post_update.sh" {
bs, _ := io.ReadAll(tr)
if err != nil {
return nil, err
}
ret[header.Name] = string(bs)
} else if header.Name == "post_remove.sh" {
bs, _ := io.ReadAll(tr)
if err != nil {
return nil, err
}
ret[header.Name] = string(bs)
}
}
err = archive.Close()
if err != nil {
return nil, err
}
err = file.Close()
if err != nil {
return nil, err
}
return ret, nil
}
type Operation uint8
const (
Install Operation = 0
Update = 1
Remove = 2
)
func ExecutePackageScripts(filename, rootDir string, operation Operation, postOperation bool) error {
pkgInfo, err := ReadPackage(filename)
if err != nil {
return err
}
scripts, err := ReadPackageScripts(filename)
if err != nil {
return err
}
run := func(name, content string) error {
temp, err := os.CreateTemp("", name)
if err != nil {
return err
}
_, err = temp.WriteString(content)
if err != nil {
return err
}
cmd := exec.Command("/bin/bash", temp.Name())
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))
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_URL=%s", pkgInfo.Url))
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
}
return nil
}
if operation == Install {
if val, ok := scripts["pre_install.sh"]; !postOperation && ok {
err := run("pre_install.sh", val)
if err != nil {
return err
}
}
if val, ok := scripts["post_install.sh"]; postOperation && ok {
err := run("post_install.sh", val)
if err != nil {
return err
}
}
} else if operation == Update {
if val, ok := scripts["pre_update.sh"]; !postOperation && ok {
err := run("pre_update.sh", val)
if err != nil {
return err
}
}
if val, ok := scripts["post_update.sh"]; postOperation && ok {
err := run("post_update.sh", val)
if err != nil {
return err
}
}
} else if operation == Remove {
if val, ok := scripts["post_remove.sh"]; postOperation && ok {
err := run("post_remove.sh", val)
if err != nil {
return err
}
}
}
return nil
}
func ReadPackageInfo(contents string, defaultValues bool) (*PackageInfo, error) { func ReadPackageInfo(contents string, defaultValues bool) (*PackageInfo, error) {
pkgInfo := PackageInfo{ pkgInfo := PackageInfo{
Name: "", Name: "",
@@ -217,7 +376,8 @@ func InstallPackage(filename, installDir string, force, binaryPkgFromSrc, keepTe
if err != nil { if err != nil {
return err return err
} }
if IsPackageInstalled(pkgInfo.Name, installDir) { packageInstalled := IsPackageInstalled(pkgInfo.Name, installDir)
if packageInstalled {
oldFiles = GetPackageFiles(pkgInfo.Name, installDir) oldFiles = GetPackageFiles(pkgInfo.Name, installDir)
} }
if !force { if !force {
@@ -228,8 +388,18 @@ func InstallPackage(filename, installDir string, force, binaryPkgFromSrc, keepTe
return errors.New("Could not resolve all dependencies. Missing " + strings.Join(unresolved, ", ")) return errors.New("Could not resolve all dependencies. Missing " + strings.Join(unresolved, ", "))
} }
} }
if pkgInfo.Type == "binary" { if pkgInfo.Type == "binary" {
if !packageInstalled {
err = ExecutePackageScripts(filename, installDir, Install, false)
if err != nil {
return err
}
} else {
err = ExecutePackageScripts(filename, installDir, Update, false)
if err != nil {
return err
}
}
seenHardlinks := make(map[string]string) seenHardlinks := make(map[string]string)
for { for {
header, err := tr.Next() header, err := tr.Next()
@@ -304,19 +474,6 @@ func InstallPackage(filename, installDir string, force, binaryPkgFromSrc, keepTe
} }
} }
} else if pkgInfo.Type == "source" { } else if pkgInfo.Type == "source" {
for {
header, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
if header.Name == "source.sh" {
bs, err := io.ReadAll(tr)
if err != nil {
return err
}
temp := "/var/tmp/bpm_source-" + pkgInfo.Name temp := "/var/tmp/bpm_source-" + pkgInfo.Name
err = os.RemoveAll(temp) err = os.RemoveAll(temp)
if err != nil { if err != nil {
@@ -327,16 +484,106 @@ func InstallPackage(filename, installDir string, force, binaryPkgFromSrc, keepTe
if err != nil { if err != nil {
return err return err
} }
for {
header, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
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
}
} else {
fmt.Println("Creating Directory: " + extractFilename)
}
case tar.TypeReg:
err := os.Remove(extractFilename)
if err != nil && !os.IsNotExist(err) {
return err
}
outFile, err := os.Create(extractFilename)
fmt.Println("Creating File: " + extractFilename)
if err != nil {
return err
}
if _, err := io.Copy(outFile, tr); 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:
fmt.Println("Skipping symlink (Bundling symlinks in source packages is not supported)")
case tar.TypeLink:
fmt.Println("Skipping hard link (Bundling hard links in source packages is not supported)")
default:
return errors.New("ExtractTarGz: unknown type: " + strconv.Itoa(int(header.Typeflag)) + " in " + extractFilename)
}
}
if header.Name == "source.sh" {
bs, err := io.ReadAll(tr)
if err != nil {
return err
}
err = os.WriteFile(path.Join(temp, "source.sh"), bs, 0644) err = os.WriteFile(path.Join(temp, "source.sh"), bs, 0644)
if err != nil { if err != nil {
return err return err
} }
}
}
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")
}
if err != nil {
return err
}
fmt.Println("Running source.sh file...") fmt.Println("Running source.sh file...")
cmd := exec.Command("/usr/bin/sh", "source.sh") if !packageInstalled {
err = ExecutePackageScripts(filename, installDir, Install, false)
if err != nil {
return err
}
} else {
err = ExecutePackageScripts(filename, installDir, Update, false)
if err != nil {
return err
}
}
cmd := exec.Command("/bin/bash", "-e", "source.sh")
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
cmd.Dir = temp cmd.Dir = temp
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", installDir))
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_URL=%s", pkgInfo.Url))
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() err = cmd.Run()
if err != nil { if err != nil {
return err return err
@@ -431,18 +678,31 @@ func InstallPackage(filename, installDir string, force, binaryPkgFromSrc, keepTe
if err != nil { if err != nil {
return err return err
} }
scripts, err := ReadPackageScripts(filename)
for key, val := range scripts {
err = os.WriteFile(path.Join(compiledDir, key), []byte(val), 0644)
if err != nil {
return err
}
}
sed := fmt.Sprintf("s/%s/files/", strings.Replace(strings.TrimPrefix(path.Join(temp, "/output/"), "/"), "/", `\/`, -1)) 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 := exec.Command("/usr/bin/tar", "-czvf", compiledInfo.Name+"-"+compiledInfo.Version+".bpm", "pkg.info", path.Join(temp, "/output/"), "--transform", sed)
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
cmd.Dir = compiledDir cmd.Dir = compiledDir
fmt.Printf("running command: %s %s\n", cmd.Path, strings.Join(cmd.Args, " ")) fmt.Printf("running command: %s %s\n", cmd.Path, strings.Join(cmd.Args, " "))
err := cmd.Run() err = cmd.Run()
if err != nil { if err != nil {
return err return err
} }
err = os.Remove(path.Join(compiledDir, "pkg.info")) err = os.Remove(path.Join(compiledDir, "pkg.info"))
for key := range scripts {
err = os.Remove(path.Join(compiledDir, key))
if err != nil {
return err
}
}
if err != nil { if err != nil {
return err return err
} }
@@ -453,7 +713,8 @@ func InstallPackage(filename, installDir string, force, binaryPkgFromSrc, keepTe
return err return err
} }
} }
} if len(files) == 0 {
return errors.New("no output files for source package. Cancelling package installation")
} }
} else { } else {
return errors.New("Unknown package type: " + pkgInfo.Type) return errors.New("Unknown package type: " + pkgInfo.Type)
@@ -512,6 +773,25 @@ func InstallPackage(filename, installDir string, force, binaryPkgFromSrc, keepTe
return err return err
} }
scripts, 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"))
if err != nil {
return err
}
_, err = f.WriteString(val)
if err != nil {
return err
}
err = f.Close()
if err != nil {
return err
}
}
err = archive.Close() err = archive.Close()
if err != nil { if err != nil {
return err return err
@@ -522,8 +802,77 @@ func InstallPackage(filename, installDir string, force, binaryPkgFromSrc, keepTe
} }
if len(filesDiff) != 0 { if len(filesDiff) != 0 {
fmt.Println("Removing obsolete files") fmt.Println("Removing obsolete files")
var symlinks []string
for _, f := range filesDiff { for _, f := range filesDiff {
fmt.Println("Removing: " + path.Join(installedDir, f)) f = path.Join(installDir, f)
lstat, err := os.Lstat(f)
if os.IsNotExist(err) {
continue
}
if err != nil {
return err
}
if lstat.Mode() == os.ModeSymlink {
symlinks = append(symlinks, f)
continue
}
stat, err := os.Stat(f)
if os.IsNotExist(err) {
continue
}
if err != nil {
return err
}
if stat.IsDir() {
dir, err := os.ReadDir(f)
if err != nil {
return err
}
if len(dir) == 0 {
fmt.Println("Removing: " + f)
err := os.Remove(f)
if err != nil {
return err
}
}
} else {
fmt.Println("Removing: " + f)
err := os.Remove(f)
if err != nil {
return err
}
}
}
for _, f := range symlinks {
f = path.Join(installDir, f)
_, err := os.Lstat(f)
if os.IsNotExist(err) {
continue
}
if err != nil {
return err
}
_, err = filepath.EvalSymlinks(f)
if os.IsNotExist(err) {
err := os.Remove(f)
if err != nil {
return err
}
}
if err != nil {
return err
}
}
}
if !packageInstalled {
err = ExecutePackageScripts(filename, installDir, Install, true)
if err != nil {
return err
}
} else {
err = ExecutePackageScripts(filename, installDir, Update, true)
if err != nil {
return err
} }
} }
return nil return nil
@@ -743,10 +1092,26 @@ func setPackageInfo(pkg, contents, rootDir string) error {
func RemovePackage(pkg, rootDir string) error { func RemovePackage(pkg, rootDir string) error {
installedDir := path.Join(rootDir, "var/lib/bpm/installed/") installedDir := path.Join(rootDir, "var/lib/bpm/installed/")
pkgDir := path.Join(installedDir, pkg) pkgDir := path.Join(installedDir, pkg)
pkgInfo := GetPackageInfo(pkg, rootDir, false)
if pkgInfo == nil {
return errors.New("could not get package info")
}
files := GetPackageFiles(pkg, rootDir) files := GetPackageFiles(pkg, rootDir)
var symlinks []string
for _, file := range files { for _, file := range files {
file = path.Join(rootDir, file) file = path.Join(rootDir, file)
stat, err := os.Lstat(file) lstat, err := os.Lstat(file)
if os.IsNotExist(err) {
continue
}
if err != nil {
return err
}
if lstat.Mode() == os.ModeSymlink {
symlinks = append(symlinks, file)
continue
}
stat, err := os.Stat(file)
if os.IsNotExist(err) { if os.IsNotExist(err) {
continue continue
} }
@@ -773,6 +1138,56 @@ func RemovePackage(pkg, rootDir string) error {
} }
} }
} }
for _, file := range symlinks {
file = path.Join(rootDir, file)
_, err := os.Lstat(file)
if os.IsNotExist(err) {
continue
}
if err != nil {
return err
}
_, err = filepath.EvalSymlinks(file)
if os.IsNotExist(err) {
err := os.Remove(file)
if err != nil {
return err
}
}
if err != nil {
return err
}
}
if _, err := os.Stat(path.Join(pkgDir, "post_remove.sh")); err == nil {
cmd := exec.Command("/bin/bash", path.Join(pkgDir, "post_remove.sh"))
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))
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_URL=%s", pkgInfo.Url))
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
}
}
err := os.RemoveAll(pkgDir) err := os.RemoveAll(pkgDir)
if err != nil { if err != nil {
return err return err
@@ -780,44 +1195,3 @@ func RemovePackage(pkg, rootDir string) error {
fmt.Println("Removing: " + pkgDir) fmt.Println("Removing: " + pkgDir)
return nil return nil
} }
func FixInstalledPackages(rootDir string) (map[string]error, int) {
var errs map[string]error
totalFixed := 0
pkgs, err := GetInstalledPackages(rootDir)
if err != nil {
log.Fatalf("Could not check if installed package info files are formatted correctly\nError: %s", err.Error())
}
for _, pkg := range pkgs {
fixed := false
pkgInfo := GetPackageInfo(pkg, "/", true)
if pkgInfo.Name == "" {
errs[pkg] = errors.New("this package contains no name")
continue
}
if pkgInfo.Description == "" {
pkgInfo.Description = "Default Description"
fixed = true
}
if pkgInfo.Version == "" {
errs[pkg] = errors.New("this package contains no version")
continue
}
if pkgInfo.Arch == "" {
pkgInfo.Arch = GetArch()
fixed = true
}
if pkgInfo.Type == "" {
errs[pkg] = errors.New("this package contains no type")
continue
}
if fixed {
totalFixed++
}
err := setPackageInfo(pkg, CreateInfoFile(*pkgInfo), rootDir)
if err != nil {
log.Fatalf("Could not check if installed package info files are formatted correctly\nError: %s", err.Error())
}
}
return errs, totalFixed
}
+164 -80
View File
@@ -3,9 +3,11 @@ package main
import ( import (
"bufio" "bufio"
"capcreepergr.me/bpm/bpm_utils" "capcreepergr.me/bpm/bpm_utils"
"flag"
"fmt" "fmt"
"log" "log"
"os" "os"
"path/filepath"
"slices" "slices"
"strings" "strings"
) )
@@ -15,29 +17,25 @@ import (
/* A simple-to-use package manager */ /* A simple-to-use package manager */
/* ---------------------------------- */ /* ---------------------------------- */
var bpmVer = "0.1.0" var bpmVer = "0.1.7"
var subcommand = "help"
var subcommandArgs []string
// Flags
var rootDir = "/" var rootDir = "/"
var yesAll = false
var buildSource = false
var keepTempDir = false
var forceInstall = false
var pkgListNumbers = false
var pkgListNames = false
func main() { func main() {
/*errs, fixed := bpm_utils.FixInstalledPackages(rootDir) resolveFlags()
if len(errs) != 0 {
for pkg, err := range errs {
fmt.Printf("Package (%s) could not be read properly\nError: %s\n", pkg, err.Error())
}
fmt.Println("The aforementioned packages require manual fixing. Make sure their info files are valid in " + path.Join(rootDir, "var/lib/bpm/installed"))
os.Exit(1)
} else {
if fixed != 0 {
fmt.Println("Fixed " + strconv.Itoa(fixed) + " outdated package info files")
}
}*/
resolveCommand() resolveCommand()
} }
func getArgs() []string {
return os.Args[1:]
}
type commandType uint8 type commandType uint8
const ( const (
@@ -47,15 +45,11 @@ const (
list list
install install
remove remove
cleanup file
) )
func getCommandType() commandType { func getCommandType() commandType {
if len(getArgs()) == 0 { switch subcommand {
return help
}
cmd := getArgs()[0]
switch cmd {
case "version": case "version":
return version return version
case "info": case "info":
@@ -66,23 +60,20 @@ func getCommandType() commandType {
return install return install
case "remove": case "remove":
return remove return remove
case "cleanup": case "file":
return cleanup return file
default: default:
return help return help
} }
} }
func resolveCommand() { func resolveCommand() {
switch getCommandType() { switch getCommandType() {
case version: case version:
resolveFlags()
fmt.Println("Bubble Package Manager (BPM)") fmt.Println("Bubble Package Manager (BPM)")
fmt.Println("Version: " + bpmVer) fmt.Println("Version: " + bpmVer)
case info: case info:
_, i := resolveFlags() packages := subcommandArgs
packages := getArgs()[1+i:]
if len(packages) == 0 { if len(packages) == 0 {
fmt.Println("No packages were given") fmt.Println("No packages were given")
return return
@@ -99,7 +90,6 @@ func resolveCommand() {
} }
} }
case list: case list:
flags, _ := resolveFlags()
packages, err := bpm_utils.GetInstalledPackages(rootDir) packages, err := bpm_utils.GetInstalledPackages(rootDir)
if err != nil { if err != nil {
log.Fatalf("Could not get installed packages\nError: %s", err.Error()) log.Fatalf("Could not get installed packages\nError: %s", err.Error())
@@ -109,9 +99,9 @@ func resolveCommand() {
fmt.Println("No packages have been installed") fmt.Println("No packages have been installed")
return return
} }
if slices.Contains(flags, "n") { if pkgListNumbers {
fmt.Println(len(packages)) fmt.Println(len(packages))
} else if slices.Contains(flags, "l") { } else if pkgListNames {
for _, pkg := range packages { for _, pkg := range packages {
fmt.Println(pkg) fmt.Println(pkg)
} }
@@ -133,8 +123,7 @@ func resolveCommand() {
fmt.Println("This subcommand needs to be run with superuser permissions") fmt.Println("This subcommand needs to be run with superuser permissions")
os.Exit(0) os.Exit(0)
} }
flags, i := resolveFlags() files := subcommandArgs
files := getArgs()[1+i:]
if len(files) == 0 { if len(files) == 0 {
fmt.Println("No files were given to install") fmt.Println("No files were given to install")
return return
@@ -150,7 +139,7 @@ func resolveCommand() {
if pkgInfo.Type == "source" { if pkgInfo.Type == "source" {
verb = "build" verb = "build"
} }
if !slices.Contains(flags, "f") { if !forceInstall {
if pkgInfo.Arch != "any" && 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
@@ -166,7 +155,10 @@ func resolveCommand() {
} }
} }
} }
if !slices.Contains(flags, "y") { if rootDir != "/" {
fmt.Println("Warning: Installing to " + rootDir)
}
if !yesAll {
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
if pkgInfo.Type == "source" { if pkgInfo.Type == "source" {
fmt.Print("Would you like to view the source.sh file of this package? [Y\\n] ") fmt.Print("Would you like to view the source.sh file of this package? [Y\\n] ")
@@ -182,7 +174,7 @@ func resolveCommand() {
} }
} }
if bpm_utils.IsPackageInstalled(pkgInfo.Name, rootDir) { if bpm_utils.IsPackageInstalled(pkgInfo.Name, rootDir) {
if !slices.Contains(flags, "y") { if !yesAll {
installedInfo := bpm_utils.GetPackageInfo(pkgInfo.Name, rootDir, false) installedInfo := bpm_utils.GetPackageInfo(pkgInfo.Name, rootDir, false)
if strings.Compare(pkgInfo.Version, installedInfo.Version) > 0 { if strings.Compare(pkgInfo.Version, installedInfo.Version) > 0 {
fmt.Println("This file contains a newer version of this package (" + installedInfo.Version + " -> " + pkgInfo.Version + ")") fmt.Println("This file contains a newer version of this package (" + installedInfo.Version + " -> " + pkgInfo.Version + ")")
@@ -201,7 +193,7 @@ func resolveCommand() {
continue continue
} }
} }
} else if !slices.Contains(flags, "y") { } else if !yesAll {
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)
text, _ := reader.ReadString('\n') text, _ := reader.ReadString('\n')
@@ -211,15 +203,15 @@ func resolveCommand() {
} }
} }
err = bpm_utils.InstallPackage(file, rootDir, slices.Contains(flags, "f"), slices.Contains(flags, "b"), slices.Contains(flags, "k")) err = bpm_utils.InstallPackage(file, rootDir, forceInstall, buildSource, keepTempDir)
if err != nil { if err != nil {
if pkgInfo.Type == "source" && slices.Contains(flags, "k") { if pkgInfo.Type == "source" && keepTempDir {
fmt.Println("BPM temp directory was created at /var/tmp/bpm_source-" + pkgInfo.Name) 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" && slices.Contains(flags, "k") { if pkgInfo.Type == "source" && keepTempDir {
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 **")
} }
} }
@@ -228,8 +220,7 @@ func resolveCommand() {
fmt.Println("This subcommand needs to be run with superuser permissions") fmt.Println("This subcommand needs to be run with superuser permissions")
os.Exit(0) os.Exit(0)
} }
flags, i := resolveFlags() packages := subcommandArgs
packages := getArgs()[1+i:]
if len(packages) == 0 { if len(packages) == 0 {
fmt.Println("No packages were given") fmt.Println("No packages were given")
return return
@@ -242,7 +233,10 @@ func resolveCommand() {
} }
fmt.Print("----------------\n" + bpm_utils.CreateInfoFile(*pkgInfo)) fmt.Print("----------------\n" + bpm_utils.CreateInfoFile(*pkgInfo))
fmt.Println("----------------") fmt.Println("----------------")
if !slices.Contains(flags, "y") { if rootDir != "/" {
fmt.Println("Warning: Installing to " + rootDir)
}
if !yesAll {
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
fmt.Print("Do you wish to remove this package? [y\\N] ") fmt.Print("Do you wish to remove this package? [y\\N] ")
text, _ := reader.ReadString('\n') text, _ := reader.ReadString('\n')
@@ -258,60 +252,150 @@ func resolveCommand() {
} }
fmt.Printf("Package (%s) was successfully removed!\n", pkgInfo.Name) fmt.Printf("Package (%s) was successfully removed!\n", pkgInfo.Name)
} }
case file:
files := subcommandArgs
if len(files) == 0 {
fmt.Println("No files were given to get which packages manage it")
return
}
for _, file := range files {
absFile, err := filepath.Abs(file)
if err != nil {
log.Fatalf("Could not get absolute path of %s", file)
}
stat, err := os.Stat(absFile)
if os.IsNotExist(err) {
log.Fatalf(absFile + " does not exist!")
}
pkgs, err := bpm_utils.GetInstalledPackages(rootDir)
if err != nil {
log.Fatalf("Could not get installed packages. Error %s", err.Error())
}
if !strings.HasPrefix(absFile, rootDir) {
log.Fatalf("Could not get relative path of %s to root path", absFile)
}
absFile, err = filepath.Rel(rootDir, absFile)
if err != nil {
log.Fatalf("Could not get relative path of %s to root path", absFile)
}
absFile = strings.TrimPrefix(absFile, "/")
if stat.IsDir() {
absFile = absFile + "/"
}
var pkgList []string
for _, pkg := range pkgs {
if slices.Contains(bpm_utils.GetPackageFiles(pkg, rootDir), absFile) {
pkgList = append(pkgList, pkg)
}
}
if len(pkgList) == 0 {
fmt.Println(absFile + " is not managed by any packages")
} else {
fmt.Println(absFile + " is managed by the following packages:")
for _, pkg := range pkgList {
fmt.Println("- " + pkg)
}
}
}
default: default:
printHelp()
}
}
func printHelp() {
fmt.Println("\033[1m------Help------\033[0m") fmt.Println("\033[1m------Help------\033[0m")
fmt.Println("\033[1m\\ Command Format /\033[0m") fmt.Println("\033[1m\\ Command Format /\033[0m")
fmt.Println("-> command format: bpm <subcommand> [-flags]...") fmt.Println("-> command format: bpm <subcommand> [-flags]...")
fmt.Println("-> flags will be read if passed right after the subcommand otherwise they will be read as subcommand arguments") 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("\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 [-R] | shows information on an installed package")
fmt.Println("-> bpm list [-n, -l] | lists all installed packages") fmt.Println(" -R=<root_path> lets you define the root path which will be used")
fmt.Println("-> bpm list [-R, -n, -l] | lists all installed packages")
fmt.Println(" -R=<root_path> lets you define the root path which will be used")
fmt.Println(" -n shows the number of packages") fmt.Println(" -n shows the number of packages")
fmt.Println(" -l lists package names only") fmt.Println(" -l lists package names only")
fmt.Println("-> bpm install [-y, -f, -b] <files...> | installs the following files") fmt.Println("-> bpm install [-R, -y, -f, -b] <files...> | installs the following files")
fmt.Println(" -R=<root_path> lets you define the root path which will be used")
fmt.Println(" -y skips the confirmation prompt") fmt.Println(" -y skips the confirmation prompt")
fmt.Println(" -f skips dependency and architecture checking") 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(" -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(" -k keeps the temp directory created by BPM after source package installation")
fmt.Println("-> bpm remove [-y] <packages...> | removes the following packages") fmt.Println("-> bpm remove [-R, -y] <packages...> | removes the following packages")
fmt.Println(" -R=<root_path> lets you define the root path which will be used")
fmt.Println(" -y skips the confirmation prompt") fmt.Println(" -y skips the confirmation prompt")
fmt.Println("-> bpm cleanup | removes all unneeded dependencies") fmt.Println("-> bpm file [-R] <files...> | shows what packages the following packages are managed by")
fmt.Println(" -R=<root_path> lets you define the root path which will be used")
fmt.Println("\033[1m----------------\033[0m") fmt.Println("\033[1m----------------\033[0m")
}
} }
func resolveFlags() ([]string, int) { func resolveFlags() {
flags := getArgs()[1:] // List flags
var ret []string listFlagSet := flag.NewFlagSet("List flags", flag.ExitOnError)
for _, flag := range flags { listFlagSet.Usage = printHelp
if strings.HasPrefix(flag, "-") { listFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
f := strings.TrimPrefix(flag, "-") listFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
switch getCommandType() { listFlagSet.BoolVar(&pkgListNumbers, "n", false, "List the number of all packages installed with BPM")
default: listFlagSet.BoolVar(&pkgListNames, "l", false, "List the names of all packages installed with BPM")
log.Fatalf("Invalid flag " + flag) // Info flags
case list: infoFlagSet := flag.NewFlagSet("Info flags", flag.ExitOnError)
v := [...]string{"l", "n"} infoFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
if !slices.Contains(v[:], f) { infoFlagSet.Usage = printHelp
log.Fatalf("Invalid flag " + flag) // Install flags
} installFlagSet := flag.NewFlagSet("Install flags", flag.ExitOnError)
ret = append(ret, f) installFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
case install: installFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
v := [...]string{"y", "f", "b", "k"} installFlagSet.BoolVar(&buildSource, "b", false, "Build binary package from source package")
if !slices.Contains(v[:], f) { installFlagSet.BoolVar(&keepTempDir, "k", false, "Keep temporary directory after source compilation")
log.Fatalf("Invalid flag " + flag) installFlagSet.BoolVar(&forceInstall, "f", false, "Force installation by skipping architecture and dependency resolution")
} installFlagSet.Usage = printHelp
ret = append(ret, f) // Remove flags
case remove: removeFlagSet := flag.NewFlagSet("Remove flags", flag.ExitOnError)
v := [...]string{"y"} removeFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
if !slices.Contains(v[:], f) { removeFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
log.Fatalf("Invalid flag " + flag) removeFlagSet.Usage = printHelp
} // File flags
ret = append(ret, f) // Remove flags
} fileFlagSet := flag.NewFlagSet("Remove flags", flag.ExitOnError)
fileFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
fileFlagSet.Usage = printHelp
if len(os.Args[1:]) <= 0 {
subcommand = "help"
} else { } else {
break subcommand = os.Args[1]
subcommandArgs = os.Args[2:]
if getCommandType() == list {
err := listFlagSet.Parse(subcommandArgs)
if err != nil {
return
}
subcommandArgs = listFlagSet.Args()
} else if getCommandType() == info {
err := infoFlagSet.Parse(subcommandArgs)
if err != nil {
return
}
subcommandArgs = infoFlagSet.Args()
} else if getCommandType() == install {
err := installFlagSet.Parse(subcommandArgs)
if err != nil {
return
}
subcommandArgs = installFlagSet.Args()
} else if getCommandType() == remove {
err := removeFlagSet.Parse(subcommandArgs)
if err != nil {
return
}
subcommandArgs = removeFlagSet.Args()
} else if getCommandType() == file {
err := fileFlagSet.Parse(subcommandArgs)
if err != nil {
return
}
subcommandArgs = fileFlagSet.Args()
} }
} }
return ret, len(ret)
} }
Binary file not shown.
@@ -7,21 +7,48 @@ fi
output=$1 output=$1
if [[ ! "$output" =~ ^[a-zA-Z0-9_-]{1,}$ ]]; then if [[ ! "$output" =~ ^[a-z.A-Z0-9_-]{1,}$ ]]; then
echo "Invalid output name! The name must only contain letters, numbers, hyphens or underscores!" echo "Invalid output name! The name must only contain letters, numbers, hyphens or underscores!"
exit 1 exit 1
fi fi
type="binary" type="binary"
toCompress=("pkg.info")
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"
toCompress+=("files/")
else else
if [ -f source.sh ]; then if [ -f source.sh ]; then
type="source" type="source"
echo "source.sh file found" echo "source.sh file found"
toCompress+=("source.sh")
if [ -f pre_update.sh ]; then
echo "pre_update.sh file found"
toCompress+=("pre_update.sh")
fi
if [ -f post_update.sh ]; then
echo "post_update.sh file found"
toCompress+=("post_update.sh")
fi
if [ -f pre_install.sh ]; then
echo "pre_install.sh file found"
toCompress+=("pre_install.sh")
fi
if [ -f post_install.sh ]; then
echo "post_install.sh file found"
toCompress+=("post_install.sh")
fi
if [ -f post_remove.sh ]; then
echo "post_remove.sh file found"
toCompress+=("post_remove.sh")
fi
if [ -d source-files ]; then
echo "source-files/ directory found"
toCompress+=("source-files/")
fi
else else
echo "files/ directory or source.sh file not found in $PWD" echo "files/ directory or source.sh file not found in $PWD"
exit 1 exit 1
@@ -35,10 +62,16 @@ else
exit 1 exit 1
fi fi
echo "Creating $type package as $output.bpm" echo "Creating $type package as $output"
if [[ "$type" == "binary" ]]; then tar -czf "$output" "${toCompress[@]}"
tar -czf "$output".bpm files/ pkg.info
else #if [[ "$type" == "binary" ]]; then
tar -czf "$output".bpm source.sh pkg.info # tar -czf "$output" files/ pkg.info
fi #else
# if [ -d source-files ]; then
# tar -czf "$output" source.sh source-files/ pkg.info
# else
# tar -czf "$output" source.sh pkg.info
# fi
#fi
+1 -1
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.2.0 version: 1.4.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.
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
name: bpm name: bpm
description: The Bubble Package Manager description: The Bubble Package Manager
version: 0.1.0 version: 0.1.7
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