9 Commits
Author SHA1 Message Date
EnumDev 497c94cbc0 Added a $BPM_PKG_ARCH variable to source script
Added a "keep" field to package descriptor files which will prevent the specified files from being overwritten
Completely revamped the bpm-setup script in the bpm-utils package
2024-04-23 17:27:34 +03:00
EnumDev d086b4a639 Added the ability to create pre_install.sh, post_install.sh, pre_update.sh, post_update.sh and post_remove.sh scripts 2024-04-19 17:43:14 +03:00
EnumDev 3e8b247e83 Actually fixed the bug that could brick your system 2024-04-18 19:34:20 +03:00
EnumDev c8fc1b902c Fixed major bug where deleting obsolete files would delete directories with other files inside them which could brick your system 2024-04-18 15:04:52 +03:00
EnumDev 6472a113cf Added a new subcommand that lets you view what packages a file is managed by 2024-04-18 12:14:38 +03:00
EnumDev d3f1c52202 Added environment variables that can be used during source package compilation to fetch package info (i.e $BPM_PKG_NAME or $BPM_PKG_VERSION)
Packages created using the -b flag will now contain the version number in their filename
Source install scripts will now exit if any error is encountered
Obsolete files should now be removed properly
2024-04-17 21:26:18 +03:00
EnumDev b568d4db32 Added a way to include files in a source package that can be used during the compilation process (i.e patch files) 2024-04-16 21:04:36 +03:00
EnumDev 472d21a618 - Switched to using the flag system from the flag go package
- Added the -R flag which lets you choose a destination root directory
2024-04-13 09:23:03 +03:00
EnumDev a9283037d8 - 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
2024-03-30 22:22:31 +02:00
10 changed files with 952 additions and 267 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!
+595 -126
View File
@@ -8,7 +8,6 @@ import (
"fmt" "fmt"
"io" "io"
"io/fs" "io/fs"
"log"
"os" "os"
"os/exec" "os/exec"
"path" "path"
@@ -26,6 +25,7 @@ type PackageInfo struct {
License string License string
Arch string Arch string
Type string Type string
Keep []string
Depends []string Depends []string
MakeDepends []string MakeDepends []string
Provides []string Provides []string
@@ -101,6 +101,167 @@ 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))
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", pkgInfo.Arch))
depends := make([]string, len(pkgInfo.Depends))
copy(depends, pkgInfo.Depends)
for i := 0; i < len(depends); i++ {
depends[i] = fmt.Sprintf("\"%s\"", depends[i])
}
makeDepends := make([]string, len(pkgInfo.MakeDepends))
copy(makeDepends, pkgInfo.MakeDepends)
for i := 0; i < len(makeDepends); i++ {
makeDepends[i] = fmt.Sprintf("\"%s\"", makeDepends[i])
}
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DEPENDS=(%s)", strings.Join(depends, " ")))
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_MAKE_DEPENDS=(%s)", strings.Join(makeDepends, " ")))
cmd.Env = append(cmd.Env, "BPM_PKG_TYPE=source")
err = cmd.Run()
if err != nil {
return err
}
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: "",
@@ -110,6 +271,7 @@ func ReadPackageInfo(contents string, defaultValues bool) (*PackageInfo, error)
License: "", License: "",
Arch: "", Arch: "",
Type: "", Type: "",
Keep: nil,
Depends: nil, Depends: nil,
MakeDepends: nil, MakeDepends: nil,
Provides: nil, Provides: nil,
@@ -130,10 +292,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]
@@ -143,6 +311,9 @@ func ReadPackageInfo(contents string, defaultValues bool) (*PackageInfo, error)
pkgInfo.Arch = split[1] pkgInfo.Arch = split[1]
case "type": case "type":
pkgInfo.Type = split[1] pkgInfo.Type = split[1]
case "keep":
pkgInfo.Keep = strings.Split(strings.Replace(split[1], " ", "", -1), ",")
pkgInfo.Keep = stringSliceRemoveEmpty(pkgInfo.Keep)
case "depends": case "depends":
pkgInfo.Depends = strings.Split(strings.Replace(split[1], " ", "", -1), ",") pkgInfo.Depends = strings.Split(strings.Replace(split[1], " ", "", -1), ",")
pkgInfo.Depends = stringSliceRemoveEmpty(pkgInfo.Depends) pkgInfo.Depends = stringSliceRemoveEmpty(pkgInfo.Depends)
@@ -170,7 +341,7 @@ func ReadPackageInfo(contents string, defaultValues bool) (*PackageInfo, error)
return &pkgInfo, nil return &pkgInfo, nil
} }
func CreateInfoFile(pkgInfo PackageInfo) string { func CreateInfoFile(pkgInfo PackageInfo, keepSourceFields bool) string {
ret := "" ret := ""
ret = ret + "name: " + pkgInfo.Name + "\n" ret = ret + "name: " + pkgInfo.Name + "\n"
ret = ret + "description: " + pkgInfo.Description + "\n" ret = ret + "description: " + pkgInfo.Description + "\n"
@@ -183,16 +354,22 @@ func CreateInfoFile(pkgInfo PackageInfo) string {
} }
ret = ret + "architecture: " + pkgInfo.Arch + "\n" ret = ret + "architecture: " + pkgInfo.Arch + "\n"
ret = ret + "type: " + pkgInfo.Type + "\n" ret = ret + "type: " + pkgInfo.Type + "\n"
if len(pkgInfo.Keep) > 0 {
ret = ret + "keep (" + strconv.Itoa(len(pkgInfo.Keep)) + "): " + strings.Join(pkgInfo.Keep, ",") + "\n"
}
if len(pkgInfo.Depends) > 0 { if len(pkgInfo.Depends) > 0 {
ret = ret + "depends (" + strconv.Itoa(len(pkgInfo.Depends)) + "): " + strings.Join(pkgInfo.Depends, ",") + "\n" ret = ret + "depends (" + strconv.Itoa(len(pkgInfo.Depends)) + "): " + strings.Join(pkgInfo.Depends, ",") + "\n"
} }
if len(pkgInfo.MakeDepends) > 0 && keepSourceFields {
ret = ret + "make_depends (" + strconv.Itoa(len(pkgInfo.MakeDepends)) + "): " + strings.Join(pkgInfo.MakeDepends, ",") + "\n"
}
if len(pkgInfo.Provides) > 0 { if len(pkgInfo.Provides) > 0 {
ret = ret + "provides (" + strconv.Itoa(len(pkgInfo.Provides)) + "): " + strings.Join(pkgInfo.Provides, ",") + "\n" ret = ret + "provides (" + strconv.Itoa(len(pkgInfo.Provides)) + "): " + strings.Join(pkgInfo.Provides, ",") + "\n"
} }
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,21 +382,36 @@ 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
} }
packageInstalled := IsPackageInstalled(pkgInfo.Name, installDir)
if packageInstalled {
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 {
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()
@@ -230,7 +422,8 @@ func InstallPackage(filename, installDir string, force bool) error {
return err return err
} }
if strings.HasPrefix(header.Name, "files/") && header.Name != "files/" { if strings.HasPrefix(header.Name, "files/") && header.Name != "files/" {
extractFilename := path.Join(installDir, strings.TrimPrefix(header.Name, "files/")) trimmedName := strings.TrimPrefix(header.Name, "files/")
extractFilename := path.Join(installDir, trimmedName)
switch header.Typeflag { switch header.Typeflag {
case tar.TypeDir: case tar.TypeDir:
files = append(files, strings.TrimPrefix(header.Name, "files/")) files = append(files, strings.TrimPrefix(header.Name, "files/"))
@@ -242,6 +435,13 @@ func InstallPackage(filename, installDir string, force bool) error {
fmt.Println("Creating Directory: " + extractFilename) fmt.Println("Creating Directory: " + extractFilename)
} }
case tar.TypeReg: case tar.TypeReg:
if _, err := os.Stat(extractFilename); err == nil {
if slices.Contains(pkgInfo.Keep, trimmedName) {
fmt.Println("Skipping File: " + extractFilename + "(File is configured to be kept during installs/updates)")
files = append(files, trimmedName)
continue
}
}
err := os.Remove(extractFilename) err := os.Remove(extractFilename)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return err return err
@@ -294,6 +494,16 @@ func InstallPackage(filename, installDir string, force bool) error {
} }
} }
} else if pkgInfo.Type == "source" { } else if pkgInfo.Type == "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
}
for { for {
header, err := tr.Next() header, err := tr.Next()
if err == io.EOF { if err == io.EOF {
@@ -302,102 +512,237 @@ func InstallPackage(filename, installDir string, force bool) error {
if err != nil { if err != nil {
return err 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" { if header.Name == "source.sh" {
bs, err := io.ReadAll(tr) bs, err := io.ReadAll(tr)
if err != nil { if err != nil {
return err return err
} }
temp, err := os.MkdirTemp("/var/tmp/", "bpm_source-")
fmt.Println("Creating temp directory at: " + temp)
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
} }
fmt.Println("Running source.sh file...") }
cmd := exec.Command("/usr/bin/sh", "source.sh") }
cmd.Stdin = os.Stdin if _, err := os.Stat(path.Join(temp, "source.sh")); os.IsNotExist(err) {
cmd.Stdout = os.Stdout return errors.New("source.sh file could not be found in the temporary build directory")
cmd.Stderr = os.Stderr }
cmd.Dir = temp if err != nil {
err = cmd.Run() return err
}
fmt.Println("Running source.sh file...")
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.Stdout = os.Stdout
cmd.Stderr = os.Stderr
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))
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", pkgInfo.Arch))
depends := make([]string, len(pkgInfo.Depends))
copy(depends, pkgInfo.Depends)
for i := 0; i < len(depends); i++ {
depends[i] = fmt.Sprintf("\"%s\"", depends[i])
}
makeDepends := make([]string, len(pkgInfo.MakeDepends))
copy(makeDepends, pkgInfo.MakeDepends)
for i := 0; i < len(makeDepends); i++ {
makeDepends[i] = fmt.Sprintf("\"%s\"", makeDepends[i])
}
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DEPENDS=(%s)", strings.Join(depends, " ")))
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_MAKE_DEPENDS=(%s)", strings.Join(makeDepends, " ")))
cmd.Env = append(cmd.Env, "BPM_PKG_TYPE=source")
err = cmd.Run()
if err != nil {
return err
}
if _, err := os.Stat(path.Join(temp, "/output/")); err != nil {
if os.IsNotExist(err) {
return errors.New("Output directory not be found at " + path.Join(temp, "/output/"))
}
return err
}
fmt.Println("Copying all files...")
err = filepath.WalkDir(path.Join(temp, "/output/"), func(fullpath string, d fs.DirEntry, err error) error {
relFilename, err := filepath.Rel(path.Join(temp, "/output/"), fullpath)
if relFilename == "." {
return nil
}
extractFilename := path.Join(installDir, relFilename)
if err != nil {
return err
}
if d.Type() == os.ModeDir {
files = append(files, relFilename+"/")
if err := os.Mkdir(extractFilename, 0755); err != nil {
if !os.IsExist(err) {
return err
}
} else {
fmt.Println("Creating Directory: " + extractFilename)
}
} else if d.Type().IsRegular() {
if _, err := os.Stat(extractFilename); err == nil {
if slices.Contains(pkgInfo.Keep, relFilename) {
fmt.Println("Skipping File: " + extractFilename + "(File is configured to be kept during installs/updates)")
files = append(files, relFilename)
return nil
}
}
err := os.Remove(extractFilename)
if err != nil && !os.IsNotExist(err) {
return err
}
outFile, err := os.Create(extractFilename)
fmt.Println("Creating File: " + extractFilename)
files = append(files, relFilename)
if err != nil { if err != nil {
return err return err
} }
if _, err := os.Stat(path.Join(temp, "/output/")); err != nil { f, err := os.Open(fullpath)
if os.IsNotExist(err) { if err != nil {
return errors.New("Output directory not be found at " + path.Join(temp, "/output/"))
}
return err return err
} }
fmt.Println("Copying all files...") if _, err := io.Copy(outFile, f); err != nil {
err = filepath.WalkDir(path.Join(temp, "/output/"), func(fullpath string, d fs.DirEntry, err error) error { return err
relFilename, err := filepath.Rel(path.Join(temp, "/output/"), fullpath) }
if relFilename == "." { info, err := os.Stat(fullpath)
return nil if err != nil {
} return err
extractFilename := path.Join(installDir, relFilename) }
if err != nil { if err := os.Chmod(extractFilename, info.Mode()); err != nil {
return err return err
} }
if d.Type() == os.ModeDir { err = outFile.Close()
files = append(files, relFilename+"/") if err != nil {
if err := os.Mkdir(extractFilename, 0755); err != nil { return err
if !os.IsExist(err) { }
return err err = f.Close()
} if err != nil {
} else { return err
fmt.Println("Creating Directory: " + extractFilename) }
} } else if d.Type() == os.ModeSymlink {
} else if d.Type().IsRegular() { link, err := os.Readlink(fullpath)
outFile, err := os.Create(extractFilename) if err != nil {
fmt.Println("Creating File: " + extractFilename) return err
files = append(files, relFilename) }
if err != nil { err = os.Remove(extractFilename)
return err if err != nil && !os.IsNotExist(err) {
} return err
f, err := os.Open(fullpath) }
if err != nil { fmt.Println("Creating Symlink: "+extractFilename, " -> "+link)
return err files = append(files, relFilename)
} err = os.Symlink(link, extractFilename)
if _, err := io.Copy(outFile, f); err != nil {
return err
}
info, err := os.Stat(fullpath)
if err != nil {
return err
}
if err := os.Chmod(extractFilename, info.Mode()); err != nil {
return err
}
err = outFile.Close()
if err != nil {
return err
}
err = f.Close()
if err != nil {
return err
}
} else if d.Type() == os.ModeSymlink {
link, err := os.Readlink(fullpath)
if err != nil {
return err
}
fmt.Println("Creating Symlink: "+extractFilename, " -> "+link)
files = append(files, relFilename)
err = os.Symlink(link, extractFilename)
if err != nil {
return err
}
}
return nil
})
if err != nil { if err != nil {
return err return err
} }
} }
return nil
})
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, false)), 0644)
if err != nil {
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))
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.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"))
for key := range scripts {
err = os.Remove(path.Join(compiledDir, key))
if err != nil {
return err
}
}
if err != nil {
return err
}
}
if !keepTempDir {
err := os.RemoveAll(temp)
if err != nil {
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)
@@ -405,6 +750,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 {
@@ -452,6 +801,25 @@ func InstallPackage(filename, installDir string, force bool) error {
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
@@ -460,6 +828,81 @@ 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")
var symlinks []string
for _, f := range filesDiff {
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
} }
@@ -677,10 +1120,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
} }
@@ -707,6 +1166,57 @@ 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))
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", pkgInfo.Arch))
depends := make([]string, len(pkgInfo.Depends))
copy(depends, pkgInfo.Depends)
for i := 0; i < len(depends); i++ {
depends[i] = fmt.Sprintf("\"%s\"", depends[i])
}
makeDepends := make([]string, len(pkgInfo.MakeDepends))
copy(makeDepends, pkgInfo.MakeDepends)
for i := 0; i < len(makeDepends); i++ {
makeDepends[i] = fmt.Sprintf("\"%s\"", makeDepends[i])
}
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DEPENDS=(%s)", strings.Join(depends, " ")))
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_MAKE_DEPENDS=(%s)", strings.Join(makeDepends, " ")))
cmd.Env = append(cmd.Env, "BPM_PKG_TYPE=source")
err = cmd.Run()
if err != nil {
return err
}
}
err := os.RemoveAll(pkgDir) err := os.RemoveAll(pkgDir)
if err != nil { if err != nil {
return err return err
@@ -714,44 +1224,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
}
+191 -103
View File
@@ -3,12 +3,12 @@ package main
import ( import (
"bufio" "bufio"
"capcreepergr.me/bpm/bpm_utils" "capcreepergr.me/bpm/bpm_utils"
"flag"
"fmt" "fmt"
"log" "log"
"os" "os"
"path" "path/filepath"
"slices" "slices"
"strconv"
"strings" "strings"
) )
@@ -17,33 +17,25 @@ import (
/* A simple-to-use package manager */ /* A simple-to-use package manager */
/* ---------------------------------- */ /* ---------------------------------- */
var bpmVer = "0.0.9" var bpmVer = "0.2.0"
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")
}
}
if os.Getuid() != 0 {
fmt.Println("BPM needs to be run with superuser permissions")
os.Exit(0)
}
resolveCommand() resolveCommand()
} }
func getArgs() []string {
return os.Args[1:]
}
type commandType uint8 type commandType uint8
const ( const (
@@ -53,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":
@@ -72,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,13 +84,12 @@ func resolveCommand() {
fmt.Printf("Package (%s) could not be found\n", pkg) fmt.Printf("Package (%s) could not be found\n", pkg)
continue continue
} }
fmt.Print("----------------\n" + bpm_utils.CreateInfoFile(*info)) fmt.Print("----------------\n" + bpm_utils.CreateInfoFile(*info, true))
if n == len(packages)-1 { if n == len(packages)-1 {
fmt.Println("----------------") fmt.Println("----------------")
} }
} }
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())
@@ -115,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)
} }
@@ -128,15 +112,18 @@ func resolveCommand() {
fmt.Printf("Package (%s) could not be found\n", pkg) fmt.Printf("Package (%s) could not be found\n", pkg)
continue continue
} }
fmt.Print("----------------\n" + bpm_utils.CreateInfoFile(*info)) fmt.Print("----------------\n" + bpm_utils.CreateInfoFile(*info, true))
if n == len(packages)-1 { if n == len(packages)-1 {
fmt.Println("----------------") fmt.Println("----------------")
} }
} }
} }
case install: case install:
flags, i := resolveFlags() if os.Getuid() != 0 {
files := getArgs()[1+i:] fmt.Println("This subcommand needs to be run with superuser permissions")
os.Exit(0)
}
files := subcommandArgs
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
@@ -146,14 +133,14 @@ func resolveCommand() {
if err != nil { if err != nil {
log.Fatalf("Could not read package\nError: %s\n", err) log.Fatalf("Could not read package\nError: %s\n", err)
} }
fmt.Print("----------------\n" + bpm_utils.CreateInfoFile(*pkgInfo)) fmt.Print("----------------\n" + bpm_utils.CreateInfoFile(*pkgInfo, true))
fmt.Println("----------------") fmt.Println("----------------")
verb := "install" verb := "install"
if pkgInfo.Type == "source" { if pkgInfo.Type == "source" {
verb = "build" verb = "build"
} }
if !slices.Contains(flags, "f") { if !forceInstall {
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
} }
@@ -162,13 +149,16 @@ func resolveCommand() {
continue continue
} }
if pkgInfo.Type == "source" { if pkgInfo.Type == "source" {
if unresolved := bpm_utils.CheckMakeDependencies(pkgInfo, rootDir); len(unresolved) != 0 { if unresolved := bpm_utils.CheckMakeDependencies(pkgInfo, "/"); len(unresolved) != 0 {
fmt.Printf("skipping... cannot %s package (%s) due to missing make dependencies: %s\n", verb, pkgInfo.Name, strings.Join(unresolved, ", ")) fmt.Printf("skipping... cannot %s package (%s) due to missing make dependencies: %s\n", verb, pkgInfo.Name, strings.Join(unresolved, ", "))
continue continue
} }
} }
} }
if !slices.Contains(flags, "y") { if rootDir != "/" {
fmt.Println("Warning: Operating in " + 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] ")
@@ -184,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 + ")")
@@ -203,11 +193,7 @@ func resolveCommand() {
continue continue
} }
} }
err := bpm_utils.RemovePackage(pkgInfo.Name, rootDir) } else if !yesAll {
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) 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')
@@ -217,21 +203,24 @@ func resolveCommand() {
} }
} }
err = bpm_utils.InstallPackage(file, rootDir, slices.Contains(flags, "f")) err = bpm_utils.InstallPackage(file, rootDir, forceInstall, buildSource, keepTempDir)
if err != nil { if err != nil {
if pkgInfo.Type == "source" { if pkgInfo.Type == "source" && keepTempDir {
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" && 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 **")
} }
} }
case remove: case remove:
flags, i := resolveFlags() if os.Getuid() != 0 {
packages := getArgs()[1+i:] fmt.Println("This subcommand needs to be run with superuser permissions")
os.Exit(0)
}
packages := subcommandArgs
if len(packages) == 0 { if len(packages) == 0 {
fmt.Println("No packages were given") fmt.Println("No packages were given")
return return
@@ -242,9 +231,12 @@ func resolveCommand() {
fmt.Printf("Package (%s) could not be found\n", pkg) fmt.Printf("Package (%s) could not be found\n", pkg)
continue continue
} }
fmt.Print("----------------\n" + bpm_utils.CreateInfoFile(*pkgInfo)) fmt.Print("----------------\n" + bpm_utils.CreateInfoFile(*pkgInfo, true))
fmt.Println("----------------") fmt.Println("----------------")
if !slices.Contains(flags, "y") { if rootDir != "/" {
fmt.Println("Warning: Operating in " + 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')
@@ -260,53 +252,149 @@ 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:
fmt.Println("\033[1m------Help------\033[0m") printHelp()
fmt.Println("\033[1m\\ Command Format /\033[0m")
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("\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] <files...> | installs the following files. -y skips the confirmation prompt. -f skips dependency and architecture checking")
fmt.Println("-> bpm remove [-y] <packages...> | removes the following packages. -y skips the confirmation prompt")
fmt.Println("-> bpm cleanup | removes all unneeded dependencies")
fmt.Println("\033[1m----------------\033[0m")
} }
} }
func resolveFlags() ([]string, int) { func printHelp() {
flags := getArgs()[1:] fmt.Println("\033[1m---- Command Format ----\033[0m")
var ret []string fmt.Println("-> command format: bpm <subcommand> [-flags]...")
for _, flag := range flags { fmt.Println("-> flags will be read if passed right after the subcommand otherwise they will be read as subcommand arguments")
if strings.HasPrefix(flag, "-") { fmt.Println("\033[1m---- Command List ----\033[0m")
f := strings.TrimPrefix(flag, "-") fmt.Println("-> bpm version | shows information on the installed version of bpm")
switch getCommandType() { fmt.Println("-> bpm info [-R] | shows information on an installed package")
default: fmt.Println(" -R=<root_path> lets you define the root path which will be used")
log.Fatalf("Invalid flag " + flag) fmt.Println("-> bpm list [-R, -n, -l] | lists all installed packages")
case list: fmt.Println(" -R=<root_path> lets you define the root path which will be used")
v := [...]string{"l", "n"} fmt.Println(" -n shows the number of packages")
if !slices.Contains(v[:], f) { fmt.Println(" -l lists package names only")
log.Fatalf("Invalid flag " + flag) 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")
ret = append(ret, f) fmt.Println(" -y skips the confirmation prompt")
case install: fmt.Println(" -f skips dependency and architecture checking")
v := [...]string{"y", "f"} fmt.Println(" -b creates a binary package for a source package after compilation and saves it in /var/lib/bpm/compiled")
if !slices.Contains(v[:], f) { fmt.Println(" -k keeps the temp directory created by BPM after source package installation")
log.Fatalf("Invalid flag " + flag) 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")
ret = append(ret, f) fmt.Println(" -y skips the confirmation prompt")
case remove: fmt.Println("-> bpm file [-R] <files...> | shows what packages the following packages are managed by")
v := [...]string{"y"} fmt.Println(" -R=<root_path> lets you define the root path which will be used")
if !slices.Contains(v[:], f) { fmt.Println("\033[1m----------------\033[0m")
log.Fatalf("Invalid flag " + flag) }
}
ret = append(ret, f) func resolveFlags() {
// List flags
listFlagSet := flag.NewFlagSet("List flags", flag.ExitOnError)
listFlagSet.Usage = printHelp
listFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
listFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
listFlagSet.BoolVar(&pkgListNumbers, "n", false, "List the number of all packages installed with BPM")
listFlagSet.BoolVar(&pkgListNames, "l", false, "List the names of all packages installed with BPM")
// Info flags
infoFlagSet := flag.NewFlagSet("Info flags", flag.ExitOnError)
infoFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
infoFlagSet.Usage = printHelp
// Install flags
installFlagSet := flag.NewFlagSet("Install flags", flag.ExitOnError)
installFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
installFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
installFlagSet.BoolVar(&buildSource, "b", false, "Build binary package from source package")
installFlagSet.BoolVar(&keepTempDir, "k", false, "Keep temporary directory after source compilation")
installFlagSet.BoolVar(&forceInstall, "f", false, "Force installation by skipping architecture and dependency resolution")
installFlagSet.Usage = printHelp
// Remove flags
removeFlagSet := flag.NewFlagSet("Remove flags", flag.ExitOnError)
removeFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
removeFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
removeFlagSet.Usage = printHelp
// File flags
// 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 {
subcommand = os.Args[1]
subcommandArgs = os.Args[2:]
if getCommandType() == list {
err := listFlagSet.Parse(subcommandArgs)
if err != nil {
return
} }
} else { subcommandArgs = listFlagSet.Args()
break } 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.
@@ -1,28 +0,0 @@
#!/bin/bash
if [ $# -eq 0 ]
then
echo "No output package name given!"
exit 1
fi
output=$1
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
fi
if [ -f pkg.info ]; then
echo "pkg.info file found"
else
echo "pkg.info file not found in $PWD"
exit 1
fi
echo "Creating $output.bpm package..."
tar -czf $output.bpm files/ pkg.info
+154
View File
@@ -0,0 +1,154 @@
#!/bin/bash
usage () {
echo "------BPM-Setup options------"
echo "bpm-setup -D <directory> | Path to package directory"
echo "bpm-setup -y | Skips confirmation prompt"
echo "bpm-setup -n <name> | Set the package name (Defaults to \"package-name\")"
echo "bpm-setup -d <description> | Set the package description (Defaults to \"Default package description\")"
echo "bpm-setup -v <version> | Set the package version (Defaults to \"1.0\")"
echo "bpm-setup -u <url> | Set the package URL (Optional)"
echo "bpm-setup -l <licenses> | Set the package licenses (Optional)"
echo "bpm-setup -t <binary/source> | Set the package type to binary or source (Defaults to binary)"
echo "bpm-setup -s <source template file> | Use a default template file (Defaults to /etc/bpm-utils/source.default)"
}
if [ $# -eq 0 ]; then
usage
exit
fi
NAME="package-name"
DESCRIPTION="Default package description"
VERSION="1.0"
#URL="https://my.project.url/ (Optional)"
#LICENSE="Your project's license (Optional)"
TYPE="binary"
SOURCE_FILE="/etc/bpm-utils/source.default"
while getopts "D:n:d:v:u:l:t:s:y" o; do
case "${o}" in
D)
DIRECTORY="${OPTARG}"
;;
y)
CONFIRM=yes
;;
n)
NAME="${OPTARG}"
;;
d)
DESCRIPTION="${OPTARG}"
;;
v)
VERSION="${OPTARG}"
;;
u)
URL="${OPTARG}"
;;
l)
LICENSE="${OPTARG}"
;;
t)
TYPE="${OPTARG}"
;;
s)
SOURCE_FILE="$(realpath ${OPTARG})"
;;
*)
usage
exit 1
;;
esac
done
if [ -z "${DIRECTORY}" ]; then
echo "Required directory argument missing. Try 'bpm-setup -D <directory> [other options...]"
exit 1
fi
if [[ ! "${DIRECTORY}" == "/"* ]]; then
DIRECTORY="${PWD}/${DIRECTORY}"
fi
if [ -e "${DIRECTORY}" ]; then
echo "This path already exists"
exit 1
fi
if [[ "$TYPE" != "binary" ]] && [[ "$TYPE" != "source" ]]; then
echo "Invalid package type! Package type must be either 'binary' or 'source'"
exit 1
fi
if [ -z "${CONFIRM}" ]; then
echo "Setting up package working directory at ${DIRECTORY} with the following information:"
echo "Package name: $NAME"
echo "Package description: $DESCRIPTION"
echo "Package version: $VERSION"
if [ -z "${URL}" ]; then
echo "Package URL: Not set"
else
echo "Package URL: $URL"
fi
if [ -z "${LICENSE}" ]; then
echo "Package license: Not set"
else
echo "Package license: $LICENSE"
fi
echo "Package type: $TYPE"
read -p "Create package directory? [y/N]: " CREATE
case $CREATE in
[Yy]* )
;;
*)
echo "Exiting bpm-setup..."
exit
;;
esac
fi
if ! mkdir -pv $DIRECTORY; then
echo "Could not create $DIRECTORY!"
exit 1
fi
cd $DIRECTORY
touch pkg.info
echo "name: ${NAME}" >> pkg.info
echo "description: ${DESCRIPTION}" >> pkg.info
echo "version: ${VERSION}" >> pkg.info
if [ ! -z "${URL}" ]; then echo "url: ${URL}" >> pkg.info; fi
if [ ! -z "${LICENSE}" ]; then echo "license: ${LICENSE}" >> pkg.info; fi
if [[ "$TYPE" == "binary" ]]; then
echo "architecture: $(uname -m)" >> pkg.info
echo "type: binary" >> pkg.info
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
echo "architecture: any" >> pkg.info
echo "type: source" >> pkg.info
mkdir -pv source-files
if [ -f "${SOURCE_FILE}" ]; then
touch source.temp
cat "${SOURCE_FILE}" > source.temp
export NAME DESCRIPTION VERSION URL LICENSE TYPE
envsubst '$NAME:$DESCRIPTION:$VERSION:$URL:$LICENSE:$TYPE' < source.temp > source.sh
rm source.temp
else
echo "Source file at ${SOURCE_FILE} does not exist! Creating empty source.sh instead..."
touch source.sh
fi
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 properly create your compilation script"
echo "You can add additional files that will be used during compilation to the 'source-files' directory"
echo "You can turn your package into a .bpm file use the 'bpm-package <filename>' command"
fi
+4 -3
View File
@@ -1,7 +1,8 @@
name: bpm-utils name: bpm-utils
description: Utilities to create BPM packages description: Utilities to create BPM packages
version: 1.0.0 version: 2.0.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: any
type: binary keep: etc/bpm-utils/source.default
type: binary
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.0.9 version: 0.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