mirror of
https://github.com/EnumeratedDev/bpm.git
synced 2026-09-16 10:36:12 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45c536b326
|
||
|
|
705aec8e6e
|
||
|
|
34fb7ce1d6
|
||
|
|
fee0e6a96f
|
||
|
|
c5f48becf8
|
||
|
|
ac1ba2ada3
|
||
|
|
5fb8211933
|
||
|
|
4110ad9c5c
|
||
|
|
dcff59d560
|
||
|
|
1594eec5ce
|
||
|
|
0c30d85589
|
||
|
|
768c75c9bd
|
||
|
|
abc6674b97
|
||
|
|
1d858076fd
|
||
|
|
5909370407
|
||
|
|
5a78fd3002
|
||
|
|
a7c03c272e
|
||
|
|
8ba209cf35
|
||
|
|
6f27f39065
|
||
|
|
91479e58f1
|
||
|
|
b6d055a38d
|
||
|
|
0c754a7e0f
|
||
|
|
eeb9a01e31
|
||
|
|
53e08ce7f9
|
||
|
|
6f124e0586
|
||
|
|
93921d4093
|
||
|
|
bc89db8cef
|
||
|
|
ea6d722fa8
|
||
|
|
264a0f87ba
|
||
|
|
926b0e35fc
|
||
|
|
a4d1365e55 | ||
|
|
bc3fe6fd7e |
@@ -12,7 +12,7 @@ ROOT_COMPILATION_GID ?= 65534
|
||||
|
||||
build:
|
||||
mkdir -p build
|
||||
cd src/bpm; $(GO) build -ldflags "-w -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationUID=$(ROOT_COMPILATION_UID)' -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationGID=$(ROOT_COMPILATION_GID)'" -o ../../build/bpm git.enumerated.dev/bubble-package-manager/bpm/src/bpm
|
||||
cd src/bpm; $(GO) build $(GOFLAGS) -ldflags "-w -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationUID=$(ROOT_COMPILATION_UID)' -X 'git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib.rootCompilationGID=$(ROOT_COMPILATION_GID)'" -o ../../build/bpm git.enumerated.dev/bubble-package-manager/bpm/src/bpm
|
||||
|
||||
install: build/bpm config/
|
||||
# Create directory
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
ignore_packages: []
|
||||
show_source_package_contents: always
|
||||
cleanup_make_dependencies: true
|
||||
databases:
|
||||
- name: example-database
|
||||
|
||||
+6
-1
@@ -4,11 +4,16 @@ go 1.23
|
||||
|
||||
toolchain go1.23.7
|
||||
|
||||
require git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib v0.5.0
|
||||
require (
|
||||
git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib v0.5.0
|
||||
github.com/adrg/strutil v0.3.1
|
||||
github.com/spf13/pflag v1.0.10
|
||||
)
|
||||
|
||||
replace git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib => ../bpmlib
|
||||
|
||||
require (
|
||||
github.com/drone/envsubst v1.0.3 // indirect
|
||||
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,6 +1,27 @@
|
||||
github.com/adrg/strutil v0.3.1 h1:OLvSS7CSJO8lBii4YmBt8jiK9QOtB9CzCzwl4Ic/Fz4=
|
||||
github.com/adrg/strutil v0.3.1/go.mod h1:8h90y18QLrs11IBffcGX3NW/GFBXCMcNg4M7H6MspPA=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g=
|
||||
github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f h1:xt29M2T6STgldg+WEP51gGePQCsQvklmP2eIhPIBK3g=
|
||||
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
+769
-488
File diff suppressed because it is too large
Load Diff
+276
-69
@@ -1,15 +1,20 @@
|
||||
package bpmlib
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/drone/envsubst"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -31,12 +36,6 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
|
||||
return nil, errors.New("cannot compile a non-source package")
|
||||
}
|
||||
|
||||
// Read compilation options file in current directory
|
||||
compilationOptions, err := readCompilationOptionsFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get HOME directory
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
@@ -122,21 +121,21 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Download files
|
||||
err = downloadPackageFiles(bpmpkg.PkgInfo, tempDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Setup environment for commands
|
||||
env := os.Environ()
|
||||
env = append(env, "HOME="+tempDirectory)
|
||||
env = append(env, "BPM_WORKDIR="+tempDirectory)
|
||||
env = append(env, "BPM_SOURCE="+path.Join(tempDirectory, "source"))
|
||||
env = append(env, "BPM_OUTPUT="+path.Join(tempDirectory, "output"))
|
||||
env = append(env, "BPM_PKG_NAME="+bpmpkg.PkgInfo.Name)
|
||||
env = append(env, "BPM_PKG_VERSION="+bpmpkg.PkgInfo.Version)
|
||||
env = append(env, "BPM_PKG_REVISION="+strconv.Itoa(bpmpkg.PkgInfo.Revision))
|
||||
// Check for architecture override in compilation options
|
||||
if val, ok := compilationOptions["ARCH"]; ok {
|
||||
env = append(env, "BPM_PKG_ARCH="+val)
|
||||
} else {
|
||||
env = append(env, "BPM_PKG_ARCH="+GetArch())
|
||||
}
|
||||
env = append(env, "BPM_PKG_ARCH="+bpmpkg.PkgInfo.OutputArch)
|
||||
env = append(env, CompilationBPMConfig.CompilationEnvironment...)
|
||||
|
||||
// Execute prepare and build functions in source.sh script
|
||||
@@ -188,6 +187,21 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
|
||||
packagesToCompile = append(packagesToCompile, bpmpkg.PkgInfo)
|
||||
}
|
||||
|
||||
// Create output directories for each package
|
||||
for _, pkg := range packagesToCompile {
|
||||
// Create new output directory
|
||||
err = os.Mkdir(path.Join(tempDirectory, "output_"+pkg.Name), 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Change output directory owner
|
||||
err = os.Chown(path.Join(tempDirectory, "output_"+pkg.Name), uid, gid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Compile each package
|
||||
for _, pkg := range packagesToCompile {
|
||||
// Get package function name
|
||||
@@ -196,26 +210,6 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
|
||||
packageFunctionName = "package_" + pkg.Name
|
||||
}
|
||||
|
||||
// Remove output directory if it already exists
|
||||
if _, err := os.Stat(path.Join(tempDirectory, "output")); err == nil {
|
||||
err := os.RemoveAll(path.Join(tempDirectory, "output"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Create new output directory
|
||||
err = os.Mkdir(path.Join(tempDirectory, "output"), 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Change output directory owner
|
||||
err = os.Chown(path.Join(tempDirectory, "output"), uid, gid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Execute package function in source.sh script and generate package file list
|
||||
cmd = exec.Command("bash", "-c",
|
||||
"set -a\n"+ // Source and export functions and variables in source.sh script
|
||||
@@ -224,10 +218,11 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
|
||||
"echo \"Running "+packageFunctionName+"() function\"\n"+
|
||||
"( cd \"$BPM_SOURCE\" && fakeroot -s \"$BPM_WORKDIR\"/fakeroot_file bash -e -c '"+packageFunctionName+"' ) || exit 1\n"+ // Run package() function
|
||||
"fakeroot -i \"$BPM_WORKDIR\"/fakeroot_file find \"$BPM_OUTPUT\" -mindepth 1 -printf \"%P %#m %U %G %s\\n\" > \"$BPM_WORKDIR\"/pkg.files") // Create package file list
|
||||
|
||||
cmd.Dir = tempDirectory
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Env = env
|
||||
cmd.Env = append(env, "BPM_OUTPUT="+path.Join(tempDirectory, "output_"+pkg.Name))
|
||||
if os.Getuid() == 0 {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
|
||||
@@ -238,7 +233,7 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
|
||||
}
|
||||
|
||||
// Create gzip-compressed archive for the package files
|
||||
cmd = exec.Command("bash", "-c", "find output -printf \"%P\\n\" | fakeroot -i \"$BPM_WORKDIR\"/fakeroot_file tar -czf files.tar.gz --no-recursion -C output -T -")
|
||||
cmd = exec.Command("bash", "-c", fmt.Sprintf("find %s -printf \"%%P\\n\" | fakeroot -i %s/fakeroot_file tar -czf files.tar.gz --no-recursion -C %s -T -", "output_"+pkg.Name, tempDirectory, "output_"+pkg.Name))
|
||||
cmd.Dir = tempDirectory
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
@@ -259,14 +254,12 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
|
||||
pkgInfo.Type = "binary"
|
||||
|
||||
// Set package architecture
|
||||
if val, ok := compilationOptions["ARCH"]; ok {
|
||||
pkgInfo.Arch = val
|
||||
} else {
|
||||
pkgInfo.Arch = GetArch()
|
||||
}
|
||||
pkgInfo.Arch = pkg.OutputArch
|
||||
pkgInfo.OutputArch = ""
|
||||
|
||||
// Remove split package field
|
||||
// Remove split package and downloads fields
|
||||
pkgInfo.SplitPackages = nil
|
||||
pkgInfo.Downloads = nil
|
||||
|
||||
// Marshal package info
|
||||
pkgInfoBytes, err := yaml.Marshal(pkgInfo)
|
||||
@@ -332,52 +325,266 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Remove output directory
|
||||
err = os.RemoveAll(path.Join(tempDirectory, "output_"+pkg.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputBpmPackages[pkgInfo.Name] = outputFilename
|
||||
}
|
||||
|
||||
return outputBpmPackages, nil
|
||||
}
|
||||
|
||||
func readCompilationOptionsFile() (options map[string]string, err error) {
|
||||
// Initialize options map
|
||||
options = make(map[string]string)
|
||||
func downloadPackageFiles(pkgInfo *PackageInfo, tempDirectory string) error {
|
||||
for _, download := range pkgInfo.Downloads {
|
||||
// Replace variables
|
||||
replaceVars := func(s string) string {
|
||||
switch s {
|
||||
case "BPM_PKG_VERSION":
|
||||
return pkgInfo.Version
|
||||
case "BPM_PKG_NAME":
|
||||
return pkgInfo.Name
|
||||
case "BPM_SOURCE":
|
||||
return path.Join(tempDirectory, "source/")
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// Check if file compilation options file exists
|
||||
stat, err := os.Stat(".compilation-options")
|
||||
downloadUrl, err := envsubst.Eval(strings.TrimSpace(download.Url), replaceVars)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure it is a regular file
|
||||
if !stat.Mode().IsRegular() {
|
||||
return nil, fmt.Errorf("%s is not a regular file", stat.Name())
|
||||
}
|
||||
|
||||
// Read file data
|
||||
data, err := os.ReadFile(stat.Name())
|
||||
extractTo, err := envsubst.Eval(strings.TrimSpace(download.ExtractTo), replaceVars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
cloneTo, err := envsubst.Eval(strings.TrimSpace(download.CloneTo), replaceVars)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
// Trim line
|
||||
line = strings.TrimSpace(line)
|
||||
// Make relative paths absolute
|
||||
if extractTo != "" && extractTo[0] != '/' {
|
||||
extractTo = path.Join(tempDirectory, extractTo)
|
||||
}
|
||||
// Make relative paths absolute
|
||||
if cloneTo != "" && cloneTo[0] != '/' {
|
||||
cloneTo = path.Join(tempDirectory, cloneTo)
|
||||
}
|
||||
|
||||
// Skip empty lines
|
||||
if line == "" {
|
||||
switch download.Type {
|
||||
case "", "file":
|
||||
filepath := path.Join(tempDirectory, path.Base(downloadUrl))
|
||||
if download.Filepath != "" && download.Filepath[0] != '/' {
|
||||
filepath = path.Join(tempDirectory, download.Filepath)
|
||||
}
|
||||
|
||||
err := downloadFile(downloadUrl, filepath, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if download.Checksum != "skip" {
|
||||
f, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
h := sha256.New()
|
||||
_, err = io.Copy(h, f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hex.EncodeToString(h.Sum(nil)) != download.Checksum {
|
||||
fmt.Printf("Downloaded file checksum: %x\n", h.Sum(nil))
|
||||
return fmt.Errorf("downloaded file checksums did not match")
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Skipping checksum checking...")
|
||||
}
|
||||
|
||||
if !download.NoExtract && (strings.Contains(filepath, ".tar") || strings.HasSuffix(filepath, ".tgz")) {
|
||||
cmd := exec.Command("tar", "xvf", filepath, "--strip-components="+strconv.Itoa(download.ExtractStripComponents))
|
||||
cmd.Dir = tempDirectory
|
||||
if extractTo != "" {
|
||||
err := os.MkdirAll(extractTo, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Args = append(cmd.Args, "-C", extractTo)
|
||||
}
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if !download.NoExtract && strings.HasSuffix(filepath, ".zip") {
|
||||
cmd := exec.Command("unzip", filepath)
|
||||
if extractTo != "" {
|
||||
err := os.MkdirAll(extractTo, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Args = append(cmd.Args, "-d", extractTo)
|
||||
} else {
|
||||
err := os.Mkdir(path.Join(tempDirectory, strings.TrimSuffix(path.Base(filepath), ".zip")), 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Args = append(cmd.Args, "-d", path.Join(tempDirectory, strings.TrimSuffix(path.Base(filepath), ".zip")))
|
||||
}
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case "git":
|
||||
// Replace variables in git branch
|
||||
gitBranch := download.GitBranch
|
||||
gitBranch, err := envsubst.Eval(gitBranch, func(s string) string {
|
||||
switch s {
|
||||
case "BPM_PKG_VERSION":
|
||||
return pkgInfo.Version
|
||||
case "BPM_PKG_NAME":
|
||||
return pkgInfo.Name
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command("git", "clone", "--depth=1", downloadUrl)
|
||||
cmd.Dir = tempDirectory
|
||||
if gitBranch != "" {
|
||||
cmd.Args = slices.Insert(cmd.Args, len(cmd.Args)-1, "--branch="+gitBranch)
|
||||
}
|
||||
if cloneTo != "" {
|
||||
err := os.MkdirAll(cloneTo, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Args = append(cmd.Args, cloneTo)
|
||||
}
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if download.Checksum != "skip" {
|
||||
cmd := exec.Command("git", "rev-parse", "HEAD")
|
||||
if cloneTo != "" {
|
||||
cmd.Dir = cloneTo
|
||||
} else {
|
||||
cmd.Dir = path.Join(tempDirectory, strings.TrimSuffix(path.Base(downloadUrl), ".git"))
|
||||
}
|
||||
|
||||
branchChecksum, err := cmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.TrimSpace(string(branchChecksum)) != download.Checksum {
|
||||
fmt.Printf("Git branch checksum: %s\n", strings.TrimSpace(string(branchChecksum)))
|
||||
return fmt.Errorf("Cloned git repository checksum did not match")
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Skipping checksum checking...")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown download type (%s)", download.Type)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func showPackageFiles(archiveFilename string) error {
|
||||
// Read BPM archive
|
||||
bpmpkg, err := ReadPackage(archiveFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure package type is 'source'
|
||||
if bpmpkg.PkgInfo.Type != "source" {
|
||||
return fmt.Errorf("cannot compile a non-source package")
|
||||
}
|
||||
|
||||
printTarballContent := func(filename string) error {
|
||||
// Get tarball reader for file
|
||||
reader, err := readTarballFile(archiveFilename, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read file content
|
||||
data, err := io.ReadAll(reader.tarReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Print file name and content
|
||||
fmt.Println("==============================")
|
||||
fmt.Printf("%s: %s\n", bpmpkg.PkgInfo.Name, filename)
|
||||
fmt.Println("==============================")
|
||||
fmt.Printf("%s\n", data)
|
||||
|
||||
// Close reader
|
||||
err = reader.file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Print pkg.info content
|
||||
err = printTarballContent("pkg.info")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Print source files contents
|
||||
files, err := listTarballContent(archiveFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range files {
|
||||
if !strings.HasPrefix(file, "source-files/") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Split line
|
||||
split := strings.SplitN(line, "=", 2)
|
||||
|
||||
// Throw error if line isn't valid
|
||||
if len(split) < 2 {
|
||||
return nil, fmt.Errorf("invalid line in compilation-options file: '%s'", line)
|
||||
// Print source file content
|
||||
err = printTarballContent(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
options[split[0]] = split[1]
|
||||
// Print source.sh content
|
||||
err = printTarballContent("source.sh")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return options, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,8 +8,15 @@ import (
|
||||
|
||||
type MainBPMConfigStruct struct {
|
||||
IgnorePackages []string `yaml:"ignore_packages"`
|
||||
ShowSourcePackageContents string `yaml:"show_source_package_contents"`
|
||||
CleanupMakeDependencies bool `yaml:"cleanup_make_dependencies"`
|
||||
Databases []*BPMDatabase `yaml:"databases"`
|
||||
Databases []configDatabase `yaml:"databases"`
|
||||
}
|
||||
|
||||
type configDatabase struct {
|
||||
Name string `yaml:"name"`
|
||||
Source string `yaml:"source"`
|
||||
Disabled *bool `yaml:"disabled"`
|
||||
}
|
||||
|
||||
type CompilationBPMConfigStruct struct {
|
||||
@@ -25,6 +32,7 @@ func ReadConfig() (err error) {
|
||||
|
||||
// Set default config options
|
||||
MainBPMConfig = MainBPMConfigStruct{
|
||||
ShowSourcePackageContents: "always",
|
||||
CleanupMakeDependencies: true,
|
||||
}
|
||||
|
||||
|
||||
+49
-82
@@ -3,38 +3,38 @@ package bpmlib
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type BPMDatabase struct {
|
||||
Name string `yaml:"name"`
|
||||
Source string `yaml:"source"`
|
||||
Disabled *bool `yaml:"disabled"`
|
||||
Entries map[string]*BPMDatabaseEntry
|
||||
DatabaseVersion int `yaml:"database_version"`
|
||||
Entries map[string]*BPMDatabaseEntry `yaml:"entries"`
|
||||
VirtualPackages map[string][]string
|
||||
Source string
|
||||
}
|
||||
|
||||
type BPMDatabaseEntry struct {
|
||||
Info *PackageInfo `yaml:"info"`
|
||||
Download string `yaml:"download"`
|
||||
Filepath string `yaml:"filepath"`
|
||||
DownloadSize uint64 `yaml:"download_size"`
|
||||
InstalledSize uint64 `yaml:"installed_size"`
|
||||
Database *BPMDatabase
|
||||
}
|
||||
|
||||
var BPMDatabases = make(map[string]*BPMDatabase)
|
||||
|
||||
func (db *BPMDatabase) ContainsPackage(pkg string) bool {
|
||||
_, ok := db.Entries[pkg]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (db *BPMDatabase) ReadLocalDatabase() error {
|
||||
func (db *configDatabase) ReadLocalDatabase() error {
|
||||
dbFile := "/var/lib/bpm/databases/" + db.Name + ".bpmdb"
|
||||
if _, err := os.Stat(dbFile); err != nil {
|
||||
return nil
|
||||
@@ -45,37 +45,23 @@ func (db *BPMDatabase) ReadLocalDatabase() error {
|
||||
return err
|
||||
}
|
||||
|
||||
data := string(bytes)
|
||||
for _, b := range strings.Split(data, "---") {
|
||||
entry := BPMDatabaseEntry{
|
||||
Info: &PackageInfo{
|
||||
Name: "",
|
||||
Description: "",
|
||||
Version: "",
|
||||
Revision: 1,
|
||||
Url: "",
|
||||
License: "",
|
||||
Arch: "",
|
||||
Type: "",
|
||||
Keep: make([]string, 0),
|
||||
Depends: make([]string, 0),
|
||||
MakeDepends: make([]string, 0),
|
||||
OptionalDepends: make([]string, 0),
|
||||
Conflicts: make([]string, 0),
|
||||
Provides: make([]string, 0),
|
||||
},
|
||||
Download: "",
|
||||
DownloadSize: 0,
|
||||
InstalledSize: 0,
|
||||
Database: db,
|
||||
}
|
||||
err := yaml.Unmarshal([]byte(b), &entry)
|
||||
// Unmarshal yaml
|
||||
database := &BPMDatabase{}
|
||||
err = yaml.Unmarshal(bytes, database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create database entries
|
||||
// Initialize struct values
|
||||
database.VirtualPackages = make(map[string][]string)
|
||||
database.Source = db.Source
|
||||
|
||||
entriesToRemove := make([]string, 0)
|
||||
for entryName, entry := range database.Entries {
|
||||
entry.Database = database
|
||||
|
||||
if entry.Info.IsSplitPackage() {
|
||||
// Handle split packages
|
||||
for _, splitPkg := range entry.Info.SplitPackages {
|
||||
// Turn split package into json data
|
||||
splitPkgJson, err := yaml.Marshal(splitPkg)
|
||||
@@ -101,35 +87,41 @@ func (db *BPMDatabase) ReadLocalDatabase() error {
|
||||
splitPkgClone.Url = entry.Info.Url
|
||||
|
||||
// Create entry for split package
|
||||
db.Entries[splitPkg.Name] = &BPMDatabaseEntry{
|
||||
database.Entries[splitPkg.Name] = &BPMDatabaseEntry{
|
||||
Info: &splitPkgClone,
|
||||
Download: entry.Download,
|
||||
Filepath: entry.Filepath,
|
||||
DownloadSize: entry.DownloadSize,
|
||||
InstalledSize: 0,
|
||||
Database: db,
|
||||
Database: database,
|
||||
}
|
||||
|
||||
// Add virtual packages to database
|
||||
for _, p := range splitPkg.Provides {
|
||||
db.VirtualPackages[p] = append(db.VirtualPackages[p], splitPkg.Name)
|
||||
database.VirtualPackages[p] = append(database.VirtualPackages[p], splitPkg.Name)
|
||||
}
|
||||
|
||||
// Add current entry to list for removal
|
||||
entriesToRemove = append(entriesToRemove, entryName)
|
||||
}
|
||||
} else {
|
||||
// Create entry for package
|
||||
db.Entries[entry.Info.Name] = &entry
|
||||
|
||||
// Add virtual packages to database
|
||||
for _, p := range entry.Info.Provides {
|
||||
db.VirtualPackages[p] = append(db.VirtualPackages[p], entry.Info.Name)
|
||||
database.VirtualPackages[p] = append(database.VirtualPackages[p], entry.Info.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove entries
|
||||
for _, entryName := range entriesToRemove {
|
||||
delete(database.Entries, entryName)
|
||||
}
|
||||
|
||||
BPMDatabases[db.Name] = database
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *BPMDatabase) SyncLocalDatabaseFile() error {
|
||||
func (db *configDatabase) SyncLocalDatabaseFile() error {
|
||||
dbFile := "/var/lib/bpm/databases/" + db.Name + ".bpmdb"
|
||||
|
||||
// Get URL to database
|
||||
@@ -139,14 +131,10 @@ func (db *BPMDatabase) SyncLocalDatabaseFile() error {
|
||||
}
|
||||
|
||||
// Retrieve data from URL
|
||||
resp, err := http.Get(u)
|
||||
buffer, err := retrieveUrlData(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Load data into byte buffer
|
||||
buffer, err := io.ReadAll(resp.Body)
|
||||
|
||||
// Unmarshal data to ensure it is a valid BPM database
|
||||
err = yaml.Unmarshal(buffer, &BPMDatabase{})
|
||||
@@ -174,10 +162,6 @@ func (db *BPMDatabase) SyncLocalDatabaseFile() error {
|
||||
|
||||
func ReadLocalDatabaseFiles() (err error) {
|
||||
for _, db := range MainBPMConfig.Databases {
|
||||
// Initialize struct values
|
||||
db.Entries = make(map[string]*BPMDatabaseEntry)
|
||||
db.VirtualPackages = make(map[string][]string)
|
||||
|
||||
// Read database
|
||||
err = db.ReadLocalDatabase()
|
||||
if err != nil {
|
||||
@@ -188,15 +172,6 @@ func ReadLocalDatabaseFiles() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetDatabase(name string) *BPMDatabase {
|
||||
for _, db := range MainBPMConfig.Databases {
|
||||
if db.Name == name {
|
||||
return db
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetDatabaseEntry(str string) (*BPMDatabaseEntry, *BPMDatabase, error) {
|
||||
split := strings.Split(str, "/")
|
||||
if len(split) == 1 {
|
||||
@@ -204,7 +179,7 @@ func GetDatabaseEntry(str string) (*BPMDatabaseEntry, *BPMDatabase, error) {
|
||||
if pkgName == "" {
|
||||
return nil, nil, errors.New("could not find database entry for this package")
|
||||
}
|
||||
for _, db := range MainBPMConfig.Databases {
|
||||
for _, db := range BPMDatabases {
|
||||
if db.ContainsPackage(pkgName) {
|
||||
return db.Entries[pkgName], db, nil
|
||||
}
|
||||
@@ -216,7 +191,7 @@ func GetDatabaseEntry(str string) (*BPMDatabaseEntry, *BPMDatabase, error) {
|
||||
if dbName == "" || pkgName == "" {
|
||||
return nil, nil, errors.New("could not find database entry for this package")
|
||||
}
|
||||
db := GetDatabase(dbName)
|
||||
db := BPMDatabases[dbName]
|
||||
if db == nil || !db.ContainsPackage(pkgName) {
|
||||
return nil, nil, errors.New("could not find database entry for this package")
|
||||
}
|
||||
@@ -227,7 +202,7 @@ func GetDatabaseEntry(str string) (*BPMDatabaseEntry, *BPMDatabase, error) {
|
||||
}
|
||||
|
||||
func FindReplacement(pkg string) *BPMDatabaseEntry {
|
||||
for _, db := range MainBPMConfig.Databases {
|
||||
for _, db := range BPMDatabases {
|
||||
for _, entry := range db.Entries {
|
||||
for _, replaced := range entry.Info.Replaces {
|
||||
if replaced == pkg {
|
||||
@@ -241,11 +216,10 @@ func FindReplacement(pkg string) *BPMDatabaseEntry {
|
||||
}
|
||||
|
||||
func ResolveVirtualPackage(vpkg string) *BPMDatabaseEntry {
|
||||
for _, db := range MainBPMConfig.Databases {
|
||||
for _, db := range BPMDatabases {
|
||||
if v, ok := db.VirtualPackages[vpkg]; ok {
|
||||
for _, pkg := range v {
|
||||
return db.Entries[pkg]
|
||||
}
|
||||
slices.Sort(v)
|
||||
return db.Entries[v[0]]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,30 +227,23 @@ func ResolveVirtualPackage(vpkg string) *BPMDatabaseEntry {
|
||||
}
|
||||
|
||||
func (db *BPMDatabase) FetchPackage(pkg string) (string, error) {
|
||||
// Check if package exists in database
|
||||
if !db.ContainsPackage(pkg) {
|
||||
return "", errors.New("could not fetch package '" + pkg + "'")
|
||||
}
|
||||
|
||||
// Get package url from database
|
||||
entry := db.Entries[pkg]
|
||||
URL, err := url.JoinPath(db.Source, entry.Download)
|
||||
u, err := url.JoinPath(db.Source, entry.Filepath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp, err := http.Get(URL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
err = os.MkdirAll("/var/cache/bpm/fetched/", 0755)
|
||||
// Download package from url
|
||||
err = downloadFile(u, path.Join("/var/cache/bpm/fetched/", path.Base(entry.Filepath)), 0644)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
out, err := os.Create("/var/cache/bpm/fetched/" + path.Base(entry.Download))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
return "/var/cache/bpm/fetched/" + path.Base(entry.Download), nil
|
||||
return path.Join("/var/cache/bpm/fetched/", path.Base(entry.Filepath)), nil
|
||||
}
|
||||
|
||||
+11
-14
@@ -31,7 +31,7 @@ func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeOptionalD
|
||||
}) {
|
||||
allDepends = append(allDepends, pkgInstallationReason{
|
||||
PkgName: depend,
|
||||
InstallationReason: InstallationReasonDependency,
|
||||
InstallationReason: InstallationReasonManual,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -51,23 +51,23 @@ func (pkgInfo *PackageInfo) GetDependencies(includeMakeDepends, includeOptionalD
|
||||
return allDepends
|
||||
}
|
||||
|
||||
func (pkgInfo *PackageInfo) GetAllDependencies(includeMakeDepends, includeOptionalDepends bool, rootDir string) (resolved []string) {
|
||||
func (pkgInfo *PackageInfo) GetDependenciesRecursive(includeMakeDepends bool, rootDir string) (resolved []string) {
|
||||
// Initialize slices
|
||||
resolved = make([]string, 0)
|
||||
unresolved := make([]string, 0)
|
||||
|
||||
// Call unexported function
|
||||
pkgInfo.getAllDependencies(&resolved, &unresolved, includeMakeDepends, includeOptionalDepends, rootDir)
|
||||
pkgInfo.getDependenciesRecursive(&resolved, &unresolved, includeMakeDepends, rootDir)
|
||||
|
||||
return resolved
|
||||
}
|
||||
|
||||
func (pkgInfo *PackageInfo) getAllDependencies(resolved *[]string, unresolved *[]string, includeMakeDepends, includeOptionalDepends bool, rootDir string) {
|
||||
func (pkgInfo *PackageInfo) getDependenciesRecursive(resolved *[]string, unresolved *[]string, includeMakeDepends bool, rootDir string) {
|
||||
// Add current package name to unresolved slice
|
||||
*unresolved = append(*unresolved, pkgInfo.Name)
|
||||
|
||||
// Loop through all dependencies
|
||||
for _, pkgIR := range pkgInfo.GetDependencies(includeMakeDepends, includeOptionalDepends) {
|
||||
for _, pkgIR := range pkgInfo.GetDependencies(includeMakeDepends, false) {
|
||||
depend := pkgIR.PkgName
|
||||
|
||||
if isVirtual, p := IsVirtualPackage(depend, rootDir); isVirtual {
|
||||
@@ -86,7 +86,7 @@ func (pkgInfo *PackageInfo) getAllDependencies(resolved *[]string, unresolved *[
|
||||
dependInfo := GetPackageInfo(depend, rootDir)
|
||||
|
||||
if dependInfo != nil {
|
||||
dependInfo.getAllDependencies(resolved, unresolved, includeMakeDepends, includeOptionalDepends, rootDir)
|
||||
dependInfo.getDependenciesRecursive(resolved, unresolved, includeMakeDepends, rootDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,7 +155,7 @@ func resolvePackageDependenciesFromDatabase(resolved *[]pkgInstallationReason, u
|
||||
}
|
||||
|
||||
// Resolve the dependencies of this dependency
|
||||
resolvePackageDependenciesFromDatabase(resolved, unresolved, entry.Info, checkMake, checkOptional, ignoreInstalled, verbose, rootDir)
|
||||
resolvePackageDependenciesFromDatabase(resolved, unresolved, entry.Info, checkMake, false, ignoreInstalled, verbose, rootDir)
|
||||
|
||||
// Move dependency from the unresolved slice to the resolved slice
|
||||
if !slices.ContainsFunc(*resolved, func(p pkgInstallationReason) bool {
|
||||
@@ -198,12 +198,9 @@ func GetPackageDependants(pkgName string, rootDir string) ([]string, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get installed package dependencies
|
||||
dependencies := installedPkg.PkgInfo.GetDependencies(false, true)
|
||||
|
||||
// Add installed package to list if its dependencies include pkgName
|
||||
if slices.ContainsFunc(dependencies, func(p pkgInstallationReason) bool {
|
||||
return p.PkgName == pkgName
|
||||
if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool {
|
||||
return n == pkgName
|
||||
}) {
|
||||
ret = append(ret, installedPkgName)
|
||||
continue
|
||||
@@ -212,8 +209,8 @@ func GetPackageDependants(pkgName string, rootDir string) ([]string, error) {
|
||||
// Loop through each virtual package
|
||||
for _, vpkg := range pkg.PkgInfo.Provides {
|
||||
// Add installed package to list if its dependencies contain a provided virtual package
|
||||
if slices.ContainsFunc(dependencies, func(p pkgInstallationReason) bool {
|
||||
return p.PkgName == vpkg
|
||||
if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool {
|
||||
return n == vpkg
|
||||
}) {
|
||||
ret = append(ret, installedPkgName)
|
||||
break
|
||||
|
||||
@@ -40,3 +40,11 @@ type PackageScriptErr struct {
|
||||
func (e PackageScriptErr) Error() string {
|
||||
return fmt.Sprintf("could not execute package script (%s) for package (%s): %s", e.packageScript, e.packageName, e.err)
|
||||
}
|
||||
|
||||
type PackageRemovalDependencyErr struct {
|
||||
RequiredPackages map[string][]string
|
||||
}
|
||||
|
||||
func (e PackageRemovalDependencyErr) Error() string {
|
||||
return "removing these package would break other installed packages"
|
||||
}
|
||||
|
||||
+45
-15
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"maps"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
@@ -164,14 +165,14 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
|
||||
if rootDir != "/" {
|
||||
sourcePackages := make([]string, 0)
|
||||
for _, action := range operation.Actions {
|
||||
switch action.(type) {
|
||||
switch action := action.(type) {
|
||||
case *InstallPackageAction:
|
||||
if action.(*InstallPackageAction).BpmPackage.PkgInfo.Type == "source" {
|
||||
sourcePackages = append(sourcePackages, action.(*InstallPackageAction).BpmPackage.PkgInfo.Name)
|
||||
if action.BpmPackage.PkgInfo.Type == "source" {
|
||||
sourcePackages = append(sourcePackages, action.BpmPackage.PkgInfo.Name)
|
||||
}
|
||||
case *FetchPackageAction:
|
||||
if action.(*FetchPackageAction).DatabaseEntry.Info.Type == "source" {
|
||||
sourcePackages = append(sourcePackages, action.(*FetchPackageAction).DatabaseEntry.Info.Name)
|
||||
if action.DatabaseEntry.Info.Type == "source" {
|
||||
sourcePackages = append(sourcePackages, action.DatabaseEntry.Info.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,7 +187,7 @@ func InstallPackages(rootDir string, forceInstallationReason InstallationReason,
|
||||
}
|
||||
|
||||
// RemovePackages removes the specified packages from the given root directory
|
||||
func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencies bool, packages ...string) (operation *BPMOperation, err error) {
|
||||
func RemovePackages(rootDir string, force, cleanupDependencies bool, packages ...string) (operation *BPMOperation, err error) {
|
||||
operation = &BPMOperation{
|
||||
Actions: make([]OperationAction, 0),
|
||||
UnresolvedDepends: make([]string, 0),
|
||||
@@ -198,27 +199,56 @@ func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencie
|
||||
// Search for packages
|
||||
for _, pkg := range packages {
|
||||
bpmpkg := GetPackage(pkg, rootDir)
|
||||
if isVirutal, vpkg := IsVirtualPackage(pkg, rootDir); isVirutal {
|
||||
bpmpkg = GetPackage(vpkg, rootDir)
|
||||
}
|
||||
if bpmpkg == nil {
|
||||
continue
|
||||
}
|
||||
operation.AppendAction(&RemovePackageAction{BpmPackage: bpmpkg})
|
||||
}
|
||||
|
||||
// Do not remove packages which other packages depend on
|
||||
if removeUnusedPackagesOnly {
|
||||
err := operation.RemoveNeededPackages()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not skip needed packages: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Do package cleanup
|
||||
if cleanupDependencies {
|
||||
err := operation.Cleanup(true)
|
||||
err := operation.Cleanup(MainBPMConfig.CleanupMakeDependencies)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not perform cleanup for operation: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Return error if other packages depend on removed ones
|
||||
if !force {
|
||||
// Get packages and their dependants
|
||||
packageDepndants := make(map[string][]string, 0)
|
||||
for _, action := range operation.Actions {
|
||||
dependants, err := GetPackageDependants(action.(*RemovePackageAction).BpmPackage.PkgInfo.Name, rootDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get package dependants: %s", err)
|
||||
}
|
||||
|
||||
packageDepndants[action.(*RemovePackageAction).BpmPackage.PkgInfo.Name] = dependants
|
||||
}
|
||||
|
||||
// Remove dependant packages from map if they are to be removed by this operation
|
||||
for pkg, required := range packageDepndants {
|
||||
required = slices.DeleteFunc(required, func(pkgName string) bool {
|
||||
_, ok := packageDepndants[pkgName]
|
||||
return ok
|
||||
})
|
||||
packageDepndants[pkg] = required
|
||||
}
|
||||
|
||||
// Remove empty keys from map
|
||||
maps.DeleteFunc(packageDepndants, func(pkg string, required []string) bool {
|
||||
return len(required) == 0
|
||||
})
|
||||
|
||||
// Return error
|
||||
if len(packageDepndants) != 0 {
|
||||
return nil, PackageRemovalDependencyErr{RequiredPackages: packageDepndants}
|
||||
}
|
||||
}
|
||||
|
||||
return operation, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ module git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/drone/envsubst v1.0.3
|
||||
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g=
|
||||
github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f h1:xt29M2T6STgldg+WEP51gGePQCsQvklmP2eIhPIBK3g=
|
||||
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package bpmlib
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
@@ -169,14 +168,11 @@ func GetAllPackageFiles(rootDir string, excludePackages ...string) (map[string][
|
||||
}
|
||||
bpmpkg := GetPackage(pkgName, rootDir)
|
||||
if bpmpkg == nil {
|
||||
return nil, errors.New(fmt.Sprintf("could not get BPM package (%s)", pkgName))
|
||||
return nil, fmt.Errorf("could not get BPM package (%s)", pkgName)
|
||||
}
|
||||
for _, entry := range bpmpkg.PkgFiles {
|
||||
if _, ok := ret[entry.Path]; ok {
|
||||
ret[entry.Path] = append(ret[entry.Path], bpmpkg)
|
||||
} else {
|
||||
ret[entry.Path] = []*BPMPackage{bpmpkg}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package bpmlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func retrieveUrlData(u string) ([]byte, error) {
|
||||
resp, err := http.Get(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Load data into byte buffer
|
||||
buffer, err := io.ReadAll(resp.Body)
|
||||
|
||||
return buffer, nil
|
||||
}
|
||||
|
||||
func downloadFile(u, filepath string, perm os.FileMode) error {
|
||||
if strings.HasSuffix(filepath, "/") {
|
||||
return fmt.Errorf("Filepath must not end in '/'")
|
||||
}
|
||||
|
||||
data, err := retrieveUrlData(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create parent directories
|
||||
err = os.MkdirAll(path.Dir(filepath), 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create file
|
||||
file, err := os.Create(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Copy data
|
||||
_, err = file.Write(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set file permissions
|
||||
err = file.Chmod(perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+68
-22
@@ -8,6 +8,7 @@ import (
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
type BPMOperation struct {
|
||||
@@ -15,7 +16,9 @@ type BPMOperation struct {
|
||||
UnresolvedDepends []string
|
||||
Changes map[string]string
|
||||
RootDir string
|
||||
|
||||
compiledPackages map[string]string
|
||||
hasFetchedPackages bool
|
||||
}
|
||||
|
||||
func (operation *BPMOperation) ActionsContainPackage(pkg string) bool {
|
||||
@@ -227,7 +230,7 @@ func (operation *BPMOperation) Cleanup(cleanupMakeDepends bool) error {
|
||||
}
|
||||
|
||||
keepPackages = append(keepPackages, pkg.Name)
|
||||
resolved := pkg.GetAllDependencies(!cleanupMakeDepends, true, operation.RootDir)
|
||||
resolved := pkg.GetDependenciesRecursive(!cleanupMakeDepends, operation.RootDir)
|
||||
for _, value := range resolved {
|
||||
if !slices.Contains(keepPackages, value) {
|
||||
keepPackages = append(keepPackages, value)
|
||||
@@ -333,6 +336,9 @@ func (operation *BPMOperation) ShowOperationSummary() {
|
||||
return
|
||||
}
|
||||
|
||||
writer := tabwriter.NewWriter(os.Stdout, 6, 4, 6, ' ', 0)
|
||||
fmt.Fprintln(writer, "Name\tVersion\tAction\tInstallation Reason\tFrom Source")
|
||||
|
||||
for _, value := range operation.Actions {
|
||||
var pkgInfo *PackageInfo
|
||||
var installationReason = InstallationReasonUnknown
|
||||
@@ -347,41 +353,40 @@ func (operation *BPMOperation) ShowOperationSummary() {
|
||||
pkgInfo = value.(*FetchPackageAction).DatabaseEntry.Info
|
||||
} else {
|
||||
pkgInfo = value.(*RemovePackageAction).BpmPackage.PkgInfo
|
||||
fmt.Printf("%s: %s (Remove)\n", pkgInfo.Name, pkgInfo.GetFullVersion())
|
||||
fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), "Remove", "-", "-")
|
||||
continue
|
||||
}
|
||||
|
||||
installedInfo := GetPackageInfo(pkgInfo.Name, operation.RootDir)
|
||||
additionalInfo := ""
|
||||
installationReasonStr := ""
|
||||
switch installationReason {
|
||||
case InstallationReasonManual:
|
||||
additionalInfo = "(Manual)"
|
||||
installationReasonStr = "Manual"
|
||||
case InstallationReasonDependency:
|
||||
additionalInfo = "(Dependency)"
|
||||
installationReasonStr = "Dependency"
|
||||
case InstallationReasonMakeDependency:
|
||||
additionalInfo = "(Make dependency)"
|
||||
installationReasonStr = "Make dependency"
|
||||
default:
|
||||
additionalInfo = "(Unknown)"
|
||||
}
|
||||
|
||||
if pkgInfo.Type == "source" {
|
||||
additionalInfo += " (From Source)"
|
||||
installationReasonStr = "Unknown"
|
||||
}
|
||||
|
||||
installedInfo := GetPackageInfo(pkgInfo.Name, operation.RootDir)
|
||||
if installedInfo == nil {
|
||||
fmt.Printf("%s: %s (Install) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), additionalInfo)
|
||||
fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%t\n", pkgInfo.Name, pkgInfo.GetFullVersion(), "Install", installationReasonStr, pkgInfo.Type == "source")
|
||||
} else {
|
||||
comparison := ComparePackageVersions(*pkgInfo, *installedInfo)
|
||||
if comparison < 0 {
|
||||
fmt.Printf("%s: %s -> %s (Downgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), additionalInfo)
|
||||
fmt.Fprintf(writer, "%s\t%s -> %s\t%s\t%s\t%t\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), "Downgrade", installationReasonStr, pkgInfo.Type == "source")
|
||||
} else if comparison > 0 {
|
||||
fmt.Printf("%s: %s -> %s (Upgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), additionalInfo)
|
||||
fmt.Fprintf(writer, "%s\t%s -> %s\t%s\t%s\t%t\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), "Upgrade", installationReasonStr, pkgInfo.Type == "source")
|
||||
} else {
|
||||
fmt.Printf("%s: %s (Reinstall) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), additionalInfo)
|
||||
fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%t\n", pkgInfo.Name, pkgInfo.GetFullVersion(), "Reinstall", installationReasonStr, pkgInfo.Type == "source")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.Flush()
|
||||
fmt.Println()
|
||||
|
||||
if operation.RootDir != "/" {
|
||||
fmt.Println("Warning: Operating in " + operation.RootDir)
|
||||
}
|
||||
@@ -395,6 +400,34 @@ func (operation *BPMOperation) ShowOperationSummary() {
|
||||
}
|
||||
}
|
||||
|
||||
func (operation *BPMOperation) ShowSourcePackageContent() (sourcePackagesShown int, err error) {
|
||||
// Fetch packages
|
||||
if !operation.hasFetchedPackages {
|
||||
err = operation.FetchPackages()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, action := range operation.Actions {
|
||||
if action.GetActionType() != "install" {
|
||||
continue
|
||||
}
|
||||
if action.(*InstallPackageAction).BpmPackage.PkgInfo.Type != "source" {
|
||||
continue
|
||||
}
|
||||
|
||||
err = showPackageFiles(action.(*InstallPackageAction).File)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
sourcePackagesShown++
|
||||
}
|
||||
|
||||
return sourcePackagesShown, nil
|
||||
}
|
||||
|
||||
func (operation *BPMOperation) RunHooks(verbose bool) error {
|
||||
// Return if hooks directory does not exist
|
||||
if stat, err := os.Stat(path.Join(operation.RootDir, "var/lib/bpm/hooks")); err != nil || !stat.IsDir() {
|
||||
@@ -426,7 +459,7 @@ func (operation *BPMOperation) RunHooks(verbose bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
|
||||
func (operation *BPMOperation) FetchPackages() (err error) {
|
||||
// Fetch packages from databases
|
||||
if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool {
|
||||
return action.GetActionType() == "fetch"
|
||||
@@ -448,7 +481,7 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
|
||||
var bpmpkg *BPMPackage
|
||||
|
||||
// Check if package has already been fetched from download link
|
||||
if _, ok := fetchedPackages[entry.Download]; !ok {
|
||||
if _, ok := fetchedPackages[entry.Filepath]; !ok {
|
||||
// Fetch package from database
|
||||
fetchedPackage, err := entry.Database.FetchPackage(entry.Info.Name)
|
||||
if err != nil {
|
||||
@@ -462,12 +495,12 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
|
||||
}
|
||||
|
||||
// Add fetched package to map
|
||||
fetchedPackages[entry.Download] = fetchedPackage
|
||||
fetchedPackages[entry.Filepath] = fetchedPackage
|
||||
|
||||
fmt.Printf("Package (%s) was successfully fetched!\n", entry.Info.Name)
|
||||
} else {
|
||||
// Read fetched package
|
||||
bpmpkg, err = ReadPackage(fetchedPackages[entry.Download])
|
||||
bpmpkg, err = ReadPackage(fetchedPackages[entry.Filepath])
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read package (%s): %s\n", entry.Info.Name, err)
|
||||
}
|
||||
@@ -477,14 +510,14 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
|
||||
|
||||
if bpmpkg.PkgInfo.IsSplitPackage() {
|
||||
operation.Actions[i] = &InstallPackageAction{
|
||||
File: fetchedPackages[entry.Download],
|
||||
File: fetchedPackages[entry.Filepath],
|
||||
InstallationReason: action.(*FetchPackageAction).InstallationReason,
|
||||
BpmPackage: bpmpkg,
|
||||
SplitPackageToInstall: entry.Info.Name,
|
||||
}
|
||||
} else {
|
||||
operation.Actions[i] = &InstallPackageAction{
|
||||
File: fetchedPackages[entry.Download],
|
||||
File: fetchedPackages[entry.Filepath],
|
||||
InstallationReason: action.(*FetchPackageAction).InstallationReason,
|
||||
BpmPackage: bpmpkg,
|
||||
}
|
||||
@@ -492,6 +525,19 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
operation.hasFetchedPackages = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
|
||||
// Fetch packages
|
||||
if !operation.hasFetchedPackages {
|
||||
err = operation.FetchPackages()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Determine words to be used for the following message
|
||||
words := make([]string, 0)
|
||||
if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool {
|
||||
|
||||
+45
-27
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"regexp"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -26,24 +27,43 @@ type BPMPackage struct {
|
||||
}
|
||||
|
||||
type PackageInfo struct {
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Name string `yaml:"name"`
|
||||
Description string `yaml:"description,omitempty"`
|
||||
Version string `yaml:"version,omitempty"`
|
||||
Revision int `yaml:"revision,omitempty"`
|
||||
Url string `yaml:"url,omitempty"`
|
||||
License string `yaml:"license,omitempty"`
|
||||
Arch string `yaml:"architecture,omitempty"`
|
||||
OutputArch string `yaml:"output_architecture,omitempty"`
|
||||
Type string `yaml:"type,omitempty"`
|
||||
Keep []string `yaml:"keep,omitempty"`
|
||||
Depends []string `yaml:"depends,omitempty"`
|
||||
MakeDepends []string `yaml:"make_depends,omitempty"`
|
||||
OptionalDepends []string `yaml:"optional_depends,omitempty"`
|
||||
MakeDepends []string `yaml:"make_depends,omitempty"`
|
||||
Conflicts []string `yaml:"conflicts,omitempty"`
|
||||
Replaces []string `yaml:"replaces,omitempty"`
|
||||
Provides []string `yaml:"provides,omitempty"`
|
||||
Downloads []PackageDownload `yaml:"downloads,omitempty"`
|
||||
SplitPackages []*PackageInfo `yaml:"split_packages,omitempty"`
|
||||
}
|
||||
|
||||
type PackageDownload struct {
|
||||
Url string `yaml:"url"`
|
||||
Type string `yaml:"type,omitempty"`
|
||||
Filepath string `yaml:"filepath,omitempty,omitempty"`
|
||||
|
||||
// Archive options
|
||||
NoExtract bool `yaml:"no_extract,omitempty"`
|
||||
ExtractTo string `yaml:"extract_to,omitempty"`
|
||||
ExtractStripComponents int `yaml:"extract_strip_components,omitempty"`
|
||||
|
||||
// Git options
|
||||
CloneTo string `yaml:"clone_to,omitempty"`
|
||||
GitBranch string `yaml:"git_branch,omitempty"`
|
||||
|
||||
Checksum string `yaml:"checksum,omitempty"`
|
||||
}
|
||||
|
||||
type PackageFileEntry struct {
|
||||
Path string
|
||||
OctalPerms uint32
|
||||
@@ -177,10 +197,10 @@ func ReadPackage(filename string) (*BPMPackage, error) {
|
||||
}
|
||||
|
||||
file, err := os.Open(filename)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
tr := tar.NewReader(file)
|
||||
for {
|
||||
@@ -303,14 +323,6 @@ func ReadPackageScripts(filename string) (map[string]string, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type packageOperation uint8
|
||||
|
||||
const (
|
||||
packageOperationInstall packageOperation = 0
|
||||
packageOperationUpdate = 1
|
||||
packageOperationRemove = 2
|
||||
)
|
||||
|
||||
func executePackageScript(pkg, rootDir string, verbose bool, packageScript string) error {
|
||||
var bpmpkg *BPMPackage
|
||||
var err error
|
||||
@@ -413,14 +425,8 @@ func executePackageScript(pkg, rootDir string, verbose bool, packageScript strin
|
||||
|
||||
func ReadPackageInfo(contents string) (*PackageInfo, error) {
|
||||
pkgInfo := &PackageInfo{
|
||||
Name: "",
|
||||
Description: "",
|
||||
Version: "",
|
||||
Revision: 1,
|
||||
Url: "",
|
||||
License: "",
|
||||
Arch: "",
|
||||
Type: "",
|
||||
OutputArch: GetArch(),
|
||||
Keep: make([]string, 0),
|
||||
Depends: make([]string, 0),
|
||||
MakeDepends: make([]string, 0),
|
||||
@@ -428,6 +434,7 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
|
||||
Conflicts: make([]string, 0),
|
||||
Replaces: make([]string, 0),
|
||||
Provides: make([]string, 0),
|
||||
Downloads: make([]PackageDownload, 0),
|
||||
SplitPackages: make([]*PackageInfo, 0),
|
||||
}
|
||||
err := yaml.Unmarshal([]byte(contents), &pkgInfo)
|
||||
@@ -455,35 +462,46 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure package name is valid
|
||||
if match, _ := regexp.MatchString("^[a-zA-Z0-9._-]+$", pkgInfo.Name); !match {
|
||||
return nil, fmt.Errorf("package name (%s) is invalid", pkgInfo.Name)
|
||||
}
|
||||
|
||||
// Setup split package information
|
||||
for i, splitPkg := range pkgInfo.SplitPackages {
|
||||
// Ensure split package contains a name
|
||||
if splitPkg.Name == "" {
|
||||
return nil, fmt.Errorf("invalid split package name: %s", splitPkg.Name)
|
||||
return nil, fmt.Errorf("package name (%s) is invalid", splitPkg.Name)
|
||||
}
|
||||
|
||||
// Turn split package into json data
|
||||
splitPkgJson, err := yaml.Marshal(splitPkg)
|
||||
// Ensure split package name is valid
|
||||
if match, _ := regexp.MatchString("^[a-zA-Z0-9._-]+$", splitPkg.Name); !match {
|
||||
return nil, fmt.Errorf("package name (%s) is invalid", splitPkg.Name)
|
||||
}
|
||||
|
||||
// Turn split package into yaml data
|
||||
splitPkgYaml, err := yaml.Marshal(splitPkg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Clone all main package fields onto split package
|
||||
pkgInfoClone := *pkgInfo
|
||||
pkgInfo.SplitPackages[i] = &pkgInfoClone
|
||||
*splitPkg = *pkgInfo
|
||||
|
||||
// Set split package field of split package to nil
|
||||
pkgInfo.SplitPackages[i].SplitPackages = nil
|
||||
splitPkg.SplitPackages = nil
|
||||
|
||||
// Unmarshal json data back to struct
|
||||
err = yaml.Unmarshal(splitPkgJson, &pkgInfo.SplitPackages[i])
|
||||
err = yaml.Unmarshal(splitPkgYaml, splitPkg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Force set split package version, revision
|
||||
pkgInfo.SplitPackages[i].Version = pkgInfo.Version
|
||||
pkgInfo.SplitPackages[i].Revision = pkgInfo.Revision
|
||||
splitPkg.Version = pkgInfo.Version
|
||||
splitPkg.Revision = pkgInfo.Revision
|
||||
|
||||
pkgInfo.SplitPackages[i] = splitPkg
|
||||
}
|
||||
|
||||
return pkgInfo, nil
|
||||
|
||||
@@ -3,9 +3,52 @@ package bpmlib
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type BPMLock struct {
|
||||
file *os.File
|
||||
path string
|
||||
}
|
||||
|
||||
func (lock *BPMLock) Unlock() error {
|
||||
err := lock.file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Remove(lock.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func LockBPM(rootDir string) (*BPMLock, error) {
|
||||
// Create parent directories if they don't already exist
|
||||
err := os.MkdirAll(path.Join(rootDir, "/var/lib/bpm"), 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create file
|
||||
f, err := os.Create(path.Join(rootDir, "var/lib/bpm/bpm.lock"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get exclusive file lock on file
|
||||
err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &BPMLock{f, path.Join(rootDir, "var/lib/bpm/bpm.lock")}, nil
|
||||
}
|
||||
|
||||
func GetArch() string {
|
||||
uname := syscall.Utsname{}
|
||||
err := syscall.Uname(&uname)
|
||||
|
||||
Reference in New Issue
Block a user