Compare commits
No commits in common. "develop" and "master" have entirely different histories.
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,5 +0,0 @@
|
|||||||
# IDE Directories
|
|
||||||
.idea
|
|
||||||
|
|
||||||
# Build directory
|
|
||||||
build
|
|
44
Makefile
44
Makefile
@ -1,26 +1,36 @@
|
|||||||
# Installation paths
|
ifeq ($(PREFIX),)
|
||||||
PREFIX ?= /usr/local
|
PREFIX := /usr/local
|
||||||
BINDIR ?= $(PREFIX)/bin
|
endif
|
||||||
SYSCONFDIR := $(PREFIX)/etc
|
ifeq ($(BINDIR),)
|
||||||
|
BINDIR := $(PREFIX)/bin
|
||||||
# Compilers and tools
|
endif
|
||||||
GO ?= $(shell which go)
|
ifeq ($(SYSCONFDIR),)
|
||||||
|
SYSCONFDIR := $(PREFIX)/etc
|
||||||
|
endif
|
||||||
|
ifeq ($(GO),)
|
||||||
|
GO := $(shell type -a -P go | head -n 1)
|
||||||
|
endif
|
||||||
|
|
||||||
build:
|
build:
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
cd src/bpm; $(GO) build -ldflags "-w" -o ../../build/bpm git.enumerated.dev/bubble-package-manager/bpm/src/bpm
|
$(GO) build -ldflags "-w" -o build/bpm gitlab.com/bubble-package-manager/bpm
|
||||||
|
|
||||||
install: build/bpm config/
|
install: build/bpm config/
|
||||||
# Create directories
|
mkdir -p $(DESTDIR)$(BINDIR)
|
||||||
install -dm755 $(DESTDIR)$(BINDIR)
|
mkdir -p $(DESTDIR)$(SYSCONFDIR)
|
||||||
install -dm755 $(DESTDIR)$(SYSCONFDIR)
|
cp build/bpm $(DESTDIR)$(BINDIR)/bpm
|
||||||
# Install files
|
cp config/bpm.conf $(DESTDIR)$(SYSCONFDIR)/bpm.conf
|
||||||
install -Dm755 build/bpm $(DESTDIR)$(BINDIR)/bpm
|
|
||||||
install -Dm644 config/bpm.conf $(DESTDIR)$(SYSCONFDIR)/bpm.conf
|
|
||||||
|
|
||||||
uninstall:
|
compress: build/bpm config/
|
||||||
rm $(DESTDIR)$(BINDIR)/bpm
|
mkdir -p bpm/$(BINDIR)
|
||||||
rm $(DESTDIR)$(SYSCONFDIR)/bpm.conf
|
mkdir -p bpm/$(SYSCONFDIR)
|
||||||
|
cp build/bpm bpm/$(BINDIR)/bpm
|
||||||
|
cp config/bpm.conf bpm/$(SYSCONFDIR)/bpm.conf
|
||||||
|
tar --owner=root --group=root -czf bpm.tar.gz bpm
|
||||||
|
rm -r bpm
|
||||||
|
|
||||||
|
run: build/bpm
|
||||||
|
build/bpm
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -r build/
|
rm -r build/
|
||||||
|
42
README.md
42
README.md
@ -5,22 +5,24 @@ BPM is a simple package manager for Linux systems
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
- Simple to use subcommands
|
- Simple to use subcommands
|
||||||
- Can install binary packages (and source packages in the future)
|
- Can install binary and source packages
|
||||||
|
- Can be easily installed on practically any system
|
||||||
|
- No bloat
|
||||||
|
|
||||||
## Information
|
## Information
|
||||||
BPM is still in very early development. Do not install it without knowing what you are doing. I would only recommend using it in a Virtual Machine for testing
|
|
||||||
|
BPM is still in very early development. It should not be installed on any system you use seriously. I recommend trying this out in a VM or container. In addition to this, this is one of the first projects I have made using the go programming language so code quality may not be the best. This project was made to help me learn go and how linux systems work better. It is not meant to replace the big package managers in any way
|
||||||
|
|
||||||
## Build from source
|
## Build from source
|
||||||
|
|
||||||
- Download `go` from your package manager or from the go website
|
- Download `go` from your package manager or from the go website
|
||||||
- Download `make` from your package manager
|
- Download `make` from your package manager
|
||||||
- Download `which` from your package manager
|
- Run the following command to compile the project
|
||||||
- Run the following command to compile the project. You may need to set the `GO` environment variable if your Go installation is not in your PATH
|
```
|
||||||
```sh
|
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
- Run the following command to install BPM into your system. You may also append a DESTDIR variable at the end of this line if you wish to install in a different location
|
- Run the following command to install stormfetch into your system. You may also append a DESTDIR variable at the end of this line if you wish to install in a different location
|
||||||
```sh
|
```
|
||||||
make install PREFIX=/usr SYSCONFDIR=/etc
|
make install PREFIX=/usr SYSCONFDIR=/etc
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -30,38 +32,24 @@ You are able to install bpm packages by typing the following:
|
|||||||
```sh
|
```sh
|
||||||
bpm install /path/to/package.bpm
|
bpm install /path/to/package.bpm
|
||||||
```
|
```
|
||||||
You can also use the package name directly if using repositories
|
You may also use the -y flag as shown below to bypass the installation confirmation prompt
|
||||||
```sh
|
|
||||||
bpm install package_name
|
|
||||||
```
|
|
||||||
The -y flag may be used as shown below to bypass the confirmation prompt
|
|
||||||
```sh
|
```sh
|
||||||
bpm install -y /path/to/package.bpm
|
bpm install -y /path/to/package.bpm
|
||||||
```
|
```
|
||||||
Flags must strictly be typed before the first package path or name, otherwise they'll be read as package locations themselves
|
Flags must strictly be typed before the first package path otherwise they'll be read as package locations themselves
|
||||||
|
|
||||||
You can remove an installed package by typing the following
|
You can remove an installed package by typing the following
|
||||||
```sh
|
```sh
|
||||||
bpm remove package_name
|
bpm remove package_name
|
||||||
```
|
```
|
||||||
|
The -y flag applies here as well if you wish to skip the removal confirmation prompt
|
||||||
To remove all unused dependencies try using the cleanup command
|
|
||||||
```sh
|
|
||||||
bpm cleanup
|
|
||||||
```
|
|
||||||
|
|
||||||
If using repositories, all packages can be updated using this simple command
|
|
||||||
```sh
|
|
||||||
bpm update
|
|
||||||
```
|
|
||||||
|
|
||||||
For information on the rest of the commands simply use the help command or pass in no arguments at all
|
For information on the rest of the commands simply use the help command or pass in no arguments at all
|
||||||
```sh
|
```
|
||||||
bpm help
|
bpm help
|
||||||
```
|
```
|
||||||
|
|
||||||
## Package Creation
|
## Package Creation
|
||||||
|
|
||||||
Package creation is simplified using the bpm-utils package which contains helper scripts for creating and archiving packages
|
Package creation is simplified using the bpm-utils package which contains helper scripts for creating and archiving packages. \
|
||||||
|
Learn more here: https://gitlab.com/bubble-package-manager/bpm-utils
|
||||||
Learn more here: https://git.enumerated.dev/bubble-package-manager/bpm-utils
|
|
@ -1,4 +1,7 @@
|
|||||||
ignore_packages: []
|
compilation_env: []
|
||||||
|
silent_compilation: false
|
||||||
|
compilation_dir: "/var/tmp/"
|
||||||
|
binary_output_dir: "/var/lib/bpm/compiled/"
|
||||||
repositories:
|
repositories:
|
||||||
- name: example-repository
|
- name: example-repository
|
||||||
source: https://my-repo.xyz/
|
source: https://my-repo.xyz/
|
||||||
|
9
go.mod
Normal file
9
go.mod
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module gitlab.com/bubble-package-manager/bpm
|
||||||
|
|
||||||
|
go 1.22
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/elliotchance/orderedmap/v2 v2.4.0 // indirect
|
||||||
|
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
@ -1,6 +1,9 @@
|
|||||||
|
github.com/elliotchance/orderedmap/v2 v2.4.0 h1:6tUmMwD9F998FNpwFxA5E6NQvSpk2PVw7RKsVq3+2Cw=
|
||||||
|
github.com/elliotchance/orderedmap/v2 v2.4.0/go.mod h1:85lZyVbpGaGvHvnKa7Qhx7zncAdBIBq6u56Hb1PRU5Q=
|
||||||
|
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||||
|
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
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 h1:xt29M2T6STgldg+WEP51gGePQCsQvklmP2eIhPIBK3g=
|
||||||
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
|
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=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
@ -2,10 +2,9 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib"
|
"gitlab.com/bubble-package-manager/bpm/utils"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -27,23 +26,19 @@ var subcommandArgs []string
|
|||||||
var rootDir = "/"
|
var rootDir = "/"
|
||||||
var verbose = false
|
var verbose = false
|
||||||
var yesAll = false
|
var yesAll = false
|
||||||
|
var buildSource = false
|
||||||
|
var skipCheck = false
|
||||||
|
var keepTempDir = false
|
||||||
var force = false
|
var force = false
|
||||||
var pkgListNumbers = false
|
var pkgListNumbers = false
|
||||||
var pkgListNames = false
|
var pkgListNames = false
|
||||||
var reinstall = false
|
var reinstall = false
|
||||||
var reinstallAll = false
|
var reinstallAll = false
|
||||||
var noOptional = false
|
var noOptional = false
|
||||||
var installationReason = ""
|
|
||||||
var nosync = true
|
var nosync = true
|
||||||
var removeUnused = false
|
|
||||||
var doCleanup = false
|
|
||||||
var showRepoInfo = false
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err := bpmlib.ReadConfig()
|
utils.ReadConfig()
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error: could not read BPM config: %s", err)
|
|
||||||
}
|
|
||||||
resolveFlags()
|
resolveFlags()
|
||||||
resolveCommand()
|
resolveCommand()
|
||||||
}
|
}
|
||||||
@ -60,7 +55,6 @@ const (
|
|||||||
update
|
update
|
||||||
sync
|
sync
|
||||||
remove
|
remove
|
||||||
cleanup
|
|
||||||
file
|
file
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -82,8 +76,6 @@ func getCommandType() commandType {
|
|||||||
return sync
|
return sync
|
||||||
case "remove":
|
case "remove":
|
||||||
return remove
|
return remove
|
||||||
case "cleanup":
|
|
||||||
return cleanup
|
|
||||||
case "file":
|
case "file":
|
||||||
return file
|
return file
|
||||||
default:
|
default:
|
||||||
@ -102,63 +94,20 @@ func resolveCommand() {
|
|||||||
fmt.Println("No packages were given")
|
fmt.Println("No packages were given")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read local databases
|
|
||||||
err := bpmlib.ReadLocalDatabases()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error: could not read local databases: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for n, pkg := range packages {
|
for n, pkg := range packages {
|
||||||
var info *bpmlib.PackageInfo
|
var info *utils.PackageInfo
|
||||||
isFile := false
|
info = utils.GetPackageInfo(pkg, rootDir)
|
||||||
if showRepoInfo {
|
|
||||||
var err error
|
|
||||||
var entry *bpmlib.RepositoryEntry
|
|
||||||
entry, _, err = bpmlib.GetRepositoryEntry(pkg)
|
|
||||||
if err != nil {
|
|
||||||
if entry = bpmlib.ResolveVirtualPackage(pkg); entry == nil {
|
|
||||||
log.Fatalf("Error: could not find package (%s) in any repository\n", pkg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info = entry.Info
|
|
||||||
} else if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() {
|
|
||||||
bpmpkg, err := bpmlib.ReadPackage(pkg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error: could not read package: %s\n", err)
|
|
||||||
}
|
|
||||||
info = bpmpkg.PkgInfo
|
|
||||||
isFile = true
|
|
||||||
} else {
|
|
||||||
if isVirtual, p := bpmlib.IsVirtualPackage(pkg, rootDir); isVirtual {
|
|
||||||
info = bpmlib.GetPackageInfo(p, rootDir)
|
|
||||||
} else {
|
|
||||||
info = bpmlib.GetPackageInfo(pkg, rootDir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if info == nil {
|
if info == nil {
|
||||||
log.Fatalf("Error: package (%s) is not installed\n", pkg)
|
log.Fatalf("Error: package (%s) is not installed\n", pkg)
|
||||||
}
|
}
|
||||||
if n != 0 {
|
fmt.Println("----------------")
|
||||||
fmt.Println()
|
fmt.Println(utils.CreateReadableInfo(true, true, true, info, rootDir))
|
||||||
|
if n == len(packages)-1 {
|
||||||
|
fmt.Println("----------------")
|
||||||
}
|
}
|
||||||
if isFile {
|
|
||||||
abs, err := filepath.Abs(pkg)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error: could not get absolute path of file (%s)\n", abs)
|
|
||||||
}
|
|
||||||
fmt.Println("File: " + abs)
|
|
||||||
}
|
|
||||||
fmt.Println(bpmlib.CreateReadableInfo(true, true, true, info, rootDir))
|
|
||||||
}
|
}
|
||||||
case list:
|
case list:
|
||||||
// Read local databases
|
packages, err := utils.GetInstalledPackages(rootDir)
|
||||||
err := bpmlib.ReadLocalDatabases()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error: could not read local databases: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
packages, err := bpmlib.GetInstalledPackages(rootDir)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not get installed packages: %s", err.Error())
|
log.Fatalf("Error: could not get installed packages: %s", err.Error())
|
||||||
return
|
return
|
||||||
@ -175,15 +124,15 @@ func resolveCommand() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
for n, pkg := range packages {
|
for n, pkg := range packages {
|
||||||
info := bpmlib.GetPackageInfo(pkg, rootDir)
|
info := utils.GetPackageInfo(pkg, rootDir)
|
||||||
if info == nil {
|
if info == nil {
|
||||||
fmt.Printf("Package (%s) could not be found\n", pkg)
|
fmt.Printf("Package (%s) could not be found\n", pkg)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if n != 0 {
|
fmt.Println("----------------\n" + utils.CreateReadableInfo(true, true, true, info, rootDir))
|
||||||
fmt.Println()
|
if n == len(packages)-1 {
|
||||||
|
fmt.Println("----------------")
|
||||||
}
|
}
|
||||||
fmt.Println(bpmlib.CreateReadableInfo(true, true, true, info, rootDir))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case search:
|
case search:
|
||||||
@ -192,16 +141,10 @@ func resolveCommand() {
|
|||||||
log.Fatalf("Error: no search terms given")
|
log.Fatalf("Error: no search terms given")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read local databases
|
for _, term := range searchTerms {
|
||||||
err := bpmlib.ReadLocalDatabases()
|
nameResults := make([]*utils.PackageInfo, 0)
|
||||||
if err != nil {
|
descResults := make([]*utils.PackageInfo, 0)
|
||||||
log.Fatalf("Error: could not read local databases: %s", err)
|
for _, repo := range utils.BPMConfig.Repositories {
|
||||||
}
|
|
||||||
|
|
||||||
for i, term := range searchTerms {
|
|
||||||
nameResults := make([]*bpmlib.PackageInfo, 0)
|
|
||||||
descResults := make([]*bpmlib.PackageInfo, 0)
|
|
||||||
for _, repo := range bpmlib.BPMConfig.Repositories {
|
|
||||||
for _, entry := range repo.Entries {
|
for _, entry := range repo.Entries {
|
||||||
if strings.Contains(entry.Info.Name, term) {
|
if strings.Contains(entry.Info.Name, term) {
|
||||||
nameResults = append(nameResults, entry.Info)
|
nameResults = append(nameResults, entry.Info)
|
||||||
@ -214,66 +157,69 @@ func resolveCommand() {
|
|||||||
if len(results) == 0 {
|
if len(results) == 0 {
|
||||||
log.Fatalf("Error: no results for term (%s) were found\n", term)
|
log.Fatalf("Error: no results for term (%s) were found\n", term)
|
||||||
}
|
}
|
||||||
if i > 0 {
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
fmt.Printf("Results for term (%s)\n", term)
|
fmt.Printf("Results for term (%s)\n", term)
|
||||||
for j, result := range results {
|
for i, result := range results {
|
||||||
fmt.Printf("%d) %s: %s (%s)\n", j+1, result.Name, result.Description, result.GetFullVersion())
|
fmt.Println("----------------")
|
||||||
|
fmt.Printf("%d) %s: %s (%s)\n", i+1, result.Name, result.Description, result.GetFullVersion())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case install:
|
case install:
|
||||||
// Check for required permissions
|
|
||||||
if os.Getuid() != 0 {
|
if os.Getuid() != 0 {
|
||||||
log.Fatalf("Error: this subcommand needs to be run with superuser permissions")
|
log.Fatalf("Error: this subcommand needs to be run with superuser permissions")
|
||||||
}
|
}
|
||||||
|
pkgs := subcommandArgs
|
||||||
// Return if no packages are specified
|
if len(pkgs) == 0 {
|
||||||
if len(subcommandArgs) == 0 {
|
|
||||||
fmt.Println("No packages or files were given to install")
|
fmt.Println("No packages or files were given to install")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if installationReason argument is valid
|
operation := utils.BPMOperation{
|
||||||
ir := bpmlib.InstallationReasonUnknown
|
Actions: make([]utils.OperationAction, 0),
|
||||||
switch installationReason {
|
UnresolvedDepends: make([]string, 0),
|
||||||
case "manual":
|
RootDir: rootDir,
|
||||||
ir = bpmlib.InstallationReasonManual
|
|
||||||
case "dependency":
|
|
||||||
ir = bpmlib.InstallationReasonDependency
|
|
||||||
case "":
|
|
||||||
default:
|
|
||||||
log.Fatalf("Error: %s is not a valid installation reason", installationReason)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get reinstall method
|
// Search for packages
|
||||||
var reinstallMethod bpmlib.ReinstallMethod
|
for _, pkg := range pkgs {
|
||||||
if reinstallAll {
|
if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() {
|
||||||
reinstallMethod = bpmlib.ReinstallMethodAll
|
bpmpkg, err := utils.ReadPackage(pkg)
|
||||||
} else if reinstall {
|
|
||||||
reinstallMethod = bpmlib.ReinstallMethodSpecified
|
|
||||||
} else {
|
|
||||||
reinstallMethod = bpmlib.ReinstallMethodNone
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read local databases
|
|
||||||
err := bpmlib.ReadLocalDatabases()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not read local databases: %s", err)
|
log.Fatalf("Error: could not read package: %s\n", err)
|
||||||
|
}
|
||||||
|
if !reinstall && utils.IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) && utils.GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() == bpmpkg.PkgInfo.GetFullVersion() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
operation.Actions = append(operation.Actions, &utils.InstallPackageAction{
|
||||||
|
File: pkg,
|
||||||
|
IsDependency: false,
|
||||||
|
BpmPackage: bpmpkg,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
entry, _, err := utils.GetRepositoryEntry(pkg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not find package (%s) in any repository\n", pkg)
|
||||||
|
}
|
||||||
|
if !reinstall && utils.IsPackageInstalled(entry.Info.Name, rootDir) && utils.GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
operation.Actions = append(operation.Actions, &utils.FetchPackageAction{
|
||||||
|
IsDependency: false,
|
||||||
|
RepositoryEntry: entry,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create installation operation
|
// Resolve dependencies
|
||||||
operation, err := bpmlib.InstallPackages(rootDir, ir, reinstallMethod, !noOptional, force, verbose, subcommandArgs...)
|
err := operation.ResolveDependencies(reinstallAll, !noOptional, verbose)
|
||||||
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
|
if err != nil {
|
||||||
log.Fatalf("Error: %s", err)
|
log.Fatalf("Error: could not resolve dependencies: %s\n", err)
|
||||||
} else if err != nil {
|
}
|
||||||
log.Fatalf("Error: could not setup operation: %s\n", err)
|
if len(operation.UnresolvedDepends) != 0 {
|
||||||
|
if !force {
|
||||||
|
log.Fatalf("Error: the following dependencies could not be found in any repositories: %s\n", strings.Join(operation.UnresolvedDepends, ", "))
|
||||||
|
} else {
|
||||||
|
log.Println("Warning: The following dependencies could not be found in any repositories: " + strings.Join(operation.UnresolvedDepends, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit if operation contains no actions
|
|
||||||
if len(operation.Actions) == 0 {
|
|
||||||
fmt.Println("No action needs to be taken")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show operation summary
|
// Show operation summary
|
||||||
@ -300,39 +246,68 @@ func resolveCommand() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not complete operation: %s\n", err)
|
log.Fatalf("Error: could not complete operation: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Executing hooks
|
|
||||||
fmt.Println("Running hooks...")
|
|
||||||
err = operation.RunHooks(verbose)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error: could not run hooks: %s\n", err)
|
|
||||||
}
|
|
||||||
case update:
|
case update:
|
||||||
// Check for required permissions
|
|
||||||
if os.Getuid() != 0 {
|
if os.Getuid() != 0 {
|
||||||
log.Fatalf("Error: this subcommand needs to be run with superuser permissions")
|
log.Fatalf("Error: this subcommand needs to be run with superuser permissions")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read local databases if no sync
|
// Sync repositories
|
||||||
if nosync {
|
if !nosync {
|
||||||
err := bpmlib.ReadLocalDatabases()
|
for _, repo := range utils.BPMConfig.Repositories {
|
||||||
|
fmt.Printf("Fetching package database for repository (%s)...\n", repo.Name)
|
||||||
|
err := repo.SyncLocalDatabase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not read local databases: %s", err)
|
log.Fatalf("Error: could not sync local database for repository (%s): %s\n", repo.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("All package databases synced successfully!")
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.ReadConfig()
|
||||||
|
|
||||||
|
// Get installed packages and check for updates
|
||||||
|
pkgs, err := utils.GetInstalledPackages(rootDir)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: could not get installed packages: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
operation := utils.BPMOperation{
|
||||||
|
Actions: make([]utils.OperationAction, 0),
|
||||||
|
UnresolvedDepends: make([]string, 0),
|
||||||
|
RootDir: rootDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for packages
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
entry, _, err := utils.GetRepositoryEntry(pkg)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
installedInfo := utils.GetPackageInfo(pkg, rootDir)
|
||||||
|
if installedInfo == nil {
|
||||||
|
log.Fatalf("Error: could not get package info for (%s)\n", pkg)
|
||||||
|
} else {
|
||||||
|
comparison := utils.ComparePackageVersions(*entry.Info, *installedInfo)
|
||||||
|
if comparison > 0 || reinstall {
|
||||||
|
operation.Actions = append(operation.Actions, &utils.FetchPackageAction{
|
||||||
|
IsDependency: false,
|
||||||
|
RepositoryEntry: entry,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create update operation
|
// Check for new dependencies in updated packages
|
||||||
operation, err := bpmlib.UpdatePackages(rootDir, !nosync, !noOptional, force, verbose)
|
err = operation.ResolveDependencies(reinstallAll, !noOptional, verbose)
|
||||||
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
|
if err != nil {
|
||||||
log.Fatalf("Error: %s", err)
|
log.Fatalf("Error: could not resolve dependencies: %s\n", err)
|
||||||
} else if err != nil {
|
}
|
||||||
log.Fatalf("Error: could not setup operation: %s\n", err)
|
if len(operation.UnresolvedDepends) != 0 {
|
||||||
|
if !force {
|
||||||
|
log.Fatalf("Error: the following dependencies could not be found in any repositories: %s\n", strings.Join(operation.UnresolvedDepends, ", "))
|
||||||
|
} else {
|
||||||
|
log.Println("Warning: The following dependencies could not be found in any repositories: " + strings.Join(operation.UnresolvedDepends, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit if operation contains no actions
|
|
||||||
if len(operation.Actions) == 0 {
|
|
||||||
fmt.Println("No action needs to be taken")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show operation summary
|
// Show operation summary
|
||||||
@ -354,20 +329,10 @@ func resolveCommand() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not complete operation: %s\n", err)
|
log.Fatalf("Error: could not complete operation: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Executing hooks
|
|
||||||
fmt.Println("Running hooks...")
|
|
||||||
err = operation.RunHooks(verbose)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error: could not run hooks: %s\n", err)
|
|
||||||
}
|
|
||||||
case sync:
|
case sync:
|
||||||
// Check for required permissions
|
|
||||||
if os.Getuid() != 0 {
|
if os.Getuid() != 0 {
|
||||||
log.Fatalf("Error: this subcommand needs to be run with superuser permissions")
|
log.Fatalf("Error: this subcommand needs to be run with superuser permissions")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Confirmation Prompt
|
|
||||||
if !yesAll {
|
if !yesAll {
|
||||||
fmt.Printf("Are you sure you wish to sync all databases? [y\\N] ")
|
fmt.Printf("Are you sure you wish to sync all databases? [y\\N] ")
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
@ -377,43 +342,37 @@ func resolveCommand() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, repo := range utils.BPMConfig.Repositories {
|
||||||
// Sync databases
|
fmt.Printf("Fetching package database for repository (%s)...\n", repo.Name)
|
||||||
err := bpmlib.SyncDatabase(verbose)
|
err := repo.SyncLocalDatabase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not sync local database: %s\n", err)
|
log.Fatalf("Error: could not sync local database for repository (%s): %s\n", repo.Name, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("All package databases synced successfully!")
|
fmt.Println("All package databases synced successfully!")
|
||||||
case remove:
|
case remove:
|
||||||
// Check for required permissions
|
|
||||||
if os.Getuid() != 0 {
|
if os.Getuid() != 0 {
|
||||||
log.Fatalf("Error: this subcommand needs to be run with superuser permissions")
|
log.Fatalf("Error: this subcommand needs to be run with superuser permissions")
|
||||||
}
|
}
|
||||||
|
packages := subcommandArgs
|
||||||
if len(subcommandArgs) == 0 {
|
if len(packages) == 0 {
|
||||||
fmt.Println("No packages were given")
|
fmt.Println("No packages were given")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read local databases
|
operation := &utils.BPMOperation{
|
||||||
err := bpmlib.ReadLocalDatabases()
|
Actions: make([]utils.OperationAction, 0),
|
||||||
if err != nil {
|
UnresolvedDepends: make([]string, 0),
|
||||||
log.Fatalf("Error: could not read local databases: %s", err)
|
RootDir: rootDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create remove operation
|
// Search for packages
|
||||||
operation, err := bpmlib.RemovePackages(rootDir, removeUnused, doCleanup, verbose, subcommandArgs...)
|
for _, pkg := range packages {
|
||||||
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
|
bpmpkg := utils.GetPackage(pkg, rootDir)
|
||||||
log.Fatalf("Error: %s", err)
|
if bpmpkg == nil {
|
||||||
} else if err != nil {
|
log.Fatalf("Error: package (%s) could not be found\n", pkg)
|
||||||
log.Fatalf("Error: could not setup operation: %s\n", err)
|
|
||||||
}
|
}
|
||||||
|
operation.Actions = append(operation.Actions, &utils.RemovePackageAction{BpmPackage: bpmpkg})
|
||||||
// Exit if operation contains no actions
|
|
||||||
if len(operation.Actions) == 0 {
|
|
||||||
fmt.Println("No action needs to be taken")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show operation summary
|
// Show operation summary
|
||||||
@ -431,69 +390,10 @@ func resolveCommand() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute operation
|
// Execute operation
|
||||||
err = operation.Execute(verbose, force)
|
err := operation.Execute(verbose, force)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not complete operation: %s\n", err)
|
log.Fatalf("Error: could not complete operation: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Executing hooks
|
|
||||||
fmt.Println("Running hooks...")
|
|
||||||
err = operation.RunHooks(verbose)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error: could not run hooks: %s\n", err)
|
|
||||||
}
|
|
||||||
case cleanup:
|
|
||||||
// Check for required permissions
|
|
||||||
if os.Getuid() != 0 {
|
|
||||||
log.Fatalf("Error: this subcommand needs to be run with superuser permissions")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read local databases
|
|
||||||
err := bpmlib.ReadLocalDatabases()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error: could not read local databases: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create cleanup operation
|
|
||||||
operation, err := bpmlib.CleanupPackages(rootDir, verbose)
|
|
||||||
if errors.As(err, &bpmlib.PackageNotFoundErr{}) || errors.As(err, &bpmlib.DependencyNotFoundErr{}) || errors.As(err, &bpmlib.PackageConflictErr{}) {
|
|
||||||
log.Fatalf("Error: %s", err)
|
|
||||||
} else if err != nil {
|
|
||||||
log.Fatalf("Error: could not setup operation: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit if operation contains no actions
|
|
||||||
if len(operation.Actions) == 0 {
|
|
||||||
fmt.Println("No action needs to be taken")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show operation summary
|
|
||||||
operation.ShowOperationSummary()
|
|
||||||
|
|
||||||
// Confirmation Prompt
|
|
||||||
if !yesAll {
|
|
||||||
fmt.Printf("Are you sure you wish to remove all %d packages? [y\\N] ", len(operation.Actions))
|
|
||||||
reader := bufio.NewReader(os.Stdin)
|
|
||||||
text, _ := reader.ReadString('\n')
|
|
||||||
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
|
|
||||||
fmt.Println("Cancelling package removal...")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute operation
|
|
||||||
err = operation.Execute(verbose, force)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error: could not complete operation: %s\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Executing hooks
|
|
||||||
fmt.Println("Running hooks...")
|
|
||||||
err = operation.RunHooks(verbose)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error: could not run hooks: %s\n", err)
|
|
||||||
}
|
|
||||||
case file:
|
case file:
|
||||||
files := subcommandArgs
|
files := subcommandArgs
|
||||||
if len(files) == 0 {
|
if len(files) == 0 {
|
||||||
@ -509,7 +409,7 @@ func resolveCommand() {
|
|||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
log.Fatalf("Error: file (%s) does not exist!\n", absFile)
|
log.Fatalf("Error: file (%s) does not exist!\n", absFile)
|
||||||
}
|
}
|
||||||
pkgs, err := bpmlib.GetInstalledPackages(rootDir)
|
pkgs, err := utils.GetInstalledPackages(rootDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: could not get installed packages: %s\n", err.Error())
|
log.Fatalf("Error: could not get installed packages: %s\n", err.Error())
|
||||||
}
|
}
|
||||||
@ -528,7 +428,7 @@ func resolveCommand() {
|
|||||||
|
|
||||||
var pkgList []string
|
var pkgList []string
|
||||||
for _, pkg := range pkgs {
|
for _, pkg := range pkgs {
|
||||||
if slices.ContainsFunc(bpmlib.GetPackageFiles(pkg, rootDir), func(entry *bpmlib.PackageFileEntry) bool {
|
if slices.ContainsFunc(utils.GetPackageFiles(pkg, rootDir), func(entry *utils.PackageFileEntry) bool {
|
||||||
return entry.Path == absFile
|
return entry.Path == absFile
|
||||||
}) {
|
}) {
|
||||||
pkgList = append(pkgList, pkg)
|
pkgList = append(pkgList, pkg)
|
||||||
@ -554,24 +454,25 @@ func printHelp() {
|
|||||||
fmt.Println("-> flags will be read if passed right after the subcommand otherwise they will be read as subcommand arguments")
|
fmt.Println("-> flags will be read if passed right after the subcommand otherwise they will be read as subcommand arguments")
|
||||||
fmt.Println("\033[1m---- Command List ----\033[0m")
|
fmt.Println("\033[1m---- Command List ----\033[0m")
|
||||||
fmt.Println("-> bpm version | shows information on the installed version of bpm")
|
fmt.Println("-> bpm version | shows information on the installed version of bpm")
|
||||||
fmt.Println("-> bpm info [-R, --repos] <packages...> | shows information on an installed package")
|
fmt.Println("-> bpm info [-R] <packages...> | shows information on an installed package")
|
||||||
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
||||||
fmt.Println(" --repos show information on package in repository")
|
|
||||||
fmt.Println("-> bpm list [-R, -c, -n] | lists all installed packages")
|
fmt.Println("-> bpm list [-R, -c, -n] | lists all installed packages")
|
||||||
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
||||||
fmt.Println(" -c lists the amount of installed packages")
|
fmt.Println(" -c lists the amount of installed packages")
|
||||||
fmt.Println(" -n lists only the names of installed packages")
|
fmt.Println(" -n lists only the names of installed packages")
|
||||||
fmt.Println("-> bpm search <search terms...> | Searches for packages through declared repositories")
|
fmt.Println("-> bpm search <search terms...> | Searches for packages through declared repositories")
|
||||||
fmt.Println("-> bpm install [-R, -v, -y, -f, -o, -c, -b, -k, --reinstall, --reinstall-all, --no-optional, --installation-reason] <packages...> | installs the following files")
|
fmt.Println("-> bpm install [-R, -v, -y, -f, -o, -c, -b, -k, --reinstall, --reinstall-all, --no-optional] <packages...> | installs the following files")
|
||||||
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
||||||
fmt.Println(" -v Show additional information about what BPM is doing")
|
fmt.Println(" -v Show additional information about what BPM is doing")
|
||||||
fmt.Println(" -y skips the confirmation prompt")
|
fmt.Println(" -y skips the confirmation prompt")
|
||||||
fmt.Println(" -f skips dependency, conflict and architecture checking")
|
fmt.Println(" -f skips dependency, conflict and architecture checking")
|
||||||
|
fmt.Println(" -o=<path> set the binary package output directory (defaults to /var/lib/bpm/compiled)")
|
||||||
|
fmt.Println(" -c=<path> set the compilation directory (defaults to /var/tmp)")
|
||||||
|
fmt.Println(" -b creates a binary package from a source package after compilation and saves it in the binary package output directory")
|
||||||
fmt.Println(" -k keeps the compilation directory created by BPM after source package installation")
|
fmt.Println(" -k keeps the compilation directory created by BPM after source package installation")
|
||||||
fmt.Println(" --reinstall Reinstalls packages even if they do not have a newer version available")
|
fmt.Println(" --reinstall Reinstalls packages even if they do not have a newer version available")
|
||||||
fmt.Println(" --reinstall-all Same as --reinstall but also reinstalls dependencies")
|
fmt.Println(" --reinstall-all Same as --reinstall but also reinstalls dependencies")
|
||||||
fmt.Println(" --no-optional Prevents installation of optional dependencies")
|
fmt.Println(" --no-optional Prevents installation of optional dependencies")
|
||||||
fmt.Println(" --installation-reason=<manual/dependency> sets the installation reason for all newly installed packages")
|
|
||||||
fmt.Println("-> bpm update [-R, -v, -y, -f, --reinstall, --no-sync] | updates all packages that are available in the repositories")
|
fmt.Println("-> bpm update [-R, -v, -y, -f, --reinstall, --no-sync] | updates all packages that are available in the repositories")
|
||||||
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
||||||
fmt.Println(" -v Show additional information about what BPM is doing")
|
fmt.Println(" -v Show additional information about what BPM is doing")
|
||||||
@ -583,13 +484,7 @@ func printHelp() {
|
|||||||
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
||||||
fmt.Println(" -v Show additional information about what BPM is doing")
|
fmt.Println(" -v Show additional information about what BPM is doing")
|
||||||
fmt.Println(" -y skips the confirmation prompt")
|
fmt.Println(" -y skips the confirmation prompt")
|
||||||
fmt.Println("-> bpm remove [-R, -v, -y, --unused, --cleanup] <packages...> | removes the following packages")
|
fmt.Println("-> bpm remove [-R, -v, -y] <packages...> | removes the following packages")
|
||||||
fmt.Println(" -v Show additional information about what BPM is doing")
|
|
||||||
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
|
||||||
fmt.Println(" -y skips the confirmation prompt")
|
|
||||||
fmt.Println(" --unused removes only packages that aren't required as dependencies by other packages")
|
|
||||||
fmt.Println(" --cleanup performs a dependency cleanup")
|
|
||||||
fmt.Println("-> bpm cleanup [-R, -v, -y] | remove all unused dependency packages")
|
|
||||||
fmt.Println(" -v Show additional information about what BPM is doing")
|
fmt.Println(" -v Show additional information about what BPM is doing")
|
||||||
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
fmt.Println(" -R=<path> lets you define the root path which will be used")
|
||||||
fmt.Println(" -y skips the confirmation prompt")
|
fmt.Println(" -y skips the confirmation prompt")
|
||||||
@ -608,18 +503,21 @@ func resolveFlags() {
|
|||||||
// Info flags
|
// Info flags
|
||||||
infoFlagSet := flag.NewFlagSet("Info flags", flag.ExitOnError)
|
infoFlagSet := flag.NewFlagSet("Info flags", flag.ExitOnError)
|
||||||
infoFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
infoFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
||||||
infoFlagSet.BoolVar(&showRepoInfo, "repos", false, "Show information on package in repository")
|
|
||||||
infoFlagSet.Usage = printHelp
|
infoFlagSet.Usage = printHelp
|
||||||
// Install flags
|
// Install flags
|
||||||
installFlagSet := flag.NewFlagSet("Install flags", flag.ExitOnError)
|
installFlagSet := flag.NewFlagSet("Install flags", flag.ExitOnError)
|
||||||
installFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
installFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
||||||
installFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing")
|
installFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing")
|
||||||
installFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
|
installFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
|
||||||
|
installFlagSet.StringVar(&utils.BPMConfig.BinaryOutputDir, "o", utils.BPMConfig.BinaryOutputDir, "Set the binary output directory")
|
||||||
|
installFlagSet.StringVar(&utils.BPMConfig.CompilationDir, "c", utils.BPMConfig.CompilationDir, "Set the compilation directory")
|
||||||
|
installFlagSet.BoolVar(&buildSource, "b", false, "Build binary package from source package")
|
||||||
|
installFlagSet.BoolVar(&skipCheck, "s", false, "Skip check function during source compilation")
|
||||||
|
installFlagSet.BoolVar(&keepTempDir, "k", false, "Keep temporary directory after source compilation")
|
||||||
installFlagSet.BoolVar(&force, "f", false, "Force installation by skipping architecture and dependency resolution")
|
installFlagSet.BoolVar(&force, "f", false, "Force installation by skipping architecture and dependency resolution")
|
||||||
installFlagSet.BoolVar(&reinstall, "reinstall", false, "Reinstalls packages even if they do not have a newer version available")
|
installFlagSet.BoolVar(&reinstall, "reinstall", false, "Reinstalls packages even if they do not have a newer version available")
|
||||||
installFlagSet.BoolVar(&reinstallAll, "reinstall-all", false, "Same as --reinstall but also reinstalls dependencies")
|
installFlagSet.BoolVar(&reinstallAll, "reinstall-all", false, "Same as --reinstall but also reinstalls dependencies")
|
||||||
installFlagSet.BoolVar(&noOptional, "no-optional", false, "Prevents installation of optional dependencies")
|
installFlagSet.BoolVar(&noOptional, "no-optional", false, "Prevents installation of optional dependencies")
|
||||||
installFlagSet.StringVar(&installationReason, "installation-reason", "", "Set the installation reason for all newly installed packages")
|
|
||||||
installFlagSet.Usage = printHelp
|
installFlagSet.Usage = printHelp
|
||||||
// Update flags
|
// Update flags
|
||||||
updateFlagSet := flag.NewFlagSet("Update flags", flag.ExitOnError)
|
updateFlagSet := flag.NewFlagSet("Update flags", flag.ExitOnError)
|
||||||
@ -627,6 +525,7 @@ func resolveFlags() {
|
|||||||
updateFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing")
|
updateFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing")
|
||||||
updateFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
|
updateFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
|
||||||
updateFlagSet.BoolVar(&force, "f", false, "Force update by skipping architecture and dependency resolution")
|
updateFlagSet.BoolVar(&force, "f", false, "Force update by skipping architecture and dependency resolution")
|
||||||
|
updateFlagSet.BoolVar(&reinstall, "reinstall", false, "Fetches and reinstalls all packages even if they do not have a newer version available")
|
||||||
updateFlagSet.BoolVar(&nosync, "no-sync", false, "Skips package database syncing")
|
updateFlagSet.BoolVar(&nosync, "no-sync", false, "Skips package database syncing")
|
||||||
updateFlagSet.Usage = printHelp
|
updateFlagSet.Usage = printHelp
|
||||||
// Sync flags
|
// Sync flags
|
||||||
@ -640,15 +539,7 @@ func resolveFlags() {
|
|||||||
removeFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
removeFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
||||||
removeFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing")
|
removeFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing")
|
||||||
removeFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
|
removeFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
|
||||||
removeFlagSet.BoolVar(&removeUnused, "unused", false, "Removes only packages that aren't required as dependencies by other packages")
|
|
||||||
removeFlagSet.BoolVar(&doCleanup, "cleanup", false, "Perform a dependency cleanup")
|
|
||||||
removeFlagSet.Usage = printHelp
|
removeFlagSet.Usage = printHelp
|
||||||
// Cleanup flags
|
|
||||||
cleanupFlagSet := flag.NewFlagSet("Cleanup flags", flag.ExitOnError)
|
|
||||||
cleanupFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
|
||||||
cleanupFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing")
|
|
||||||
cleanupFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
|
|
||||||
cleanupFlagSet.Usage = printHelp
|
|
||||||
// File flags
|
// File flags
|
||||||
fileFlagSet := flag.NewFlagSet("Remove flags", flag.ExitOnError)
|
fileFlagSet := flag.NewFlagSet("Remove flags", flag.ExitOnError)
|
||||||
fileFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
fileFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
|
@ -1,14 +0,0 @@
|
|||||||
module git.enumerated.dev/bubble-package-manager/bpm/src/bpm
|
|
||||||
|
|
||||||
go 1.23
|
|
||||||
|
|
||||||
toolchain go1.23.7
|
|
||||||
|
|
||||||
require git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib v0.5.0
|
|
||||||
|
|
||||||
replace git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib => ../bpmlib
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
|
@ -1,6 +0,0 @@
|
|||||||
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=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -1,38 +0,0 @@
|
|||||||
package bpmlib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BPMConfigStruct struct {
|
|
||||||
IgnorePackages []string `yaml:"ignore_packages"`
|
|
||||||
Repositories []*Repository `yaml:"repositories"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var BPMConfig BPMConfigStruct
|
|
||||||
|
|
||||||
func ReadConfig() (err error) {
|
|
||||||
if _, err = os.Stat("/etc/bpm.conf"); os.IsNotExist(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes, err := os.ReadFile("/etc/bpm.conf")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
BPMConfig = BPMConfigStruct{}
|
|
||||||
err = yaml.Unmarshal(bytes, &BPMConfig)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := len(BPMConfig.Repositories) - 1; i >= 0; i-- {
|
|
||||||
if BPMConfig.Repositories[i].Disabled != nil && *BPMConfig.Repositories[i].Disabled {
|
|
||||||
BPMConfig.Repositories = append(BPMConfig.Repositories[:i], BPMConfig.Repositories[i+1:]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package bpmlib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PackageNotFoundErr struct {
|
|
||||||
packages []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e PackageNotFoundErr) Error() string {
|
|
||||||
return "The following packages were not found in any repositories: " + strings.Join(e.packages, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
type DependencyNotFoundErr struct {
|
|
||||||
dependencies []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e DependencyNotFoundErr) Error() string {
|
|
||||||
return "The following dependencies were not found in any repositories: " + strings.Join(e.dependencies, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
type PackageConflictErr struct {
|
|
||||||
pkg string
|
|
||||||
conflicts []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e PackageConflictErr) Error() string {
|
|
||||||
return fmt.Sprintf("Package (%s) is in conflict with the following packages: %s", e.pkg, strings.Join(e.conflicts, ", "))
|
|
||||||
}
|
|
@ -1,260 +0,0 @@
|
|||||||
package bpmlib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"slices"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ReinstallMethod uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
ReinstallMethodNone ReinstallMethod = iota
|
|
||||||
ReinstallMethodSpecified ReinstallMethod = iota
|
|
||||||
ReinstallMethodAll ReinstallMethod = iota
|
|
||||||
)
|
|
||||||
|
|
||||||
// InstallPackages installs the specified packages into the given root directory by fetching them from repositories or directly from local bpm archives
|
|
||||||
func InstallPackages(rootDir string, installationReason InstallationReason, reinstallMethod ReinstallMethod, installOptionalDependencies, forceInstallation, verbose bool, packages ...string) (operation *BPMOperation, err error) {
|
|
||||||
// Setup operation struct
|
|
||||||
operation = &BPMOperation{
|
|
||||||
Actions: make([]OperationAction, 0),
|
|
||||||
UnresolvedDepends: make([]string, 0),
|
|
||||||
Changes: make(map[string]string),
|
|
||||||
RootDir: rootDir,
|
|
||||||
ForceInstallationReason: installationReason,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve packages
|
|
||||||
pkgsNotFound := make([]string, 0)
|
|
||||||
for _, pkg := range packages {
|
|
||||||
if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() {
|
|
||||||
bpmpkg, err := ReadPackage(pkg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not read package: %s", err)
|
|
||||||
}
|
|
||||||
if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) && GetPackageInfo(bpmpkg.PkgInfo.Name, rootDir).GetFullVersion() == bpmpkg.PkgInfo.GetFullVersion() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
operation.AppendAction(&InstallPackageAction{
|
|
||||||
File: pkg,
|
|
||||||
IsDependency: false,
|
|
||||||
BpmPackage: bpmpkg,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
var entry *RepositoryEntry
|
|
||||||
|
|
||||||
if e, _, err := GetRepositoryEntry(pkg); err == nil {
|
|
||||||
entry = e
|
|
||||||
} else if isVirtual, p := IsVirtualPackage(pkg, rootDir); isVirtual {
|
|
||||||
entry, _, err = GetRepositoryEntry(p)
|
|
||||||
if err != nil {
|
|
||||||
pkgsNotFound = append(pkgsNotFound, pkg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else if e := ResolveVirtualPackage(pkg); e != nil {
|
|
||||||
entry = e
|
|
||||||
} else {
|
|
||||||
pkgsNotFound = append(pkgsNotFound, pkg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if reinstallMethod == ReinstallMethodNone && IsPackageInstalled(entry.Info.Name, rootDir) && GetPackageInfo(entry.Info.Name, rootDir).GetFullVersion() == entry.Info.GetFullVersion() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
operation.AppendAction(&FetchPackageAction{
|
|
||||||
IsDependency: false,
|
|
||||||
RepositoryEntry: entry,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return error if not all packages are found
|
|
||||||
if len(pkgsNotFound) != 0 {
|
|
||||||
return nil, PackageNotFoundErr{pkgsNotFound}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve dependencies
|
|
||||||
err = operation.ResolveDependencies(reinstallMethod == ReinstallMethodAll, installOptionalDependencies, verbose)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not resolve dependencies: %s", err)
|
|
||||||
}
|
|
||||||
if len(operation.UnresolvedDepends) != 0 {
|
|
||||||
if !forceInstallation {
|
|
||||||
return nil, DependencyNotFoundErr{operation.UnresolvedDepends}
|
|
||||||
} else if verbose {
|
|
||||||
log.Printf("Warning: %s", DependencyNotFoundErr{operation.UnresolvedDepends})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace obsolete packages
|
|
||||||
operation.ReplaceObsoletePackages()
|
|
||||||
|
|
||||||
// Check for conflicts
|
|
||||||
conflicts, err := operation.CheckForConflicts()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not complete package conflict check: %s", err)
|
|
||||||
}
|
|
||||||
if len(conflicts) > 0 {
|
|
||||||
err = nil
|
|
||||||
for pkg, conflict := range conflicts {
|
|
||||||
err = errors.Join(err, PackageConflictErr{pkg, conflict})
|
|
||||||
}
|
|
||||||
if !forceInstallation {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
log.Printf("Warning: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return operation, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemovePackages removes the specified packages from the given root directory
|
|
||||||
func RemovePackages(rootDir string, removeUnusedPackagesOnly, cleanupDependencies, verbose bool, packages ...string) (operation *BPMOperation, err error) {
|
|
||||||
operation = &BPMOperation{
|
|
||||||
Actions: make([]OperationAction, 0),
|
|
||||||
UnresolvedDepends: make([]string, 0),
|
|
||||||
Changes: make(map[string]string),
|
|
||||||
RootDir: rootDir,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for packages
|
|
||||||
for _, pkg := range packages {
|
|
||||||
bpmpkg := GetPackage(pkg, 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(verbose)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not perform cleanup for operation: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return operation, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CleanupPackages finds packages installed as dependencies which are no longer required by the rest of the system in the given root directory
|
|
||||||
func CleanupPackages(rootDir string, verbose bool) (operation *BPMOperation, err error) {
|
|
||||||
operation = &BPMOperation{
|
|
||||||
Actions: make([]OperationAction, 0),
|
|
||||||
UnresolvedDepends: make([]string, 0),
|
|
||||||
Changes: make(map[string]string),
|
|
||||||
RootDir: rootDir,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do package cleanup
|
|
||||||
err = operation.Cleanup(verbose)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not perform cleanup for operation: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return operation, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdatePackages fetches the newest versions of all installed packages from
|
|
||||||
func UpdatePackages(rootDir string, syncDatabase bool, installOptionalDependencies, forceInstallation, verbose bool) (operation *BPMOperation, err error) {
|
|
||||||
// Sync repositories
|
|
||||||
if syncDatabase {
|
|
||||||
err := SyncDatabase(verbose)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not sync local database: %s", err)
|
|
||||||
}
|
|
||||||
if verbose {
|
|
||||||
fmt.Println("All package databases synced successfully!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reload config and local databases
|
|
||||||
err = ReadConfig()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not read BPM config: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get installed packages and check for updates
|
|
||||||
pkgs, err := GetInstalledPackages(rootDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not get installed packages: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
operation = &BPMOperation{
|
|
||||||
Actions: make([]OperationAction, 0),
|
|
||||||
UnresolvedDepends: make([]string, 0),
|
|
||||||
Changes: make(map[string]string),
|
|
||||||
RootDir: rootDir,
|
|
||||||
ForceInstallationReason: InstallationReasonUnknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for packages
|
|
||||||
for _, pkg := range pkgs {
|
|
||||||
if slices.Contains(BPMConfig.IgnorePackages, pkg) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var entry *RepositoryEntry
|
|
||||||
// Check if installed package can be replaced and install that instead
|
|
||||||
if e := FindReplacement(pkg); e != nil {
|
|
||||||
entry = e
|
|
||||||
} else if entry, _, err = GetRepositoryEntry(pkg); err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
installedInfo := GetPackageInfo(pkg, rootDir)
|
|
||||||
if installedInfo == nil {
|
|
||||||
return nil, fmt.Errorf("could not get package info for package (%s)", pkg)
|
|
||||||
} else {
|
|
||||||
comparison := ComparePackageVersions(*entry.Info, *installedInfo)
|
|
||||||
if comparison > 0 {
|
|
||||||
operation.AppendAction(&FetchPackageAction{
|
|
||||||
IsDependency: false,
|
|
||||||
RepositoryEntry: entry,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for new dependencies in updated packages
|
|
||||||
err = operation.ResolveDependencies(false, installOptionalDependencies, verbose)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not resolve dependencies: %s", err)
|
|
||||||
}
|
|
||||||
if len(operation.UnresolvedDepends) != 0 {
|
|
||||||
if !forceInstallation {
|
|
||||||
return nil, DependencyNotFoundErr{operation.UnresolvedDepends}
|
|
||||||
} else if verbose {
|
|
||||||
log.Printf("Warning: %s", DependencyNotFoundErr{operation.UnresolvedDepends})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace obsolete packages
|
|
||||||
operation.ReplaceObsoletePackages()
|
|
||||||
return operation, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyncDatabase syncs all databases declared in /etc/bpm.conf
|
|
||||||
func SyncDatabase(verbose bool) (err error) {
|
|
||||||
for _, repo := range BPMConfig.Repositories {
|
|
||||||
if verbose {
|
|
||||||
fmt.Printf("Fetching package database for repository (%s)...\n", repo.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := repo.SyncLocalDatabase()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
module git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib
|
|
||||||
|
|
||||||
go 1.23
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f
|
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
|
||||||
)
|
|
@ -1,155 +0,0 @@
|
|||||||
package bpmlib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BPMHook struct {
|
|
||||||
SourcePath string
|
|
||||||
SourceContent string
|
|
||||||
TriggerOperations []string `yaml:"trigger_operations"`
|
|
||||||
TargetType string `yaml:"target_type"`
|
|
||||||
Targets []string `yaml:"targets"`
|
|
||||||
Depends []string `yaml:"depends"`
|
|
||||||
Run string `yaml:"run"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// createHook returns a BPMHook instance based on the content of the given string
|
|
||||||
func createHook(sourcePath string) (*BPMHook, error) {
|
|
||||||
// Read hook from source path
|
|
||||||
bytes, err := os.ReadFile(sourcePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create base hook structure
|
|
||||||
hook := &BPMHook{
|
|
||||||
SourcePath: sourcePath,
|
|
||||||
SourceContent: string(bytes),
|
|
||||||
TriggerOperations: nil,
|
|
||||||
TargetType: "",
|
|
||||||
Targets: nil,
|
|
||||||
Depends: nil,
|
|
||||||
Run: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal yaml string
|
|
||||||
err = yaml.Unmarshal(bytes, hook)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure hook is valid
|
|
||||||
if err := hook.IsValid(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return hook, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValid ensures hook is valid
|
|
||||||
func (hook *BPMHook) IsValid() error {
|
|
||||||
ValidOperations := []string{"install", "upgrade", "remove"}
|
|
||||||
|
|
||||||
// Return error if any trigger operation is not valid or none are given
|
|
||||||
if len(hook.TriggerOperations) == 0 {
|
|
||||||
return errors.New("no trigger operations specified")
|
|
||||||
}
|
|
||||||
for _, operation := range hook.TriggerOperations {
|
|
||||||
if !slices.Contains(ValidOperations, operation) {
|
|
||||||
return errors.New("trigger operation '" + operation + "' is not valid")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if hook.TargetType != "package" && hook.TargetType != "path" {
|
|
||||||
return errors.New("target type '" + hook.TargetType + "' is not valid")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(hook.Run) == 0 {
|
|
||||||
return errors.New("command to run is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return nil as hook is valid
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute hook if all conditions are met
|
|
||||||
func (hook *BPMHook) Execute(packageChanges map[string]string, verbose bool, rootDir string) error {
|
|
||||||
// Check if package dependencies are met
|
|
||||||
installedPackages, err := GetInstalledPackages(rootDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, depend := range hook.Depends {
|
|
||||||
if !slices.Contains(installedPackages, depend) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get modified files slice
|
|
||||||
modifiedFiles := make([]*PackageFileEntry, 0)
|
|
||||||
for pkg := range packageChanges {
|
|
||||||
modifiedFiles = append(modifiedFiles, GetPackageFiles(pkg, rootDir)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if any targets are met
|
|
||||||
targetMet := false
|
|
||||||
for _, target := range hook.Targets {
|
|
||||||
if targetMet {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if hook.TargetType == "package" {
|
|
||||||
for change, operation := range packageChanges {
|
|
||||||
if target == change && slices.Contains(hook.TriggerOperations, operation) {
|
|
||||||
targetMet = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
glob, err := filepath.Glob(path.Join(rootDir, target))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, change := range modifiedFiles {
|
|
||||||
if slices.Contains(glob, path.Join(rootDir, change.Path)) {
|
|
||||||
targetMet = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !targetMet {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the command
|
|
||||||
splitCommand := strings.Split(hook.Run, " ")
|
|
||||||
cmd := exec.Command(splitCommand[0], splitCommand[1:]...)
|
|
||||||
// Setup subprocess environment
|
|
||||||
cmd.Dir = "/"
|
|
||||||
// Run hook in chroot if using the -R flag
|
|
||||||
if rootDir != "/" {
|
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: rootDir}
|
|
||||||
}
|
|
||||||
|
|
||||||
if verbose {
|
|
||||||
fmt.Printf("Running hook (%s) with run command: %s\n", hook.SourcePath, strings.Join(splitCommand, " "))
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,528 +0,0 @@
|
|||||||
package bpmlib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BPMOperation struct {
|
|
||||||
Actions []OperationAction
|
|
||||||
UnresolvedDepends []string
|
|
||||||
Changes map[string]string
|
|
||||||
RootDir string
|
|
||||||
ForceInstallationReason InstallationReason
|
|
||||||
}
|
|
||||||
|
|
||||||
func (operation *BPMOperation) ActionsContainPackage(pkg string) bool {
|
|
||||||
for _, action := range operation.Actions {
|
|
||||||
if action.GetActionType() == "install" {
|
|
||||||
if action.(*InstallPackageAction).BpmPackage.PkgInfo.Name == pkg {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else if action.GetActionType() == "fetch" {
|
|
||||||
if action.(*FetchPackageAction).RepositoryEntry.Info.Name == pkg {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else if action.GetActionType() == "remove" {
|
|
||||||
if action.(*RemovePackageAction).BpmPackage.PkgInfo.Name == pkg {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (operation *BPMOperation) AppendAction(action OperationAction) {
|
|
||||||
operation.InsertActionAt(len(operation.Actions), action)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (operation *BPMOperation) InsertActionAt(index int, action OperationAction) {
|
|
||||||
if len(operation.Actions) == index { // nil or empty slice or after last element
|
|
||||||
operation.Actions = append(operation.Actions, action)
|
|
||||||
} else {
|
|
||||||
operation.Actions = append(operation.Actions[:index+1], operation.Actions[index:]...) // index < len(a)
|
|
||||||
operation.Actions[index] = action
|
|
||||||
}
|
|
||||||
|
|
||||||
if action.GetActionType() == "install" {
|
|
||||||
pkgInfo := action.(*InstallPackageAction).BpmPackage.PkgInfo
|
|
||||||
if !IsPackageInstalled(pkgInfo.Name, operation.RootDir) {
|
|
||||||
operation.Changes[pkgInfo.Name] = "install"
|
|
||||||
} else {
|
|
||||||
operation.Changes[pkgInfo.Name] = "upgrade"
|
|
||||||
}
|
|
||||||
} else if action.GetActionType() == "fetch" {
|
|
||||||
pkgInfo := action.(*FetchPackageAction).RepositoryEntry.Info
|
|
||||||
if !IsPackageInstalled(pkgInfo.Name, operation.RootDir) {
|
|
||||||
operation.Changes[pkgInfo.Name] = "install"
|
|
||||||
} else {
|
|
||||||
operation.Changes[pkgInfo.Name] = "upgrade"
|
|
||||||
}
|
|
||||||
} else if action.GetActionType() == "remove" {
|
|
||||||
operation.Changes[action.(*RemovePackageAction).BpmPackage.PkgInfo.Name] = "remove"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (operation *BPMOperation) RemoveAction(pkg, actionType string) {
|
|
||||||
operation.Actions = slices.DeleteFunc(operation.Actions, func(a OperationAction) bool {
|
|
||||||
if a.GetActionType() != actionType {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if a.GetActionType() == "install" {
|
|
||||||
return a.(*InstallPackageAction).BpmPackage.PkgInfo.Name == pkg
|
|
||||||
} else if a.GetActionType() == "fetch" {
|
|
||||||
return a.(*FetchPackageAction).RepositoryEntry.Info.Name == pkg
|
|
||||||
} else if a.GetActionType() == "remove" {
|
|
||||||
return a.(*RemovePackageAction).BpmPackage.PkgInfo.Name == pkg
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (operation *BPMOperation) GetTotalDownloadSize() uint64 {
|
|
||||||
var ret uint64 = 0
|
|
||||||
for _, action := range operation.Actions {
|
|
||||||
if action.GetActionType() == "fetch" {
|
|
||||||
ret += action.(*FetchPackageAction).RepositoryEntry.DownloadSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (operation *BPMOperation) GetTotalInstalledSize() uint64 {
|
|
||||||
var ret uint64 = 0
|
|
||||||
for _, action := range operation.Actions {
|
|
||||||
if action.GetActionType() == "install" {
|
|
||||||
ret += action.(*InstallPackageAction).BpmPackage.GetInstalledSize()
|
|
||||||
} else if action.GetActionType() == "fetch" {
|
|
||||||
ret += action.(*FetchPackageAction).RepositoryEntry.InstalledSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (operation *BPMOperation) GetFinalActionSize(rootDir string) int64 {
|
|
||||||
var ret int64 = 0
|
|
||||||
for _, action := range operation.Actions {
|
|
||||||
if action.GetActionType() == "install" {
|
|
||||||
ret += int64(action.(*InstallPackageAction).BpmPackage.GetInstalledSize())
|
|
||||||
if IsPackageInstalled(action.(*InstallPackageAction).BpmPackage.PkgInfo.Name, rootDir) {
|
|
||||||
ret -= int64(GetPackage(action.(*InstallPackageAction).BpmPackage.PkgInfo.Name, rootDir).GetInstalledSize())
|
|
||||||
}
|
|
||||||
} else if action.GetActionType() == "fetch" {
|
|
||||||
ret += int64(action.(*FetchPackageAction).RepositoryEntry.InstalledSize)
|
|
||||||
} else if action.GetActionType() == "remove" {
|
|
||||||
ret -= int64(action.(*RemovePackageAction).BpmPackage.GetInstalledSize())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, installOptionalDependencies, verbose bool) error {
|
|
||||||
pos := 0
|
|
||||||
for _, value := range slices.Clone(operation.Actions) {
|
|
||||||
var pkgInfo *PackageInfo
|
|
||||||
if value.GetActionType() == "install" {
|
|
||||||
action := value.(*InstallPackageAction)
|
|
||||||
pkgInfo = action.BpmPackage.PkgInfo
|
|
||||||
} else if value.GetActionType() == "fetch" {
|
|
||||||
action := value.(*FetchPackageAction)
|
|
||||||
pkgInfo = action.RepositoryEntry.Info
|
|
||||||
} else {
|
|
||||||
pos++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
resolved, unresolved := pkgInfo.ResolveDependencies(&[]string{}, &[]string{}, pkgInfo.Type == "source", installOptionalDependencies, !reinstallDependencies, verbose, operation.RootDir)
|
|
||||||
|
|
||||||
operation.UnresolvedDepends = append(operation.UnresolvedDepends, unresolved...)
|
|
||||||
|
|
||||||
for _, depend := range resolved {
|
|
||||||
if !operation.ActionsContainPackage(depend) && depend != pkgInfo.Name {
|
|
||||||
if !reinstallDependencies && IsPackageInstalled(depend, operation.RootDir) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
entry, _, err := GetRepositoryEntry(depend)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("could not get repository entry for package (" + depend + ")")
|
|
||||||
}
|
|
||||||
operation.InsertActionAt(pos, &FetchPackageAction{
|
|
||||||
IsDependency: true,
|
|
||||||
RepositoryEntry: entry,
|
|
||||||
})
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (operation *BPMOperation) RemoveNeededPackages() error {
|
|
||||||
removeActions := make(map[string]*RemovePackageAction)
|
|
||||||
for _, action := range slices.Clone(operation.Actions) {
|
|
||||||
if action.GetActionType() == "remove" {
|
|
||||||
removeActions[action.(*RemovePackageAction).BpmPackage.PkgInfo.Name] = action.(*RemovePackageAction)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for pkg, action := range removeActions {
|
|
||||||
dependants, err := action.BpmPackage.PkgInfo.GetDependants(operation.RootDir)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("could not get dependant packages for package (" + pkg + ")")
|
|
||||||
}
|
|
||||||
dependants = slices.DeleteFunc(dependants, func(d string) bool {
|
|
||||||
if _, ok := removeActions[d]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
if len(dependants) != 0 {
|
|
||||||
operation.RemoveAction(pkg, action.GetActionType())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (operation *BPMOperation) Cleanup(verbose bool) error {
|
|
||||||
// Get all installed packages
|
|
||||||
installedPackageNames, err := GetInstalledPackages(operation.RootDir)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not get installed packages: %s", err)
|
|
||||||
}
|
|
||||||
installedPackages := make([]*PackageInfo, len(installedPackageNames))
|
|
||||||
for i, value := range installedPackageNames {
|
|
||||||
bpmpkg := GetPackage(value, operation.RootDir)
|
|
||||||
if bpmpkg == nil {
|
|
||||||
return errors.New("could not find installed package (" + value + ")")
|
|
||||||
}
|
|
||||||
installedPackages[i] = bpmpkg.PkgInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get packages to remove
|
|
||||||
removeActions := make(map[string]*RemovePackageAction)
|
|
||||||
for _, action := range slices.Clone(operation.Actions) {
|
|
||||||
if action.GetActionType() == "remove" {
|
|
||||||
removeActions[action.(*RemovePackageAction).BpmPackage.PkgInfo.Name] = action.(*RemovePackageAction)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get manually installed packages, resolve all their dependencies and add them to the keepPackages slice
|
|
||||||
keepPackages := make([]string, 0)
|
|
||||||
for _, pkg := range slices.Clone(installedPackages) {
|
|
||||||
if GetInstallationReason(pkg.Name, operation.RootDir) != InstallationReasonManual {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not resolve dependencies or add package to keepPackages slice if package removal action exists for it
|
|
||||||
if _, ok := removeActions[pkg.Name]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
keepPackages = append(keepPackages, pkg.Name)
|
|
||||||
resolved, _ := pkg.ResolveDependencies(&[]string{}, &[]string{}, false, true, false, verbose, operation.RootDir)
|
|
||||||
for _, value := range resolved {
|
|
||||||
if !slices.Contains(keepPackages, value) && slices.Contains(installedPackageNames, value) {
|
|
||||||
keepPackages = append(keepPackages, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all installed packages that are not in the keepPackages slice and add them to the BPM operation
|
|
||||||
for _, pkg := range installedPackageNames {
|
|
||||||
// Do not add package removal action if there already is one
|
|
||||||
if _, ok := removeActions[pkg]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !slices.Contains(keepPackages, pkg) {
|
|
||||||
bpmpkg := GetPackage(pkg, operation.RootDir)
|
|
||||||
if bpmpkg == nil {
|
|
||||||
return errors.New("Error: could not find installed package (" + pkg + ")")
|
|
||||||
}
|
|
||||||
operation.Actions = append(operation.Actions, &RemovePackageAction{BpmPackage: bpmpkg})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (operation *BPMOperation) ReplaceObsoletePackages() {
|
|
||||||
for _, value := range slices.Clone(operation.Actions) {
|
|
||||||
var pkgInfo *PackageInfo
|
|
||||||
if value.GetActionType() == "install" {
|
|
||||||
action := value.(*InstallPackageAction)
|
|
||||||
pkgInfo = action.BpmPackage.PkgInfo
|
|
||||||
|
|
||||||
} else if value.GetActionType() == "fetch" {
|
|
||||||
action := value.(*FetchPackageAction)
|
|
||||||
pkgInfo = action.RepositoryEntry.Info
|
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range pkgInfo.Replaces {
|
|
||||||
if bpmpkg := GetPackage(r, operation.RootDir); bpmpkg != nil && !operation.ActionsContainPackage(bpmpkg.PkgInfo.Name) {
|
|
||||||
operation.InsertActionAt(0, &RemovePackageAction{
|
|
||||||
BpmPackage: bpmpkg,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (operation *BPMOperation) CheckForConflicts() (map[string][]string, error) {
|
|
||||||
conflicts := make(map[string][]string)
|
|
||||||
installedPackages, err := GetInstalledPackages(operation.RootDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
allPackages := make([]*PackageInfo, len(installedPackages))
|
|
||||||
for i, value := range installedPackages {
|
|
||||||
bpmpkg := GetPackage(value, operation.RootDir)
|
|
||||||
if bpmpkg == nil {
|
|
||||||
return nil, errors.New(fmt.Sprintf("could not find installed package (%s)", value))
|
|
||||||
}
|
|
||||||
allPackages[i] = bpmpkg.PkgInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add all new packages to the allPackages slice
|
|
||||||
for _, value := range slices.Clone(operation.Actions) {
|
|
||||||
if value.GetActionType() == "install" {
|
|
||||||
action := value.(*InstallPackageAction)
|
|
||||||
pkgInfo := action.BpmPackage.PkgInfo
|
|
||||||
allPackages = append(allPackages, pkgInfo)
|
|
||||||
} else if value.GetActionType() == "fetch" {
|
|
||||||
action := value.(*FetchPackageAction)
|
|
||||||
pkgInfo := action.RepositoryEntry.Info
|
|
||||||
allPackages = append(allPackages, pkgInfo)
|
|
||||||
} else if value.GetActionType() == "remove" {
|
|
||||||
action := value.(*RemovePackageAction)
|
|
||||||
pkgInfo := action.BpmPackage.PkgInfo
|
|
||||||
for i := len(allPackages) - 1; i >= 0; i-- {
|
|
||||||
info := allPackages[i]
|
|
||||||
if info.Name == pkgInfo.Name {
|
|
||||||
allPackages = append(allPackages[:i], allPackages[i+1:]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, value := range allPackages {
|
|
||||||
for _, conflict := range value.Conflicts {
|
|
||||||
if slices.ContainsFunc(allPackages, func(info *PackageInfo) bool {
|
|
||||||
return info.Name == conflict
|
|
||||||
}) {
|
|
||||||
conflicts[value.Name] = append(conflicts[value.Name], conflict)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return conflicts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (operation *BPMOperation) ShowOperationSummary() {
|
|
||||||
if len(operation.Actions) == 0 {
|
|
||||||
fmt.Println("No action needs to be taken")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, value := range operation.Actions {
|
|
||||||
var pkgInfo *PackageInfo
|
|
||||||
if value.GetActionType() == "install" {
|
|
||||||
pkgInfo = value.(*InstallPackageAction).BpmPackage.PkgInfo
|
|
||||||
} else if value.GetActionType() == "fetch" {
|
|
||||||
pkgInfo = value.(*FetchPackageAction).RepositoryEntry.Info
|
|
||||||
} else {
|
|
||||||
pkgInfo = value.(*RemovePackageAction).BpmPackage.PkgInfo
|
|
||||||
fmt.Printf("%s: %s (Remove)\n", pkgInfo.Name, pkgInfo.GetFullVersion())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
installedInfo := GetPackageInfo(pkgInfo.Name, operation.RootDir)
|
|
||||||
sourceInfo := ""
|
|
||||||
if pkgInfo.Type == "source" {
|
|
||||||
sourceInfo = "(From Source)"
|
|
||||||
}
|
|
||||||
|
|
||||||
if installedInfo == nil {
|
|
||||||
fmt.Printf("%s: %s (Install) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), sourceInfo)
|
|
||||||
} else {
|
|
||||||
comparison := ComparePackageVersions(*pkgInfo, *installedInfo)
|
|
||||||
if comparison < 0 {
|
|
||||||
fmt.Printf("%s: %s -> %s (Downgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), sourceInfo)
|
|
||||||
} else if comparison > 0 {
|
|
||||||
fmt.Printf("%s: %s -> %s (Upgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), sourceInfo)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("%s: %s (Reinstall) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), sourceInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if operation.RootDir != "/" {
|
|
||||||
fmt.Println("Warning: Operating in " + operation.RootDir)
|
|
||||||
}
|
|
||||||
if operation.GetTotalDownloadSize() > 0 {
|
|
||||||
fmt.Printf("%s will be downloaded to complete this operation\n", unsignedBytesToHumanReadable(operation.GetTotalDownloadSize()))
|
|
||||||
}
|
|
||||||
if operation.GetFinalActionSize(operation.RootDir) > 0 {
|
|
||||||
fmt.Printf("A total of %s will be installed after the operation finishes\n", bytesToHumanReadable(operation.GetFinalActionSize(operation.RootDir)))
|
|
||||||
} else if operation.GetFinalActionSize(operation.RootDir) < 0 {
|
|
||||||
fmt.Printf("A total of %s will be freed after the operation finishes\n", strings.TrimPrefix(bytesToHumanReadable(operation.GetFinalActionSize(operation.RootDir)), "-"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get directory entries in hooks directory
|
|
||||||
dirEntries, err := os.ReadDir(path.Join(operation.RootDir, "var/lib/bpm/hooks"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find all hooks, validate and execute them
|
|
||||||
for _, entry := range dirEntries {
|
|
||||||
if entry.Type().IsRegular() && strings.HasSuffix(entry.Name(), ".bpmhook") {
|
|
||||||
hook, err := createHook(path.Join(operation.RootDir, "var/lib/bpm/hooks", entry.Name()))
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error while reading hook (%s): %s", entry.Name(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = hook.Execute(operation.Changes, verbose, operation.RootDir)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Warning: could not execute hook (%s): %s\n", entry.Name(), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (operation *BPMOperation) Execute(verbose, force bool) error {
|
|
||||||
// Fetch packages from repositories
|
|
||||||
if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool {
|
|
||||||
return action.GetActionType() == "fetch"
|
|
||||||
}) {
|
|
||||||
fmt.Println("Fetching packages from available repositories...")
|
|
||||||
for i, action := range operation.Actions {
|
|
||||||
if action.GetActionType() != "fetch" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
entry := action.(*FetchPackageAction).RepositoryEntry
|
|
||||||
fetchedPackage, err := entry.Repository.FetchPackage(entry.Info.Name)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err))
|
|
||||||
}
|
|
||||||
bpmpkg, err := ReadPackage(fetchedPackage)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err))
|
|
||||||
}
|
|
||||||
fmt.Printf("Package (%s) was successfully fetched!\n", bpmpkg.PkgInfo.Name)
|
|
||||||
operation.Actions[i] = &InstallPackageAction{
|
|
||||||
File: fetchedPackage,
|
|
||||||
IsDependency: action.(*FetchPackageAction).IsDependency,
|
|
||||||
BpmPackage: bpmpkg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine words to be used for the following message
|
|
||||||
words := make([]string, 0)
|
|
||||||
if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool {
|
|
||||||
return action.GetActionType() == "install"
|
|
||||||
}) {
|
|
||||||
words = append(words, "Installing")
|
|
||||||
}
|
|
||||||
|
|
||||||
if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool {
|
|
||||||
return action.GetActionType() == "remove"
|
|
||||||
}) {
|
|
||||||
words = append(words, "Removing")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(words) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
fmt.Printf("%s packages...\n", strings.Join(words, "/"))
|
|
||||||
|
|
||||||
// Installing/Removing packages from system
|
|
||||||
for _, action := range operation.Actions {
|
|
||||||
if action.GetActionType() == "remove" {
|
|
||||||
pkgInfo := action.(*RemovePackageAction).BpmPackage.PkgInfo
|
|
||||||
err := removePackage(pkgInfo.Name, verbose, operation.RootDir)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New(fmt.Sprintf("could not remove package (%s): %s\n", pkgInfo.Name, err))
|
|
||||||
}
|
|
||||||
} else if action.GetActionType() == "install" {
|
|
||||||
value := action.(*InstallPackageAction)
|
|
||||||
bpmpkg := value.BpmPackage
|
|
||||||
isReinstall := IsPackageInstalled(bpmpkg.PkgInfo.Name, operation.RootDir)
|
|
||||||
var err error
|
|
||||||
if value.IsDependency {
|
|
||||||
err = installPackage(value.File, operation.RootDir, verbose, true)
|
|
||||||
} else {
|
|
||||||
err = installPackage(value.File, operation.RootDir, verbose, force)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return errors.New(fmt.Sprintf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err))
|
|
||||||
}
|
|
||||||
if operation.ForceInstallationReason != InstallationReasonUnknown && !value.IsDependency {
|
|
||||||
err := SetInstallationReason(bpmpkg.PkgInfo.Name, operation.ForceInstallationReason, operation.RootDir)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err))
|
|
||||||
}
|
|
||||||
} else if value.IsDependency && !isReinstall {
|
|
||||||
err := SetInstallationReason(bpmpkg.PkgInfo.Name, InstallationReasonDependency, operation.RootDir)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Printf("Package (%s) was successfully installed\n", bpmpkg.PkgInfo.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println("Operation complete!")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type OperationAction interface {
|
|
||||||
GetActionType() string
|
|
||||||
}
|
|
||||||
|
|
||||||
type InstallPackageAction struct {
|
|
||||||
File string
|
|
||||||
IsDependency bool
|
|
||||||
BpmPackage *BPMPackage
|
|
||||||
}
|
|
||||||
|
|
||||||
func (action *InstallPackageAction) GetActionType() string {
|
|
||||||
return "install"
|
|
||||||
}
|
|
||||||
|
|
||||||
type FetchPackageAction struct {
|
|
||||||
IsDependency bool
|
|
||||||
RepositoryEntry *RepositoryEntry
|
|
||||||
}
|
|
||||||
|
|
||||||
func (action *FetchPackageAction) GetActionType() string {
|
|
||||||
return "fetch"
|
|
||||||
}
|
|
||||||
|
|
||||||
type RemovePackageAction struct {
|
|
||||||
BpmPackage *BPMPackage
|
|
||||||
}
|
|
||||||
|
|
||||||
func (action *RemovePackageAction) GetActionType() string {
|
|
||||||
return "remove"
|
|
||||||
}
|
|
49
utils/config.go
Normal file
49
utils/config.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BPMConfigStruct struct {
|
||||||
|
CompilationEnv []string `yaml:"compilation_env"`
|
||||||
|
SilentCompilation bool `yaml:"silent_compilation"`
|
||||||
|
BinaryOutputDir string `yaml:"binary_output_dir"`
|
||||||
|
CompilationDir string `yaml:"compilation_dir"`
|
||||||
|
Repositories []*Repository `yaml:"repositories"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var BPMConfig BPMConfigStruct
|
||||||
|
|
||||||
|
func ReadConfig() {
|
||||||
|
if _, err := os.Stat("/etc/bpm.conf"); os.IsNotExist(err) {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
bytes, err := os.ReadFile("/etc/bpm.conf")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
BPMConfig = BPMConfigStruct{
|
||||||
|
CompilationEnv: make([]string, 0),
|
||||||
|
SilentCompilation: false,
|
||||||
|
BinaryOutputDir: "/var/lib/bpm/compiled/",
|
||||||
|
CompilationDir: "/var/tmp/",
|
||||||
|
}
|
||||||
|
err = yaml.Unmarshal(bytes, &BPMConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
for i := len(BPMConfig.Repositories) - 1; i >= 0; i-- {
|
||||||
|
if BPMConfig.Repositories[i].Disabled != nil && *BPMConfig.Repositories[i].Disabled {
|
||||||
|
BPMConfig.Repositories = append(BPMConfig.Repositories[:i], BPMConfig.Repositories[i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, repo := range BPMConfig.Repositories {
|
||||||
|
repo.Entries = make(map[string]*RepositoryEntry)
|
||||||
|
err := repo.ReadLocalDatabase()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package bpmlib
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
@ -7,12 +7,12 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tarballFileReader struct {
|
type TarballFileReader struct {
|
||||||
tarReader *tar.Reader
|
tarReader *tar.Reader
|
||||||
file *os.File
|
file *os.File
|
||||||
}
|
}
|
||||||
|
|
||||||
func readTarballContent(tarballPath, fileToExtract string) (*tarballFileReader, error) {
|
func ReadTarballContent(tarballPath, fileToExtract string) (*TarballFileReader, error) {
|
||||||
file, err := os.Open(tarballPath)
|
file, err := os.Open(tarballPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -32,7 +32,7 @@ func readTarballContent(tarballPath, fileToExtract string) (*tarballFileReader,
|
|||||||
return nil, errors.New("file to extract must be a regular file")
|
return nil, errors.New("file to extract must be a regular file")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &tarballFileReader{
|
return &TarballFileReader{
|
||||||
tarReader: tr,
|
tarReader: tr,
|
||||||
file: file,
|
file: file,
|
||||||
}, nil
|
}, nil
|
@ -1,4 +1,4 @@
|
|||||||
package bpmlib
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -55,7 +55,7 @@ func stringSliceRemove(s []string, r string) []string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func unsignedBytesToHumanReadable(b uint64) string {
|
func UnsignedBytesToHumanReadable(b uint64) string {
|
||||||
bf := float64(b)
|
bf := float64(b)
|
||||||
for _, unit := range []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} {
|
for _, unit := range []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} {
|
||||||
if math.Abs(bf) < 1024.0 {
|
if math.Abs(bf) < 1024.0 {
|
||||||
@ -66,7 +66,7 @@ func unsignedBytesToHumanReadable(b uint64) string {
|
|||||||
return fmt.Sprintf("%.1fYiB", bf)
|
return fmt.Sprintf("%.1fYiB", bf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func bytesToHumanReadable(b int64) string {
|
func BytesToHumanReadable(b int64) string {
|
||||||
bf := float64(b)
|
bf := float64(b)
|
||||||
for _, unit := range []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} {
|
for _, unit := range []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} {
|
||||||
if math.Abs(bf) < 1024.0 {
|
if math.Abs(bf) < 1024.0 {
|
289
utils/operations.go
Normal file
289
utils/operations.go
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BPMOperation struct {
|
||||||
|
Actions []OperationAction
|
||||||
|
UnresolvedDepends []string
|
||||||
|
RootDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (operation *BPMOperation) ActionsContainPackage(pkg string) bool {
|
||||||
|
for _, action := range operation.Actions {
|
||||||
|
if action.GetActionType() == "install" {
|
||||||
|
if action.(*InstallPackageAction).BpmPackage.PkgInfo.Name == pkg {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else if action.GetActionType() == "fetch" {
|
||||||
|
if action.(*FetchPackageAction).RepositoryEntry.Info.Name == pkg {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else if action.GetActionType() == "remove" {
|
||||||
|
if action.(*RemovePackageAction).BpmPackage.PkgInfo.Name == pkg {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (operation *BPMOperation) InsertActionAt(index int, action OperationAction) {
|
||||||
|
if len(operation.Actions) == index { // nil or empty slice or after last element
|
||||||
|
operation.Actions = append(operation.Actions, action)
|
||||||
|
}
|
||||||
|
operation.Actions = append(operation.Actions[:index+1], operation.Actions[index:]...) // index < len(a)
|
||||||
|
operation.Actions[index] = action
|
||||||
|
}
|
||||||
|
|
||||||
|
func (operation *BPMOperation) GetTotalDownloadSize() uint64 {
|
||||||
|
var ret uint64 = 0
|
||||||
|
for _, action := range operation.Actions {
|
||||||
|
if action.GetActionType() == "fetch" {
|
||||||
|
ret += action.(*FetchPackageAction).RepositoryEntry.DownloadSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (operation *BPMOperation) GetTotalInstalledSize() uint64 {
|
||||||
|
var ret uint64 = 0
|
||||||
|
for _, action := range operation.Actions {
|
||||||
|
if action.GetActionType() == "install" {
|
||||||
|
ret += action.(*InstallPackageAction).BpmPackage.GetInstalledSize()
|
||||||
|
} else if action.GetActionType() == "fetch" {
|
||||||
|
ret += action.(*FetchPackageAction).RepositoryEntry.InstalledSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (operation *BPMOperation) GetFinalActionSize(rootDir string) int64 {
|
||||||
|
var ret int64 = 0
|
||||||
|
for _, action := range operation.Actions {
|
||||||
|
if action.GetActionType() == "install" {
|
||||||
|
ret += int64(action.(*InstallPackageAction).BpmPackage.GetInstalledSize())
|
||||||
|
if IsPackageInstalled(action.(*InstallPackageAction).BpmPackage.PkgInfo.Name, rootDir) {
|
||||||
|
ret -= int64(GetPackage(action.(*InstallPackageAction).BpmPackage.PkgInfo.Name, rootDir).GetInstalledSize())
|
||||||
|
}
|
||||||
|
} else if action.GetActionType() == "fetch" {
|
||||||
|
ret += int64(action.(*FetchPackageAction).RepositoryEntry.InstalledSize)
|
||||||
|
} else if action.GetActionType() == "remove" {
|
||||||
|
ret -= int64(action.(*RemovePackageAction).BpmPackage.GetInstalledSize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (operation *BPMOperation) ResolveDependencies(reinstallDependencies, installOptionalDependencies, verbose bool) error {
|
||||||
|
pos := 0
|
||||||
|
for _, value := range slices.Clone(operation.Actions) {
|
||||||
|
var pkgInfo *PackageInfo
|
||||||
|
if value.GetActionType() == "install" {
|
||||||
|
action := value.(*InstallPackageAction)
|
||||||
|
pkgInfo = action.BpmPackage.PkgInfo
|
||||||
|
} else if value.GetActionType() == "fetch" {
|
||||||
|
action := value.(*FetchPackageAction)
|
||||||
|
pkgInfo = action.RepositoryEntry.Info
|
||||||
|
} else {
|
||||||
|
pos++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved, unresolved := pkgInfo.ResolveDependencies(&[]string{}, &[]string{}, pkgInfo.Type == "source", installOptionalDependencies, !reinstallDependencies, verbose, operation.RootDir)
|
||||||
|
|
||||||
|
operation.UnresolvedDepends = append(operation.UnresolvedDepends, unresolved...)
|
||||||
|
|
||||||
|
for _, depend := range resolved {
|
||||||
|
if !operation.ActionsContainPackage(depend) && depend != pkgInfo.Name {
|
||||||
|
if !reinstallDependencies && IsPackageInstalled(depend, operation.RootDir) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
entry, _, err := GetRepositoryEntry(depend)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("could not get repository entry for package (" + depend + ")")
|
||||||
|
}
|
||||||
|
operation.InsertActionAt(pos, &FetchPackageAction{
|
||||||
|
IsDependency: true,
|
||||||
|
RepositoryEntry: entry,
|
||||||
|
})
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (operation *BPMOperation) ShowOperationSummary() {
|
||||||
|
if len(operation.Actions) == 0 {
|
||||||
|
fmt.Println("All packages are up to date!")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, value := range operation.Actions {
|
||||||
|
var pkgInfo *PackageInfo
|
||||||
|
if value.GetActionType() == "install" {
|
||||||
|
pkgInfo = value.(*InstallPackageAction).BpmPackage.PkgInfo
|
||||||
|
} else if value.GetActionType() == "fetch" {
|
||||||
|
pkgInfo = value.(*FetchPackageAction).RepositoryEntry.Info
|
||||||
|
} else {
|
||||||
|
pkgInfo = value.(*RemovePackageAction).BpmPackage.PkgInfo
|
||||||
|
fmt.Printf("%s: %s (Remove)\n", pkgInfo.Name, pkgInfo.GetFullVersion())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
installedInfo := GetPackageInfo(pkgInfo.Name, operation.RootDir)
|
||||||
|
sourceInfo := ""
|
||||||
|
if pkgInfo.Type == "source" {
|
||||||
|
if operation.RootDir != "/" {
|
||||||
|
log.Fatalf("cannot compile and install source packages to a different root directory")
|
||||||
|
}
|
||||||
|
sourceInfo = "(From Source)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if installedInfo == nil {
|
||||||
|
fmt.Printf("%s: %s (Install) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), sourceInfo)
|
||||||
|
} else {
|
||||||
|
comparison := ComparePackageVersions(*pkgInfo, *installedInfo)
|
||||||
|
if comparison < 0 {
|
||||||
|
fmt.Printf("%s: %s -> %s (Downgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), sourceInfo)
|
||||||
|
} else if comparison > 0 {
|
||||||
|
fmt.Printf("%s: %s -> %s (Upgrade) %s\n", pkgInfo.Name, installedInfo.GetFullVersion(), pkgInfo.GetFullVersion(), sourceInfo)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s: %s (Reinstall) %s\n", pkgInfo.Name, pkgInfo.GetFullVersion(), sourceInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if operation.RootDir != "/" {
|
||||||
|
fmt.Println("Warning: Operating in " + operation.RootDir)
|
||||||
|
}
|
||||||
|
if operation.GetTotalDownloadSize() > 0 {
|
||||||
|
fmt.Printf("%s will be downloaded to complete this operation\n", UnsignedBytesToHumanReadable(operation.GetTotalDownloadSize()))
|
||||||
|
}
|
||||||
|
if operation.GetFinalActionSize(operation.RootDir) > 0 {
|
||||||
|
fmt.Printf("A total of %s will be installed after the operation finishes\n", BytesToHumanReadable(operation.GetFinalActionSize(operation.RootDir)))
|
||||||
|
} else if operation.GetFinalActionSize(operation.RootDir) < 0 {
|
||||||
|
fmt.Printf("A total of %s will be freed after the operation finishes\n", strings.TrimPrefix(BytesToHumanReadable(operation.GetFinalActionSize(operation.RootDir)), "-"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (operation *BPMOperation) Execute(verbose, force bool) error {
|
||||||
|
// Fetch packages from repositories
|
||||||
|
if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool {
|
||||||
|
return action.GetActionType() == "fetch"
|
||||||
|
}) {
|
||||||
|
fmt.Println("Fetching packages from available repositories...")
|
||||||
|
for i, action := range operation.Actions {
|
||||||
|
if action.GetActionType() != "fetch" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
entry := action.(*FetchPackageAction).RepositoryEntry
|
||||||
|
fetchedPackage, err := entry.Repository.FetchPackage(entry.Info.Name)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err))
|
||||||
|
}
|
||||||
|
bpmpkg, err := ReadPackage(fetchedPackage)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("could not fetch package (%s): %s\n", entry.Info.Name, err))
|
||||||
|
}
|
||||||
|
fmt.Printf("Package (%s) was successfully fetched!\n", bpmpkg.PkgInfo.Name)
|
||||||
|
operation.Actions[i] = &InstallPackageAction{
|
||||||
|
File: fetchedPackage,
|
||||||
|
IsDependency: action.(*FetchPackageAction).IsDependency,
|
||||||
|
BpmPackage: bpmpkg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine words to be used for the following message
|
||||||
|
words := make([]string, 0)
|
||||||
|
if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool {
|
||||||
|
return action.GetActionType() == "install"
|
||||||
|
}) {
|
||||||
|
words = append(words, "Installing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if slices.ContainsFunc(operation.Actions, func(action OperationAction) bool {
|
||||||
|
return action.GetActionType() == "remove"
|
||||||
|
}) {
|
||||||
|
words = append(words, "Removing")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(words) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fmt.Printf("%s packages...\n", strings.Join(words, "/"))
|
||||||
|
|
||||||
|
// Installing/Removing packages from system
|
||||||
|
for _, action := range operation.Actions {
|
||||||
|
if action.GetActionType() == "remove" {
|
||||||
|
pkgInfo := action.(*RemovePackageAction).BpmPackage.PkgInfo
|
||||||
|
err := RemovePackage(pkgInfo.Name, verbose, operation.RootDir)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("could not remove package (%s): %s\n", pkgInfo.Name, err))
|
||||||
|
}
|
||||||
|
} else if action.GetActionType() == "install" {
|
||||||
|
value := action.(*InstallPackageAction)
|
||||||
|
bpmpkg := value.BpmPackage
|
||||||
|
var err error
|
||||||
|
if value.IsDependency {
|
||||||
|
err = InstallPackage(value.File, operation.RootDir, verbose, true, false, false, false)
|
||||||
|
} else {
|
||||||
|
err = InstallPackage(value.File, operation.RootDir, verbose, force, false, false, false)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("could not install package (%s): %s\n", bpmpkg.PkgInfo.Name, err))
|
||||||
|
}
|
||||||
|
fmt.Printf("Package (%s) was successfully installed\n", bpmpkg.PkgInfo.Name)
|
||||||
|
if value.IsDependency {
|
||||||
|
err := SetInstallationReason(bpmpkg.PkgInfo.Name, Dependency, operation.RootDir)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(fmt.Sprintf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("Operation complete!")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type OperationAction interface {
|
||||||
|
GetActionType() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type InstallPackageAction struct {
|
||||||
|
File string
|
||||||
|
IsDependency bool
|
||||||
|
BpmPackage *BPMPackage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (action *InstallPackageAction) GetActionType() string {
|
||||||
|
return "install"
|
||||||
|
}
|
||||||
|
|
||||||
|
type FetchPackageAction struct {
|
||||||
|
IsDependency bool
|
||||||
|
RepositoryEntry *RepositoryEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
func (action *FetchPackageAction) GetActionType() string {
|
||||||
|
return "fetch"
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemovePackageAction struct {
|
||||||
|
BpmPackage *BPMPackage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (action *RemovePackageAction) GetActionType() string {
|
||||||
|
return "remove"
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package bpmlib
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
@ -8,13 +8,16 @@ import (
|
|||||||
version "github.com/knqyf263/go-rpm-version"
|
version "github.com/knqyf263/go-rpm-version"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BPMPackage struct {
|
type BPMPackage struct {
|
||||||
@ -36,7 +39,6 @@ type PackageInfo struct {
|
|||||||
MakeDepends []string `yaml:"make_depends,omitempty"`
|
MakeDepends []string `yaml:"make_depends,omitempty"`
|
||||||
OptionalDepends []string `yaml:"optional_depends,omitempty"`
|
OptionalDepends []string `yaml:"optional_depends,omitempty"`
|
||||||
Conflicts []string `yaml:"conflicts,omitempty"`
|
Conflicts []string `yaml:"conflicts,omitempty"`
|
||||||
Replaces []string `yaml:"replaces,omitempty"`
|
|
||||||
Provides []string `yaml:"provides,omitempty"`
|
Provides []string `yaml:"provides,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,9 +73,9 @@ func (pkgInfo *PackageInfo) GetFullVersion() string {
|
|||||||
type InstallationReason string
|
type InstallationReason string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
InstallationReasonManual InstallationReason = "manual"
|
Manual InstallationReason = "manual"
|
||||||
InstallationReasonDependency InstallationReason = "dependency"
|
Dependency InstallationReason = "dependency"
|
||||||
InstallationReasonUnknown InstallationReason = "unknown"
|
Unknown InstallationReason = "unknown"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ComparePackageVersions(info1, info2 PackageInfo) int {
|
func ComparePackageVersions(info1, info2 PackageInfo) int {
|
||||||
@ -87,19 +89,19 @@ func GetInstallationReason(pkg, rootDir string) InstallationReason {
|
|||||||
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)
|
||||||
if stat, err := os.Stat(path.Join(pkgDir, "installation_reason")); err != nil || stat.IsDir() {
|
if stat, err := os.Stat(path.Join(pkgDir, "installation_reason")); err != nil || stat.IsDir() {
|
||||||
return InstallationReasonManual
|
return Manual
|
||||||
}
|
}
|
||||||
b, err := os.ReadFile(path.Join(pkgDir, "installation_reason"))
|
b, err := os.ReadFile(path.Join(pkgDir, "installation_reason"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return InstallationReasonUnknown
|
return Unknown
|
||||||
}
|
}
|
||||||
reason := strings.TrimSpace(string(b))
|
reason := string(b)
|
||||||
if reason == "manual" {
|
if reason == "manual" {
|
||||||
return InstallationReasonManual
|
return Manual
|
||||||
} else if reason == "dependency" {
|
} else if reason == "dependency" {
|
||||||
return InstallationReasonDependency
|
return Dependency
|
||||||
}
|
}
|
||||||
return InstallationReasonUnknown
|
return Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetInstallationReason(pkg string, reason InstallationReason, rootDir string) error {
|
func SetInstallationReason(pkg string, reason InstallationReason, rootDir string) error {
|
||||||
@ -277,15 +279,15 @@ func ReadPackageScripts(filename string) (map[string]string, error) {
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type packageOperation uint8
|
type Operation uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
packageOperationInstall packageOperation = 0
|
Install Operation = 0
|
||||||
packageOperationUpdate = 1
|
Update = 1
|
||||||
packageOperationRemove = 2
|
Remove = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
func executePackageScripts(filename, rootDir string, operation packageOperation, postOperation bool) error {
|
func ExecutePackageScripts(filename, rootDir string, operation Operation, postOperation bool) error {
|
||||||
pkgInfo, err := ReadPackage(filename)
|
pkgInfo, err := ReadPackage(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -306,17 +308,22 @@ func executePackageScripts(filename, rootDir string, operation packageOperation,
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command("/bin/bash", temp.Name())
|
cmd := exec.Command("/bin/bash", temp.Name())
|
||||||
|
if !BPMConfig.SilentCompilation {
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
}
|
||||||
cmd.Dir = rootDir
|
cmd.Dir = rootDir
|
||||||
cmd.Env = os.Environ()
|
cmd.Env = os.Environ()
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir))
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", pkgInfo.PkgInfo.Name))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", pkgInfo.PkgInfo.Name))
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DESC=%s", pkgInfo.PkgInfo.Description))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DESC=%s", pkgInfo.PkgInfo.Description))
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", pkgInfo.PkgInfo.Version))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", pkgInfo.PkgInfo.Version))
|
||||||
if operation != packageOperationInstall {
|
if operation != Install {
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_OLD_VERSION=%s", GetPackageInfo(pkgInfo.PkgInfo.Name, rootDir).Version))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_OLD_VERSION=%s", GetPackageInfo(pkgInfo.PkgInfo.Name, rootDir).Version))
|
||||||
}
|
}
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", pkgInfo.PkgInfo.Revision))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", pkgInfo.PkgInfo.Revision))
|
||||||
if operation != packageOperationInstall {
|
if operation != Install {
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_OLD_REVISION=%d", GetPackageInfo(pkgInfo.PkgInfo.Name, rootDir).Revision))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_OLD_REVISION=%d", GetPackageInfo(pkgInfo.PkgInfo.Name, rootDir).Revision))
|
||||||
}
|
}
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", pkgInfo.PkgInfo.Url))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", pkgInfo.PkgInfo.Url))
|
||||||
@ -341,7 +348,7 @@ func executePackageScripts(filename, rootDir string, operation packageOperation,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if operation == packageOperationInstall {
|
if operation == Install {
|
||||||
if val, ok := scripts["pre_install.sh"]; !postOperation && ok {
|
if val, ok := scripts["pre_install.sh"]; !postOperation && ok {
|
||||||
err := run("pre_install.sh", val)
|
err := run("pre_install.sh", val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -354,7 +361,7 @@ func executePackageScripts(filename, rootDir string, operation packageOperation,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if operation == packageOperationUpdate {
|
} else if operation == Update {
|
||||||
if val, ok := scripts["pre_update.sh"]; !postOperation && ok {
|
if val, ok := scripts["pre_update.sh"]; !postOperation && ok {
|
||||||
err := run("pre_update.sh", val)
|
err := run("pre_update.sh", val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -367,7 +374,7 @@ func executePackageScripts(filename, rootDir string, operation packageOperation,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if operation == packageOperationRemove {
|
} else if operation == Remove {
|
||||||
if val, ok := scripts["pre_remove.sh"]; !postOperation && ok {
|
if val, ok := scripts["pre_remove.sh"]; !postOperation && ok {
|
||||||
err := run("pre_remove.sh", val)
|
err := run("pre_remove.sh", val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -399,7 +406,6 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
|
|||||||
MakeDepends: make([]string, 0),
|
MakeDepends: make([]string, 0),
|
||||||
OptionalDepends: make([]string, 0),
|
OptionalDepends: make([]string, 0),
|
||||||
Conflicts: make([]string, 0),
|
Conflicts: make([]string, 0),
|
||||||
Replaces: make([]string, 0),
|
|
||||||
Provides: make([]string, 0),
|
Provides: make([]string, 0),
|
||||||
}
|
}
|
||||||
err := yaml.Unmarshal([]byte(contents), &pkgInfo)
|
err := yaml.Unmarshal([]byte(contents), &pkgInfo)
|
||||||
@ -425,6 +431,14 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
|
|||||||
return &pkgInfo, nil
|
return &pkgInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateInfoFile(pkgInfo *PackageInfo) string {
|
||||||
|
b, err := yaml.Marshal(&pkgInfo)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, pkgInfo *PackageInfo, rootDir string) string {
|
func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, pkgInfo *PackageInfo, rootDir string) string {
|
||||||
ret := make([]string, 0)
|
ret := make([]string, 0)
|
||||||
appendArray := func(label string, array []string) {
|
appendArray := func(label string, array []string) {
|
||||||
@ -446,17 +460,11 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p
|
|||||||
}
|
}
|
||||||
if showPackageRelations {
|
if showPackageRelations {
|
||||||
appendArray("Dependencies", pkgInfo.Depends)
|
appendArray("Dependencies", pkgInfo.Depends)
|
||||||
if pkgInfo.Type == "source" {
|
|
||||||
appendArray("Make Dependencies", pkgInfo.MakeDepends)
|
appendArray("Make Dependencies", pkgInfo.MakeDepends)
|
||||||
}
|
|
||||||
appendArray("Optional dependencies", pkgInfo.OptionalDepends)
|
appendArray("Optional dependencies", pkgInfo.OptionalDepends)
|
||||||
dependants, err := pkgInfo.GetDependants(rootDir)
|
|
||||||
if err == nil {
|
|
||||||
appendArray("Dependant packages", dependants)
|
|
||||||
}
|
|
||||||
appendArray("Conflicting packages", pkgInfo.Conflicts)
|
appendArray("Conflicting packages", pkgInfo.Conflicts)
|
||||||
appendArray("Provided packages", pkgInfo.Provides)
|
appendArray("Provided packages", pkgInfo.Provides)
|
||||||
appendArray("Replaces packages", pkgInfo.Replaces)
|
|
||||||
}
|
}
|
||||||
ret = append(ret, "Installation Reason: "+string(GetInstallationReason(pkgInfo.Name, rootDir)))
|
ret = append(ret, "Installation Reason: "+string(GetInstallationReason(pkgInfo.Name, rootDir)))
|
||||||
return strings.Join(ret, "\n")
|
return strings.Join(ret, "\n")
|
||||||
@ -464,12 +472,12 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations bool, p
|
|||||||
|
|
||||||
func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) error {
|
func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string) error {
|
||||||
if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) {
|
if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) {
|
||||||
err := executePackageScripts(filename, rootDir, packageOperationInstall, false)
|
err := ExecutePackageScripts(filename, rootDir, Install, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err := executePackageScripts(filename, rootDir, packageOperationUpdate, false)
|
err := ExecutePackageScripts(filename, rootDir, Update, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -480,7 +488,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tarballFile, err := readTarballContent(filename, "files.tar.gz")
|
tarballFile, err := ReadTarballContent(filename, "files.tar.gz")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -597,7 +605,447 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func installPackage(filename, rootDir string, verbose, force bool) error {
|
func isSplitPackage(filename string) bool {
|
||||||
|
pkgInfo, err := ReadPackage(filename)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if pkgInfo.PkgInfo.Type != "source" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("test $(tar -tf %s | grep '^pkg.info' | wc -l) -eq 1", filename))
|
||||||
|
if err := cmd.Run(); err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func compilePackage(bpmpkg *BPMPackage, filename, rootDir string, verbose, binaryPkgFromSrc, skipCheck, keepTempDir bool) (error, []string) {
|
||||||
|
var files []string
|
||||||
|
if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) {
|
||||||
|
err := ExecutePackageScripts(filename, rootDir, Install, false)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := ExecutePackageScripts(filename, rootDir, Update, false)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//seenHardlinks := make(map[string]string)
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tr := tar.NewReader(file)
|
||||||
|
|
||||||
|
temp := path.Join(BPMConfig.CompilationDir, "bpm_source-"+bpmpkg.PkgInfo.Name)
|
||||||
|
err = os.RemoveAll(temp)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
if verbose {
|
||||||
|
fmt.Println("Creating temp directory at: " + temp)
|
||||||
|
}
|
||||||
|
err = os.Mkdir(temp, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
err = os.Chown(temp, 65534, 65534)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
header, err := tr.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
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, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if verbose {
|
||||||
|
fmt.Println("Creating Directory: " + extractFilename)
|
||||||
|
}
|
||||||
|
err = os.Chown(extractFilename, 65534, 65534)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case tar.TypeReg:
|
||||||
|
err := os.Remove(extractFilename)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
outFile, err := os.Create(extractFilename)
|
||||||
|
if verbose {
|
||||||
|
fmt.Println("Creating File: " + extractFilename)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
err = os.Chown(extractFilename, 65534, 65534)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(outFile, tr); err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
if err := os.Chmod(extractFilename, header.FileInfo().Mode()); err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
err = outFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
case tar.TypeSymlink:
|
||||||
|
if verbose {
|
||||||
|
fmt.Println("Skipping symlink (Bundling symlinks in source packages is not supported)")
|
||||||
|
}
|
||||||
|
case tar.TypeLink:
|
||||||
|
if verbose {
|
||||||
|
fmt.Println("Skipping hard link (Bundling hard links in source packages is not supported)")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.New("unknown type (" + strconv.Itoa(int(header.Typeflag)) + ") in " + extractFilename), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if header.Name == "source.sh" {
|
||||||
|
bs, err := io.ReadAll(tr)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
err = os.WriteFile(path.Join(temp, "source.sh"), bs, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
err = os.Chown(path.Join(temp, "source.sh"), 65534, 65534)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(path.Join(temp, "source.sh")); os.IsNotExist(err) {
|
||||||
|
return errors.New("source.sh file could not be found in the temporary build directory"), nil
|
||||||
|
}
|
||||||
|
fmt.Println("Running source.sh file...")
|
||||||
|
if !IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) {
|
||||||
|
err = ExecutePackageScripts(filename, rootDir, Install, false)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = ExecutePackageScripts(filename, rootDir, Update, false)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bs, err := os.ReadFile(path.Join(temp, "source.sh"))
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(string(bs), "package()") {
|
||||||
|
fmt.Print("This package does not seem to have the required 'package' function\nThe source.sh file may have been created for an older BPM version\nPlease update the source.sh file")
|
||||||
|
return errors.New("invalid source.sh format"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
runScript := `
|
||||||
|
cd "$BPM_WORKDIR"
|
||||||
|
|
||||||
|
set -a
|
||||||
|
source "source.sh"
|
||||||
|
set +a
|
||||||
|
|
||||||
|
if [[ $(type -t prepare) == function ]]; then
|
||||||
|
echo "Running prepare() function..."
|
||||||
|
bash -e -c prepare
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Failed to run prepare() function in source.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$BPM_SOURCE"
|
||||||
|
if [[ $(type -t build) == function ]]; then
|
||||||
|
echo "Running build() function..."
|
||||||
|
bash -e -c build
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Failed to run build() function in source.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$BPM_SOURCE"
|
||||||
|
if [[ $(type -t check) == function ]] && [ -z "$SKIPCHECK" ]; then
|
||||||
|
echo "Running check() function..."
|
||||||
|
bash -e -c check
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Failed to run check() function in source.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
cd "$BPM_SOURCE"
|
||||||
|
if ! [[ $(type -t package) == function ]]; then
|
||||||
|
echo "Failed to locate package() function in source.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Running package() function..."
|
||||||
|
touch "$BPM_WORKDIR"/fakeroot_file
|
||||||
|
fakeroot -s "$BPM_WORKDIR"/fakeroot_file bash -e -c package
|
||||||
|
bash -e -c package
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Failed to run package() function in source.sh"
|
||||||
|
fi
|
||||||
|
`
|
||||||
|
err = os.WriteFile(path.Join(temp, "run.sh"), []byte(runScript), 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
err = os.Chown(path.Join(temp, "run.sh"), 65534, 65534)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("/bin/bash", "-e", "run.sh")
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
|
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: 65534, Gid: 65534}
|
||||||
|
cmd.Dir = temp
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
cmd.Env = append(cmd.Env, "USER=nobody")
|
||||||
|
cmd.Env = append(cmd.Env, "HOME="+temp)
|
||||||
|
|
||||||
|
err = os.Mkdir(path.Join(temp, "source"), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
err = os.Chown(path.Join(temp, "source"), 65534, 65534)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
err = os.Mkdir(path.Join(temp, "output"), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
err = os.Chown(path.Join(temp, "output"), 65534, 65534)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
cmd.Env = append(cmd.Env, "BPM_WORKDIR="+temp)
|
||||||
|
cmd.Env = append(cmd.Env, "BPM_SOURCE="+path.Join(temp, "source"))
|
||||||
|
cmd.Env = append(cmd.Env, "BPM_OUTPUT="+path.Join(temp, "output"))
|
||||||
|
cmd.Env = append(cmd.Env, "SKIPCHECK="+strconv.FormatBool(skipCheck))
|
||||||
|
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_NAME=%s", bpmpkg.PkgInfo.Name))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_DESC=%s", bpmpkg.PkgInfo.Description))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_VERSION=%s", bpmpkg.PkgInfo.Version))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_REVISION=%d", bpmpkg.PkgInfo.Revision))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_URL=%s", bpmpkg.PkgInfo.Url))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_PKG_ARCH=%s", bpmpkg.PkgInfo.Arch))
|
||||||
|
depends := make([]string, len(bpmpkg.PkgInfo.Depends))
|
||||||
|
copy(depends, bpmpkg.PkgInfo.Depends)
|
||||||
|
for i := 0; i < len(depends); i++ {
|
||||||
|
depends[i] = fmt.Sprintf("\"%s\"", depends[i])
|
||||||
|
}
|
||||||
|
makeDepends := make([]string, len(bpmpkg.PkgInfo.MakeDepends))
|
||||||
|
copy(makeDepends, bpmpkg.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, " ")))
|
||||||
|
for _, value := range BPMConfig.CompilationEnv {
|
||||||
|
cmd.Env = append(cmd.Env, value)
|
||||||
|
}
|
||||||
|
cmd.Env = append(cmd.Env, "BPM_PKG_TYPE=source")
|
||||||
|
|
||||||
|
if !BPMConfig.SilentCompilation {
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
}
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
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/")), nil
|
||||||
|
}
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
if dir, _ := os.ReadDir(path.Join(temp, "output/")); len(dir) == 0 {
|
||||||
|
return errors.New("output directory is empty"), nil
|
||||||
|
}
|
||||||
|
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(rootDir, 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 {
|
||||||
|
if verbose {
|
||||||
|
fmt.Println("Creating Directory: " + extractFilename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if d.Type().IsRegular() {
|
||||||
|
if _, err := os.Stat(extractFilename); err == nil {
|
||||||
|
if slices.Contains(bpmpkg.PkgInfo.Keep, relFilename) {
|
||||||
|
if verbose {
|
||||||
|
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)
|
||||||
|
if verbose {
|
||||||
|
fmt.Println("Creating File: " + extractFilename)
|
||||||
|
}
|
||||||
|
files = append(files, relFilename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := os.Open(fullpath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
err = os.Remove(extractFilename)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if verbose {
|
||||||
|
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 {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
if binaryPkgFromSrc {
|
||||||
|
compiledDir := path.Join(BPMConfig.BinaryOutputDir)
|
||||||
|
err = os.MkdirAll(compiledDir, 0755)
|
||||||
|
compiledInfo := PackageInfo{}
|
||||||
|
compiledInfo = *bpmpkg.PkgInfo
|
||||||
|
compiledInfo.Type = "binary"
|
||||||
|
compiledInfo.Arch = GetArch()
|
||||||
|
err = os.WriteFile(path.Join(temp, "pkg.info"), []byte(CreateInfoFile(&compiledInfo)), 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
err = os.Chown(path.Join(temp, "pkg.info"), 65534, 65534)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
scripts, err := ReadPackageScripts(filename)
|
||||||
|
for key, val := range scripts {
|
||||||
|
err = os.WriteFile(path.Join(temp, key), []byte(val), 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
err = os.Chown(path.Join(temp, key), 65534, 65534)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sed := fmt.Sprintf("s/output/files/")
|
||||||
|
fileName := compiledInfo.Name + "-" + compiledInfo.GetFullVersion() + "-" + compiledInfo.Arch + ".bpm"
|
||||||
|
cmd := exec.Command("/usr/bin/fakeroot", "-i fakeroot_file", "tar", "-czvpf", fileName, "pkg.info", "output/", "--transform", sed)
|
||||||
|
if !BPMConfig.SilentCompilation {
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
}
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
|
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: 65534, Gid: 65534}
|
||||||
|
cmd.Dir = temp
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
fmt.Printf("running command: %s\n", strings.Join(cmd.Args, " "))
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
err = copyFileContents(path.Join(temp, fileName), path.Join(compiledDir, fileName))
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
err = os.Chown(path.Join(compiledDir, fileName), 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !keepTempDir {
|
||||||
|
err := os.RemoveAll(temp)
|
||||||
|
if err != nil {
|
||||||
|
return err, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(files) == 0 {
|
||||||
|
return errors.New("no output files for source package. Cancelling package installation"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
return nil, files
|
||||||
|
}
|
||||||
|
|
||||||
|
func InstallPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc, skipCheck, keepTempDir bool) error {
|
||||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -716,7 +1164,13 @@ func installPackage(filename, rootDir string, verbose, force bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if bpmpkg.PkgInfo.Type == "source" {
|
} else if bpmpkg.PkgInfo.Type == "source" {
|
||||||
return errors.New("direct source package compilation in BPM has been temporarily removed and is being reworked on")
|
if isSplitPackage(filename) {
|
||||||
|
return errors.New("BPM is unable to install split source packages")
|
||||||
|
}
|
||||||
|
err, _ := compilePackage(bpmpkg, filename, rootDir, verbose, binaryPkgFromSrc, skipCheck, keepTempDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return errors.New("unknown package type: " + bpmpkg.PkgInfo.Type)
|
return errors.New("unknown package type: " + bpmpkg.PkgInfo.Type)
|
||||||
}
|
}
|
||||||
@ -727,8 +1181,11 @@ func installPackage(filename, rootDir string, verbose, force bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pkgDir := path.Join(installedDir, bpmpkg.PkgInfo.Name)
|
pkgDir := path.Join(installedDir, bpmpkg.PkgInfo.Name)
|
||||||
|
err = os.RemoveAll(pkgDir)
|
||||||
err = os.MkdirAll(pkgDir, 0755)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.Mkdir(pkgDir, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -738,7 +1195,7 @@ func installPackage(filename, rootDir string, verbose, force bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tarballFile, err := readTarballContent(filename, "pkg.files")
|
tarballFile, err := ReadTarballContent(filename, "pkg.files")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -786,12 +1243,12 @@ func installPackage(filename, rootDir string, verbose, force bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !packageInstalled {
|
if !packageInstalled {
|
||||||
err = executePackageScripts(filename, rootDir, packageOperationInstall, true)
|
err = ExecutePackageScripts(filename, rootDir, Install, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = executePackageScripts(filename, rootDir, packageOperationUpdate, true)
|
err = ExecutePackageScripts(filename, rootDir, Update, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -836,39 +1293,6 @@ func (pkgInfo *PackageInfo) CheckDependencies(checkMake, checkOptional bool, roo
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pkgInfo *PackageInfo) GetDependants(rootDir string) ([]string, error) {
|
|
||||||
ret := make([]string, 0)
|
|
||||||
|
|
||||||
pkgs, err := GetInstalledPackages(rootDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("could not get installed packages")
|
|
||||||
}
|
|
||||||
for _, pkg := range pkgs {
|
|
||||||
bpmpkg := GetPackage(pkg, rootDir)
|
|
||||||
if bpmpkg == nil {
|
|
||||||
return nil, errors.New("package not found: " + pkg)
|
|
||||||
}
|
|
||||||
if bpmpkg.PkgInfo.Name == pkgInfo.Name {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies := bpmpkg.PkgInfo.GetAllDependencies(false, true)
|
|
||||||
|
|
||||||
if slices.Contains(dependencies, pkgInfo.Name) {
|
|
||||||
ret = append(ret, pkg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, vpkg := range pkgInfo.Provides {
|
|
||||||
if slices.Contains(dependencies, vpkg) {
|
|
||||||
ret = append(ret, pkg)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pkgInfo *PackageInfo) CheckConflicts(rootDir string) []string {
|
func (pkgInfo *PackageInfo) CheckConflicts(rootDir string) []string {
|
||||||
var ret []string
|
var ret []string
|
||||||
for _, conflict := range pkgInfo.Conflicts {
|
for _, conflict := range pkgInfo.Conflicts {
|
||||||
@ -893,20 +1317,16 @@ func (pkgInfo *PackageInfo) ResolveDependencies(resolved, unresolved *[]string,
|
|||||||
*resolved = append(*resolved, depend)
|
*resolved = append(*resolved, depend)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
} else if ignoreInstalled && IsPackageProvided(depend, rootDir) {
|
} else if ignoreInstalled && IsPackageInstalled(depend, rootDir) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var err error
|
entry, _, err := GetRepositoryEntry(depend)
|
||||||
var entry *RepositoryEntry
|
|
||||||
entry, _, err = GetRepositoryEntry(depend)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if entry = ResolveVirtualPackage(depend); entry == nil {
|
|
||||||
if !slices.Contains(*unresolved, depend) {
|
if !slices.Contains(*unresolved, depend) {
|
||||||
*unresolved = append(*unresolved, depend)
|
*unresolved = append(*unresolved, depend)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
|
||||||
entry.Info.ResolveDependencies(resolved, unresolved, checkMake, checkOptional, ignoreInstalled, verbose, rootDir)
|
entry.Info.ResolveDependencies(resolved, unresolved, checkMake, checkOptional, ignoreInstalled, verbose, rootDir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -926,26 +1346,6 @@ func IsPackageInstalled(pkg, rootDir string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsVirtualPackage(pkg, rootDir string) (bool, string) {
|
|
||||||
pkgs, err := GetInstalledPackages(rootDir)
|
|
||||||
if err != nil {
|
|
||||||
return false, ""
|
|
||||||
}
|
|
||||||
for _, p := range pkgs {
|
|
||||||
if p == pkg {
|
|
||||||
return false, ""
|
|
||||||
}
|
|
||||||
i := GetPackageInfo(p, rootDir)
|
|
||||||
if i == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if slices.Contains(i.Provides, pkg) {
|
|
||||||
return true, p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsPackageProvided(pkg, rootDir string) bool {
|
func IsPackageProvided(pkg, rootDir string) bool {
|
||||||
pkgs, err := GetInstalledPackages(rootDir)
|
pkgs, err := GetInstalledPackages(rootDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1105,7 +1505,7 @@ func GetAllPackageFiles(rootDir string, excludePackages ...string) (map[string][
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func removePackage(pkg string, verbose bool, rootDir string) error {
|
func RemovePackage(pkg string, verbose bool, 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)
|
pkgInfo := GetPackageInfo(pkg, rootDir)
|
||||||
@ -1189,6 +1589,11 @@ func removePackage(pkg string, verbose bool, rootDir string) error {
|
|||||||
// Executing post_remove script
|
// Executing post_remove script
|
||||||
if _, err := os.Stat(path.Join(pkgDir, "post_remove.sh")); err == nil {
|
if _, err := os.Stat(path.Join(pkgDir, "post_remove.sh")); err == nil {
|
||||||
cmd := exec.Command("/bin/bash", path.Join(pkgDir, "post_remove.sh"))
|
cmd := exec.Command("/bin/bash", path.Join(pkgDir, "post_remove.sh"))
|
||||||
|
if !BPMConfig.SilentCompilation {
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
}
|
||||||
cmd.Dir = rootDir
|
cmd.Dir = rootDir
|
||||||
cmd.Env = os.Environ()
|
cmd.Env = os.Environ()
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("BPM_ROOT=%s", rootDir))
|
@ -1,14 +1,14 @@
|
|||||||
package bpmlib
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,7 +17,6 @@ type Repository struct {
|
|||||||
Source string `yaml:"source"`
|
Source string `yaml:"source"`
|
||||||
Disabled *bool `yaml:"disabled"`
|
Disabled *bool `yaml:"disabled"`
|
||||||
Entries map[string]*RepositoryEntry
|
Entries map[string]*RepositoryEntry
|
||||||
VirtualPackages map[string][]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RepositoryEntry struct {
|
type RepositoryEntry struct {
|
||||||
@ -25,6 +24,7 @@ type RepositoryEntry struct {
|
|||||||
Download string `yaml:"download"`
|
Download string `yaml:"download"`
|
||||||
DownloadSize uint64 `yaml:"download_size"`
|
DownloadSize uint64 `yaml:"download_size"`
|
||||||
InstalledSize uint64 `yaml:"installed_size"`
|
InstalledSize uint64 `yaml:"installed_size"`
|
||||||
|
IsVirtualPackage bool `yaml:"-"`
|
||||||
Repository *Repository
|
Repository *Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +44,8 @@ func (repo *Repository) ReadLocalDatabase() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtualPackages := make(map[string][]string)
|
||||||
|
|
||||||
data := string(bytes)
|
data := string(bytes)
|
||||||
for _, b := range strings.Split(data, "---") {
|
for _, b := range strings.Split(data, "---") {
|
||||||
entry := RepositoryEntry{
|
entry := RepositoryEntry{
|
||||||
@ -66,6 +68,7 @@ func (repo *Repository) ReadLocalDatabase() error {
|
|||||||
Download: "",
|
Download: "",
|
||||||
DownloadSize: 0,
|
DownloadSize: 0,
|
||||||
InstalledSize: 0,
|
InstalledSize: 0,
|
||||||
|
IsVirtualPackage: false,
|
||||||
Repository: repo,
|
Repository: repo,
|
||||||
}
|
}
|
||||||
err := yaml.Unmarshal([]byte(b), &entry)
|
err := yaml.Unmarshal([]byte(b), &entry)
|
||||||
@ -74,69 +77,54 @@ func (repo *Repository) ReadLocalDatabase() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range entry.Info.Provides {
|
for _, p := range entry.Info.Provides {
|
||||||
repo.VirtualPackages[p] = append(repo.VirtualPackages[p], entry.Info.Name)
|
virtualPackages[p] = append(virtualPackages[p], entry.Info.Name)
|
||||||
}
|
}
|
||||||
repo.Entries[entry.Info.Name] = &entry
|
repo.Entries[entry.Info.Name] = &entry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for key, value := range virtualPackages {
|
||||||
|
if _, ok := repo.Entries[key]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sort.Strings(value)
|
||||||
|
entry := RepositoryEntry{
|
||||||
|
Info: repo.Entries[value[0]].Info,
|
||||||
|
Download: repo.Entries[value[0]].Download,
|
||||||
|
DownloadSize: repo.Entries[value[0]].DownloadSize,
|
||||||
|
InstalledSize: repo.Entries[value[0]].InstalledSize,
|
||||||
|
IsVirtualPackage: true,
|
||||||
|
Repository: repo,
|
||||||
|
}
|
||||||
|
repo.Entries[key] = &entry
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *Repository) SyncLocalDatabase() error {
|
func (repo *Repository) SyncLocalDatabase() error {
|
||||||
repoFile := "/var/lib/bpm/repositories/" + repo.Name + ".bpmdb"
|
repoFile := "/var/lib/bpm/repositories/" + repo.Name + ".bpmdb"
|
||||||
|
err := os.MkdirAll(path.Dir(repoFile), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Get URL to database
|
|
||||||
u, err := url.JoinPath(repo.Source, "database.bpmdb")
|
u, err := url.JoinPath(repo.Source, "database.bpmdb")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve data from URL
|
|
||||||
resp, err := http.Get(u)
|
resp, err := http.Get(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
// Load data into byte buffer
|
|
||||||
buffer, err := io.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
// Unmarshal data to ensure it is a valid BPM repository
|
|
||||||
err = yaml.Unmarshal(buffer, &Repository{})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not decode repository: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create parent directories to repository file
|
|
||||||
err = os.MkdirAll(path.Dir(repoFile), 0755)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create file and save repository data
|
|
||||||
out, err := os.Create(repoFile)
|
out, err := os.Create(repoFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer out.Close()
|
defer out.Close()
|
||||||
|
|
||||||
_, err = out.Write(buffer)
|
_, err = io.Copy(out, resp.Body)
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadLocalDatabases() (err error) {
|
|
||||||
for _, repo := range BPMConfig.Repositories {
|
|
||||||
// Initialize struct values
|
|
||||||
repo.Entries = make(map[string]*RepositoryEntry)
|
|
||||||
repo.VirtualPackages = make(map[string][]string)
|
|
||||||
|
|
||||||
// Read database
|
|
||||||
err = repo.ReadLocalDatabase()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -179,32 +167,6 @@ func GetRepositoryEntry(str string) (*RepositoryEntry, *Repository, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindReplacement(pkg string) *RepositoryEntry {
|
|
||||||
for _, repo := range BPMConfig.Repositories {
|
|
||||||
for _, entry := range repo.Entries {
|
|
||||||
for _, replaced := range entry.Info.Replaces {
|
|
||||||
if replaced == pkg {
|
|
||||||
return entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ResolveVirtualPackage(vpkg string) *RepositoryEntry {
|
|
||||||
for _, repo := range BPMConfig.Repositories {
|
|
||||||
if v, ok := repo.VirtualPackages[vpkg]; ok {
|
|
||||||
for _, pkg := range v {
|
|
||||||
return repo.Entries[pkg]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *Repository) FetchPackage(pkg string) (string, error) {
|
func (repo *Repository) FetchPackage(pkg string) (string, error) {
|
||||||
if !repo.ContainsPackage(pkg) {
|
if !repo.ContainsPackage(pkg) {
|
||||||
return "", errors.New("could not fetch package '" + pkg + "'")
|
return "", errors.New("could not fetch package '" + pkg + "'")
|
Loading…
x
Reference in New Issue
Block a user