16 Commits
14 changed files with 593 additions and 157 deletions
+8 -4
View File
@@ -1,12 +1,10 @@
module git.enumerated.dev/bubble-package-manager/bpm/src/bpm module git.enumerated.dev/bubble-package-manager/bpm/src/bpm
go 1.23 go 1.24.0
toolchain go1.23.7
require ( require (
git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib v0.5.0 git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib v0.5.0
github.com/adrg/strutil v0.3.1 github.com/lithammer/fuzzysearch v1.1.8
github.com/spf13/pflag v1.0.10 github.com/spf13/pflag v1.0.10
) )
@@ -15,5 +13,11 @@ replace git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib => ../bpmlib
require ( require (
github.com/drone/envsubst v1.0.3 // indirect github.com/drone/envsubst v1.0.3 // indirect
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f // indirect github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/schollz/progressbar/v3 v3.18.0 // indirect
golang.org/x/sys v0.37.0 // indirect
golang.org/x/term v0.36.0 // indirect
golang.org/x/text v0.9.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )
+50 -11
View File
@@ -1,6 +1,5 @@
github.com/adrg/strutil v0.3.1 h1:OLvSS7CSJO8lBii4YmBt8jiK9QOtB9CzCzwl4Ic/Fz4= github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
github.com/adrg/strutil v0.3.1/go.mod h1:8h90y18QLrs11IBffcGX3NW/GFBXCMcNg4M7H6MspPA= github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g=
@@ -9,19 +8,59 @@ github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f h1:xt29M2T6STgldg+WEP51gGePQCsQvklmP2eIhPIBK3g= github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f 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=
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/schollz/progressbar/v3 v3.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA=
github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 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=
+144 -44
View File
@@ -16,8 +16,7 @@ import (
"git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib" "git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib"
"github.com/adrg/strutil" "github.com/lithammer/fuzzysearch/fuzzy"
"github.com/adrg/strutil/metrics"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
) )
@@ -26,7 +25,7 @@ import (
/* A simple-to-use package manager */ /* A simple-to-use package manager */
/* ------------------------------------------------------- */ /* ------------------------------------------------------- */
var bpmVer = "0.6.0" var bpmVer = "0.6.1"
var currentFlagSet *flag.FlagSet var currentFlagSet *flag.FlagSet
@@ -45,7 +44,7 @@ func main() {
} }
switch subcommand { switch subcommand {
case "version": case "v", "version":
fmt.Println("Bubble Package Manager (BPM)") fmt.Println("Bubble Package Manager (BPM)")
fmt.Println("Version: " + bpmVer) fmt.Println("Version: " + bpmVer)
case "q", "query": case "q", "query":
@@ -53,6 +52,7 @@ func main() {
currentFlagSet = flag.NewFlagSet("query", flag.ExitOnError) currentFlagSet = flag.NewFlagSet("query", flag.ExitOnError)
currentFlagSet.StringP("root", "R", "/", "Operate on specified root directory") currentFlagSet.StringP("root", "R", "/", "Operate on specified root directory")
currentFlagSet.BoolP("database", "d", false, "Show package information from remote databases") currentFlagSet.BoolP("database", "d", false, "Show package information from remote databases")
currentFlagSet.BoolP("human-readable", "h", false, "Show package installed size in a human readable format")
setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Show information on the specified packages", os.Args[2:]) setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Show information on the specified packages", os.Args[2:])
showPackageInfo() showPackageInfo()
@@ -62,6 +62,13 @@ func main() {
currentFlagSet.StringP("root", "R", "/", "Operate on specified root directory") currentFlagSet.StringP("root", "R", "/", "Operate on specified root directory")
currentFlagSet.BoolP("count", "c", false, "Show total package count") currentFlagSet.BoolP("count", "c", false, "Show total package count")
currentFlagSet.BoolP("names", "n", false, "Show all package names") currentFlagSet.BoolP("names", "n", false, "Show all package names")
currentFlagSet.BoolP("database", "d", false, "Show packages from remote databases")
currentFlagSet.Bool("manual", false, "Show packages installed as dependencies")
currentFlagSet.Bool("depends", false, "Show packages installed as dependencies")
currentFlagSet.Bool("make-depends", false, "Show packages installed as make dependencies")
currentFlagSet.String("sort", "", "Sort listed packages by 'name' or 'size")
currentFlagSet.Bool("reverse", false, "Reverse the order in which packages are listed")
currentFlagSet.BoolP("human-readable", "h", false, "Show package installed size in a human readable format")
setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "List packages", os.Args[2:]) setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "List packages", os.Args[2:])
showPackageList() showPackageList()
@@ -149,6 +156,7 @@ func main() {
currentFlagSet.BoolP("yes", "y", false, "Enter 'yes' in all prompts") currentFlagSet.BoolP("yes", "y", false, "Enter 'yes' in all prompts")
currentFlagSet.BoolP("depends", "d", false, "Install required dependencies for package compilation") currentFlagSet.BoolP("depends", "d", false, "Install required dependencies for package compilation")
currentFlagSet.BoolP("skip-checks", "s", false, "Skip the check function in source.sh scripts") currentFlagSet.BoolP("skip-checks", "s", false, "Skip the check function in source.sh scripts")
currentFlagSet.BoolP("keep", "k", false, "Keep compilation files after successful package compilation")
currentFlagSet.BoolP("output-directory", "o", false, "Set the output directory for the binary packages") currentFlagSet.BoolP("output-directory", "o", false, "Set the output directory for the binary packages")
currentFlagSet.Int("output-fd", -1, "Set the file descriptor output package names will be written to") currentFlagSet.Int("output-fd", -1, "Set the file descriptor output package names will be written to")
setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Compile source packages and convert them to binary ones", os.Args[2:]) setupFlagsAndHelp(currentFlagSet, fmt.Sprintf("bpm %s <options>", subcommand), "Compile source packages and convert them to binary ones", os.Args[2:])
@@ -167,6 +175,7 @@ func showPackageInfo() {
// Get flags // Get flags
rootDir, _ := currentFlagSet.GetString("root") rootDir, _ := currentFlagSet.GetString("root")
showDatabaseInfo, _ := currentFlagSet.GetBool("database") showDatabaseInfo, _ := currentFlagSet.GetBool("database")
showHumanReadableSize, _ := currentFlagSet.GetBool("human-readable")
// Get packages // Get packages
packages := currentFlagSet.Args() packages := currentFlagSet.Args()
@@ -184,9 +193,6 @@ func showPackageInfo() {
} }
for n, pkg := range packages { for n, pkg := range packages {
var info *bpmlib.PackageInfo
isFile := false
showInstallationReason := false
if showDatabaseInfo { if showDatabaseInfo {
var err error var err error
var entry *bpmlib.BPMDatabaseEntry var entry *bpmlib.BPMDatabaseEntry
@@ -198,25 +204,33 @@ func showPackageInfo() {
return return
} }
} }
info = entry.Info
} else if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() { if n != 0 {
bpmpkg, err := bpmlib.ReadPackage(pkg) fmt.Println()
}
fmt.Println(entry.CreateReadableInfo(rootDir, showHumanReadableSize))
return
}
var bpmpkg *bpmlib.BPMPackage
isFile := false
if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() {
bpmpkg, err = bpmlib.ReadPackage(pkg)
if err != nil { if err != nil {
log.Printf("Error: could not read package: %s\n", err) log.Printf("Error: could not read package: %s\n", err)
exitCode = 1 exitCode = 1
return return
} }
info = bpmpkg.PkgInfo
isFile = true isFile = true
} else { } else {
if isVirtual, p := bpmlib.IsVirtualPackage(pkg, rootDir); isVirtual { if isVirtual, p := bpmlib.IsVirtualPackage(pkg, rootDir); isVirtual {
info = bpmlib.GetPackageInfo(p, rootDir) bpmpkg = bpmlib.GetPackage(p, rootDir)
} else { } else {
info = bpmlib.GetPackageInfo(pkg, rootDir) bpmpkg = bpmlib.GetPackage(pkg, rootDir)
} }
showInstallationReason = true
} }
if info == nil { if bpmpkg == nil {
log.Printf("Error: package (%s) is not installed\n", pkg) log.Printf("Error: package (%s) is not installed\n", pkg)
exitCode = 1 exitCode = 1
return return
@@ -233,7 +247,7 @@ func showPackageInfo() {
} }
fmt.Println("File: " + abs) fmt.Println("File: " + abs)
} }
fmt.Println(bpmlib.CreateReadableInfo(true, true, true, showInstallationReason, info, rootDir)) fmt.Println(bpmpkg.CreateReadableInfo(rootDir, showHumanReadableSize))
} }
} }
@@ -242,6 +256,19 @@ func showPackageList() {
rootDir, _ := currentFlagSet.GetString("root") rootDir, _ := currentFlagSet.GetString("root")
showPkgCount, _ := currentFlagSet.GetBool("count") showPkgCount, _ := currentFlagSet.GetBool("count")
showPkgNames, _ := currentFlagSet.GetBool("names") showPkgNames, _ := currentFlagSet.GetBool("names")
showDatabase, _ := currentFlagSet.GetBool("database")
showManual, _ := currentFlagSet.GetBool("manual")
showDepends, _ := currentFlagSet.GetBool("depends")
showMakeDepends, _ := currentFlagSet.GetBool("make-depends")
sortPackages, _ := currentFlagSet.GetString("sort")
reversePackages, _ := currentFlagSet.GetBool("reverse")
showHumanReadableSize, _ := currentFlagSet.GetBool("human-readable")
if !isFlagSet(currentFlagSet, "manual") && !isFlagSet(currentFlagSet, "depends") && !isFlagSet(currentFlagSet, "make-depends") {
showManual = true
showDepends = true
showMakeDepends = true
}
// Read local databases // Read local databases
err := bpmlib.ReadLocalDatabaseFiles() err := bpmlib.ReadLocalDatabaseFiles()
@@ -251,33 +278,109 @@ func showPackageList() {
return return
} }
packages, err := bpmlib.GetInstalledPackages(rootDir) installedPackageNames, err := bpmlib.GetInstalledPackages(rootDir)
if err != nil { if err != nil {
log.Printf("Error: could not get installed packages: %s", err.Error()) log.Printf("Error: could not get installed packages: %s", err.Error())
exitCode = 1 exitCode = 1
return return
} }
if showPkgCount {
fmt.Println(len(packages)) installedPackages := make([]*bpmlib.BPMPackage, len(installedPackageNames))
} else if showPkgNames { for i, pkgName := range installedPackageNames {
for _, pkg := range packages { installedPackages[i] = bpmlib.GetPackage(pkgName, rootDir)
fmt.Println(pkg)
} }
} else {
if len(packages) == 0 { databaseEntries := make([]*bpmlib.BPMDatabaseEntry, 0)
fmt.Println("No packages have been installed") for _, db := range bpmlib.BPMDatabases {
databaseEntries = append(databaseEntries, slices.Collect(maps.Values(db.Entries))...)
}
switch sortPackages {
case "", "name":
case "size":
slices.SortFunc(installedPackages, func(a, b *bpmlib.BPMPackage) int {
return int(b.GetInstalledSize() - a.GetInstalledSize())
})
slices.SortFunc(databaseEntries, func(a, b *bpmlib.BPMDatabaseEntry) int {
return int(b.InstalledSize - a.InstalledSize)
})
default:
log.Printf("Error: cannot sort by '%s'", sortPackages)
exitCode = 1
return return
} }
for n, pkg := range packages {
info := bpmlib.GetPackageInfo(pkg, rootDir) if reversePackages {
if info == nil { slices.Reverse(installedPackages)
fmt.Printf("Package (%s) could not be found\n", pkg) slices.Reverse(databaseEntries)
}
if showPkgCount {
if showDatabase {
fmt.Println(len(databaseEntries))
} else {
fmt.Println(len(installedPackages))
}
} else if showPkgNames {
if showDatabase {
for _, entry := range databaseEntries {
fmt.Println(entry.Database.Name + "/" + entry.Info.Name)
}
} else {
for _, pkg := range installedPackages {
installationReason := bpmlib.GetInstallationReason(pkg.PkgInfo.Name, rootDir)
if installationReason == bpmlib.InstallationReasonManual && !showManual {
continue
} else if installationReason == bpmlib.InstallationReasonDependency && !showDepends {
continue
} else if installationReason == bpmlib.InstallationReasonMakeDependency && !showMakeDepends {
continue
} else if installationReason == bpmlib.InstallationReasonUnknown && (!showManual || !showDepends || !showMakeDepends) {
continue continue
} }
fmt.Println(pkg.PkgInfo.Name)
}
}
} else {
if showDatabase {
if len(databaseEntries) == 0 {
fmt.Println("There are no database entries available")
return
}
for n, entry := range databaseEntries {
if n != 0 { if n != 0 {
fmt.Println() fmt.Println()
} }
fmt.Println(bpmlib.CreateReadableInfo(true, true, true, true, info, rootDir)) fmt.Println(entry.CreateReadableInfo(rootDir, showHumanReadableSize))
}
} else {
if len(installedPackages) == 0 {
fmt.Println("No packages have been installed")
return
}
for n, bpmpkg := range installedPackages {
if bpmpkg == nil {
fmt.Printf("Package (%s) could not be found\n", installedPackageNames[n])
continue
}
installationReason := bpmlib.GetInstallationReason(bpmpkg.PkgInfo.Name, rootDir)
if installationReason == bpmlib.InstallationReasonManual && !showManual {
continue
} else if installationReason == bpmlib.InstallationReasonDependency && !showDepends {
continue
} else if installationReason == bpmlib.InstallationReasonMakeDependency && !showMakeDepends {
continue
} else if installationReason == bpmlib.InstallationReasonUnknown && (!showManual || !showDepends || !showMakeDepends) {
continue
}
if n != 0 {
fmt.Println()
}
fmt.Println(bpmpkg.CreateReadableInfo(rootDir, showHumanReadableSize))
}
} }
} }
} }
@@ -300,23 +403,19 @@ func searchForPackages() {
} }
for i, term := range searchTerms { for i, term := range searchTerms {
resultsMap := make(map[*bpmlib.PackageInfo]float64, 0) // Find matches
resultsMap := make(map[*bpmlib.BPMDatabaseEntry]int)
// Loop through all packages
for _, db := range bpmlib.BPMDatabases { for _, db := range bpmlib.BPMDatabases {
for _, entry := range db.Entries { for _, entry := range db.Entries {
// Calculate string similarity and add to map match := fuzzy.RankMatchNormalizedFold(term, entry.Info.Name)
similarity := strutil.Similarity(entry.Info.Name, term, metrics.NewSmithWatermanGotoh()) if match == -1 {
if similarity > 0.8 {
resultsMap[entry.Info] = similarity
continue continue
} }
similarity = strutil.Similarity(entry.Info.Description, term, metrics.NewSmithWatermanGotoh())
if similarity > 0.8 { resultsMap[entry] = match
resultsMap[entry.Info] = similarity
}
} }
} }
if len(resultsMap) == 0 { if len(resultsMap) == 0 {
log.Printf("Error: no results for term (%s) were found\n", term) log.Printf("Error: no results for term (%s) were found\n", term)
exitCode = 1 exitCode = 1
@@ -326,7 +425,7 @@ func searchForPackages() {
// Sort results // Sort results
results := slices.Collect(maps.Keys(resultsMap)) results := slices.Collect(maps.Keys(resultsMap))
sort.Slice(results, func(i, j int) bool { sort.Slice(results, func(i, j int) bool {
return resultsMap[results[i]] > resultsMap[results[j]] return resultsMap[results[i]] < resultsMap[results[j]]
}) })
// Print results // Print results
@@ -336,7 +435,7 @@ func searchForPackages() {
fmt.Printf("Results for term (%s)\n", term) fmt.Printf("Results for term (%s)\n", term)
for j := 0; j < 10 && j < len(results); j++ { for j := 0; j < 10 && j < len(results); j++ {
result := results[j] result := results[j]
fmt.Printf("%d) %s: %s (%s)\n", j+1, result.Name, result.Description, result.GetFullVersion()) fmt.Printf("%d) %s/%s: %s (%s)\n", j+1, result.Database.Name, result.Info.Name, result.Info.Description, result.Info.GetFullVersion())
} }
} }
} }
@@ -942,6 +1041,7 @@ func compilePackage() {
rootDir, _ := currentFlagSet.GetString("root") rootDir, _ := currentFlagSet.GetString("root")
verbose, _ := currentFlagSet.GetBool("verbose") verbose, _ := currentFlagSet.GetBool("verbose")
yesAll, _ := currentFlagSet.GetBool("yes") yesAll, _ := currentFlagSet.GetBool("yes")
keepCompilationFiles, _ := currentFlagSet.GetBool("keep")
installSrcPkgDepends, _ := currentFlagSet.GetBool("depends") installSrcPkgDepends, _ := currentFlagSet.GetBool("depends")
skipChecks, _ := currentFlagSet.GetBool("skip-checks") skipChecks, _ := currentFlagSet.GetBool("skip-checks")
outputDirectory, _ := currentFlagSet.GetString("output-directory") outputDirectory, _ := currentFlagSet.GetString("output-directory")
@@ -1139,7 +1239,7 @@ func compilePackage() {
return return
} }
outputBpmPackages, err := bpmlib.CompileSourcePackage(sourcePackage, outputDirectory, skipChecks) outputBpmPackages, err := bpmlib.CompileSourcePackage(sourcePackage, outputDirectory, skipChecks, keepCompilationFiles, verbose)
if err != nil { if err != nil {
// Remove unused packages // Remove unused packages
cleanupFunc() cleanupFunc()
+91 -11
View File
@@ -6,9 +6,11 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/fs"
"os" "os"
"os/exec" "os/exec"
"path" "path"
"path/filepath"
"slices" "slices"
"strconv" "strconv"
"strings" "strings"
@@ -21,7 +23,7 @@ import (
var rootCompilationUID = "65534" var rootCompilationUID = "65534"
var rootCompilationGID = "65534" var rootCompilationGID = "65534"
func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bool) (outputBpmPackages map[string]string, err error) { func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks, keepCompilationFiles, verbose bool) (outputBpmPackages map[string]string, err error) {
// Initialize map // Initialize map
outputBpmPackages = make(map[string]string) outputBpmPackages = make(map[string]string)
@@ -122,7 +124,7 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
} }
// Download files // Download files
err = downloadPackageFiles(bpmpkg.PkgInfo, tempDirectory) err = downloadPackageFiles(bpmpkg.PkgInfo, tempDirectory, verbose)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -143,8 +145,8 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
"set -a\n"+ // Source and export functions and variables in source.sh script "set -a\n"+ // Source and export functions and variables in source.sh script
". \"${BPM_WORKDIR}\"/source.sh\n"+ ". \"${BPM_WORKDIR}\"/source.sh\n"+
"set +a\n"+ "set +a\n"+
"[[ $(type -t prepare) == \"function\" ]] && { echo \"Running prepare() function\"; bash -e -c 'cd \"$BPM_WORKDIR\" && prepare' || exit 1; }\n"+ // Run prepare() function if it exists "[[ $(type -t prepare) == \"function\" ]] && { echo \"Running prepare() function...\"; bash -e -c 'cd \"$BPM_WORKDIR\" && prepare' || exit 1; }\n"+ // Run prepare() function if it exists
"[[ $(type -t build) == \"function\" ]] && { echo \"Running build() function\"; bash -e -c 'cd \"$BPM_SOURCE\" && build' || exit 1; }\n"+ // Run build() function if it exists "[[ $(type -t build) == \"function\" ]] && { echo \"Running build() function...\"; bash -e -c 'cd \"$BPM_SOURCE\" && build' || exit 1; }\n"+ // Run build() function if it exists
"exit 0") "exit 0")
cmd.Dir = tempDirectory cmd.Dir = tempDirectory
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
@@ -165,7 +167,7 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
"set -a\n"+ // Source and export functions and variables in source.sh script "set -a\n"+ // Source and export functions and variables in source.sh script
". \"${BPM_WORKDIR}\"/source.sh\n"+ ". \"${BPM_WORKDIR}\"/source.sh\n"+
"set +a\n"+ "set +a\n"+
"[[ $(type -t check) == \"function\" ]] && { echo \"Running check() function\"; bash -e -c 'cd \"$BPM_SOURCE\" && check' || exit 1; }\n"+ // Run check() function if it exists "[[ $(type -t check) == \"function\" ]] && { echo \"Running check() function...\"; bash -e -c 'cd \"$BPM_SOURCE\" && check' || exit 1; }\n"+ // Run check() function if it exists
"exit 0") "exit 0")
cmd.Dir = tempDirectory cmd.Dir = tempDirectory
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
@@ -215,9 +217,8 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
"set -a\n"+ // Source and export functions and variables in source.sh script "set -a\n"+ // Source and export functions and variables in source.sh script
". \"${BPM_WORKDIR}\"/source.sh\n"+ ". \"${BPM_WORKDIR}\"/source.sh\n"+
"set +a\n"+ "set +a\n"+
"echo \"Running "+packageFunctionName+"() function\"\n"+ "echo \"Running "+packageFunctionName+"() function...\"\n"+
"( cd \"$BPM_SOURCE\" && fakeroot -s \"$BPM_WORKDIR\"/fakeroot_file bash -e -c '"+packageFunctionName+"' ) || exit 1\n"+ // Run package() function "( cd \"$BPM_SOURCE\" && fakeroot -s \"$BPM_WORKDIR\"/fakeroot_file bash -e -c '"+packageFunctionName+"' ) || exit 1\n") // Run package() function
"fakeroot -i \"$BPM_WORKDIR\"/fakeroot_file find \"$BPM_OUTPUT\" -mindepth 1 -printf \"%P %#m %U %G %s\\n\" > \"$BPM_WORKDIR\"/pkg.files") // Create package file list
cmd.Dir = tempDirectory cmd.Dir = tempDirectory
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
@@ -232,7 +233,77 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
return nil, err return nil, err
} }
// Strip all ELF binaries
if !slices.Contains(pkg.Options, "nostrip") {
fmt.Println("Stripping ELF binaries...")
err = filepath.WalkDir(path.Join(tempDirectory, "output_"+pkg.Name), func(path string, d fs.DirEntry, err error) error {
cmd = exec.Command("file", "-bi", path)
cmd.Stderr = os.Stderr
output, err := cmd.Output()
if err != nil {
return err
}
mimetype := strings.Split(string(output), ";")[0]
if strings.HasPrefix(mimetype, "application/x-executable") {
cmd := exec.Command("strip", path)
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return err
}
if verbose {
fmt.Printf("Stripped %s (%s)\n", path, mimetype)
}
} else if strings.HasPrefix(mimetype, "application/x-pie-executable") || strings.HasPrefix(mimetype, "application/x-sharedlib") {
cmd := exec.Command("strip", "--strip-unneeded", path)
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return err
}
if verbose {
fmt.Printf("Stripped %s (%s)\n", path, mimetype)
}
} else if strings.HasPrefix(mimetype, "application/x-archive") {
cmd := exec.Command("strip", "--strip-debug", path)
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return err
}
if verbose {
fmt.Printf("Stripped %s (%s)\n", path, mimetype)
}
}
return nil
})
if err != nil {
return nil, err
}
}
// Generate package file list
fmt.Println("Generating package file list...")
cmd = exec.Command("bash", "-c", "fakeroot -i \"$BPM_WORKDIR\"/fakeroot_file find \"$BPM_OUTPUT\" -mindepth 1 -printf \"%P %#m %U %G %s\\n\" > \"$BPM_WORKDIR\"/pkg.files")
cmd.Dir = tempDirectory
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = append(env, "BPM_OUTPUT="+path.Join(tempDirectory, "output_"+pkg.Name))
if os.Getuid() == 0 {
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
}
err = cmd.Run()
if err != nil {
return nil, err
}
// Create gzip-compressed archive for the package files // Create gzip-compressed archive for the package files
fmt.Println("Generating compressed file archive...")
cmd = exec.Command("bash", "-c", fmt.Sprintf("find %s -printf \"%%P\\n\" | fakeroot -i %s/fakeroot_file tar -czf files.tar.gz --no-recursion -C %s -T -", "output_"+pkg.Name, tempDirectory, "output_"+pkg.Name)) cmd = exec.Command("bash", "-c", fmt.Sprintf("find %s -printf \"%%P\\n\" | fakeroot -i %s/fakeroot_file tar -czf files.tar.gz --no-recursion -C %s -T -", "output_"+pkg.Name, tempDirectory, "output_"+pkg.Name))
cmd.Dir = tempDirectory cmd.Dir = tempDirectory
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
@@ -286,6 +357,7 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
bpmArchiveFiles = append(bpmArchiveFiles, packageScripts...) // Package scripts bpmArchiveFiles = append(bpmArchiveFiles, packageScripts...) // Package scripts
// Create final BPM archive // Create final BPM archive
fmt.Println("Generating final BPM archive...")
cmd = exec.Command("bash", "-c", "tar -cf final-archive.bpm --owner=0 --group=0 -C \"$BPM_WORKDIR\" "+strings.Join(bpmArchiveFiles, " ")) cmd = exec.Command("bash", "-c", "tar -cf final-archive.bpm --owner=0 --group=0 -C \"$BPM_WORKDIR\" "+strings.Join(bpmArchiveFiles, " "))
cmd.Dir = tempDirectory cmd.Dir = tempDirectory
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
@@ -334,10 +406,15 @@ func CompileSourcePackage(archiveFilename, outputDirectory string, skipChecks bo
outputBpmPackages[pkgInfo.Name] = outputFilename outputBpmPackages[pkgInfo.Name] = outputFilename
} }
// Delete temporary directory
if !keepCompilationFiles {
os.RemoveAll(tempDirectory)
}
return outputBpmPackages, nil return outputBpmPackages, nil
} }
func downloadPackageFiles(pkgInfo *PackageInfo, tempDirectory string) error { func downloadPackageFiles(pkgInfo *PackageInfo, tempDirectory string, verbose bool) error {
for _, download := range pkgInfo.Downloads { for _, download := range pkgInfo.Downloads {
// Replace variables // Replace variables
replaceVars := func(s string) string { replaceVars := func(s string) string {
@@ -382,7 +459,7 @@ func downloadPackageFiles(pkgInfo *PackageInfo, tempDirectory string) error {
filepath = path.Join(tempDirectory, download.Filepath) filepath = path.Join(tempDirectory, download.Filepath)
} }
err := downloadFile(downloadUrl, filepath, 0644) err := downloadFile("Downloading file "+path.Base(filepath), downloadUrl, filepath, 0644)
if err != nil { if err != nil {
return err return err
} }
@@ -409,7 +486,10 @@ func downloadPackageFiles(pkgInfo *PackageInfo, tempDirectory string) error {
} }
if !download.NoExtract && (strings.Contains(filepath, ".tar") || strings.HasSuffix(filepath, ".tgz")) { if !download.NoExtract && (strings.Contains(filepath, ".tar") || strings.HasSuffix(filepath, ".tgz")) {
cmd := exec.Command("tar", "xvf", filepath, "--strip-components="+strconv.Itoa(download.ExtractStripComponents)) cmd := exec.Command("tar", "xf", filepath, "--strip-components="+strconv.Itoa(download.ExtractStripComponents))
if verbose {
cmd.Args[1] = "xvf"
}
cmd.Dir = tempDirectory cmd.Dir = tempDirectory
if extractTo != "" { if extractTo != "" {
err := os.MkdirAll(extractTo, 0755) err := os.MkdirAll(extractTo, 0755)
+137 -4
View File
@@ -1,12 +1,18 @@
package bpmlib package bpmlib
import ( import (
"bufio"
"bytes"
"errors" "errors"
"fmt" "fmt"
"io"
"maps"
"net/http"
"net/url" "net/url"
"os" "os"
"path" "path"
"slices" "slices"
"strconv"
"strings" "strings"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@@ -16,6 +22,7 @@ type BPMDatabase struct {
DatabaseVersion int `yaml:"database_version"` DatabaseVersion int `yaml:"database_version"`
Entries map[string]*BPMDatabaseEntry `yaml:"entries"` Entries map[string]*BPMDatabaseEntry `yaml:"entries"`
VirtualPackages map[string][]string VirtualPackages map[string][]string
Name string
Source string Source string
} }
@@ -54,6 +61,7 @@ func (db *configDatabase) ReadLocalDatabase() error {
// Initialize struct values // Initialize struct values
database.VirtualPackages = make(map[string][]string) database.VirtualPackages = make(map[string][]string)
database.Name = db.Name
database.Source = db.Source database.Source = db.Source
entriesToRemove := make([]string, 0) entriesToRemove := make([]string, 0)
@@ -131,13 +139,30 @@ func (db *configDatabase) SyncLocalDatabaseFile() error {
} }
// Retrieve data from URL // Retrieve data from URL
buffer, err := retrieveUrlData(u) req, err := http.NewRequest("GET", u, nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// Create progress bar
bar := createProgressBar(resp.ContentLength, "Syncing "+db.Name, false)
// Copy data
var buffer bytes.Buffer
bufferWriter := bufio.NewWriter(&buffer)
_, err = io.Copy(io.MultiWriter(bufferWriter, bar), resp.Body)
if err != nil { if err != nil {
return err return err
} }
// Unmarshal data to ensure it is a valid BPM database // Unmarshal data to ensure it is a valid BPM database
err = yaml.Unmarshal(buffer, &BPMDatabase{}) err = yaml.Unmarshal(buffer.Bytes(), &BPMDatabase{})
if err != nil { if err != nil {
return fmt.Errorf("could not decode database: %s", err) return fmt.Errorf("could not decode database: %s", err)
} }
@@ -155,7 +180,7 @@ func (db *configDatabase) SyncLocalDatabaseFile() error {
} }
defer out.Close() defer out.Close()
_, err = out.Write(buffer) _, err = out.Write(buffer.Bytes())
return nil return nil
} }
@@ -240,10 +265,118 @@ func (db *BPMDatabase) FetchPackage(pkg string) (string, error) {
} }
// Download package from url // Download package from url
err = downloadFile(u, path.Join("/var/cache/bpm/fetched/", path.Base(entry.Filepath)), 0644) err = downloadFile("Downloading "+entry.Info.Name, u, path.Join("/var/cache/bpm/fetched/", path.Base(entry.Filepath)), 0644)
if err != nil { if err != nil {
return "", err return "", err
} }
return path.Join("/var/cache/bpm/fetched/", path.Base(entry.Filepath)), nil return path.Join("/var/cache/bpm/fetched/", path.Base(entry.Filepath)), nil
} }
func (entry *BPMDatabaseEntry) GetEntryDependants() (dependants []string) {
dependantsMap := make(map[string][]string)
for _, db := range BPMDatabases {
for _, e := range db.Entries {
if slices.Contains(e.Info.Depends, entry.Info.Name) {
dependantsMap[e.Info.Name] = append(dependantsMap[e.Info.Name], e.Database.Name)
}
}
}
// Get keys
keySlice := slices.Collect(maps.Keys(dependantsMap))
slices.Sort(keySlice)
// Add all dependant entries to slice in alphabetical order
for _, entryName := range keySlice {
dbs := dependantsMap[entryName]
if len(dbs) > 1 {
for _, db := range dbs {
dependants = append(dependants, db+"/"+entryName)
}
} else {
dependants = append(dependants, entryName)
}
}
return dependants
}
func (entry *BPMDatabaseEntry) CreateReadableInfo(rootDir string, humanReadableSize bool) string {
ret := make([]string, 0)
appendArray := func(label string, array []string, sort bool) {
if len(array) == 0 {
return
}
if sort {
// Sort array
slices.Sort(array)
}
ret = append(ret, fmt.Sprintf("%s: %s", label, strings.Join(array, ", ")))
}
ret = append(ret, "Name: "+entry.Info.Name)
ret = append(ret, "Database: "+entry.Database.Name)
ret = append(ret, "Description: "+entry.Info.Description)
ret = append(ret, "Version: "+entry.Info.GetFullVersion())
if entry.Info.Url != "" {
ret = append(ret, "URL: "+entry.Info.Url)
}
if entry.Info.License != "" {
ret = append(ret, "License: "+entry.Info.License)
}
ret = append(ret, "Architecture: "+entry.Info.Arch)
if entry.Info.Type == "source" && entry.Info.OutputArch != "" && entry.Info.OutputArch != GetArch() {
ret = append(ret, "Output architecture: "+entry.Info.OutputArch)
}
ret = append(ret, "Type: "+entry.Info.Type)
appendArray("Dependencies", entry.Info.Depends, true)
if entry.Info.Type == "source" {
appendArray("Make Dependencies", entry.Info.MakeDepends, true)
}
appendArray("Optional dependencies", entry.Info.OptionalDepends, true)
dependants := entry.GetEntryDependants()
if len(dependants) > 0 {
appendArray("Dependant packages", dependants, false)
}
appendArray("Conflicting packages", entry.Info.Conflicts, true)
appendArray("Provided packages", entry.Info.Provides, true)
appendArray("Replaces packages", entry.Info.Replaces, true)
if entry.Info.Type == "source" && len(entry.Info.SplitPackages) != 0 {
splitPkgs := make([]string, len(entry.Info.SplitPackages))
for i, splitPkgInfo := range entry.Info.SplitPackages {
splitPkgs[i] = splitPkgInfo.Name
}
appendArray("Split Packages", splitPkgs, true)
}
if rootDir != "" && IsPackageInstalled(entry.Info.Name, rootDir) {
installationReason := GetInstallationReason(entry.Info.Name, rootDir)
var installationReasonString string
switch installationReason {
case InstallationReasonManual:
installationReasonString = "Manual"
case InstallationReasonDependency:
installationReasonString = "Dependency"
case InstallationReasonMakeDependency:
installationReasonString = "Make dependency"
default:
installationReasonString = "Unknown"
}
ret = append(ret, "Installation Reason: "+installationReasonString)
}
if entry.Info.Type == "binary" {
installedSize := int64(entry.InstalledSize)
var installedSizeStr string
if humanReadableSize {
installedSizeStr = bytesToHumanReadable(installedSize)
} else {
installedSizeStr = strconv.FormatInt(installedSize, 10)
}
ret = append(ret, "Installed size: "+installedSizeStr)
}
return strings.Join(ret, "\n")
}
+9 -18
View File
@@ -1,7 +1,6 @@
package bpmlib package bpmlib
import ( import (
"errors"
"fmt" "fmt"
"slices" "slices"
) )
@@ -170,19 +169,11 @@ func resolvePackageDependenciesFromDatabase(resolved *[]pkgInstallationReason, u
} }
} }
func GetPackageDependants(pkgName string, rootDir string) ([]string, error) { func (pkgInfo *PackageInfo) GetPackageDependants(rootDir string) (dependants []string) {
ret := make([]string, 0)
// Get BPM package
pkg := GetPackage(pkgName, rootDir)
if pkg == nil {
return nil, errors.New("package not found: " + pkgName)
}
// Get installed package names // Get installed package names
pkgs, err := GetInstalledPackages(rootDir) pkgs, err := GetInstalledPackages(rootDir)
if err != nil { if err != nil {
return nil, errors.New("could not get installed packages") return nil
} }
// Loop through all installed packages // Loop through all installed packages
@@ -190,33 +181,33 @@ func GetPackageDependants(pkgName string, rootDir string) ([]string, error) {
// Get installed BPM package // Get installed BPM package
installedPkg := GetPackage(installedPkgName, rootDir) installedPkg := GetPackage(installedPkgName, rootDir)
if installedPkg == nil { if installedPkg == nil {
return nil, errors.New("package not found: " + installedPkgName) return nil
} }
// Skip iteration if comparing the same packages // Skip iteration if comparing the same packages
if installedPkg.PkgInfo.Name == pkgName { if installedPkg.PkgInfo.Name == pkgInfo.Name {
continue continue
} }
// Add installed package to list if its dependencies include pkgName // Add installed package to list if its dependencies include pkgName
if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool { if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool {
return n == pkgName return n == pkgInfo.Name
}) { }) {
ret = append(ret, installedPkgName) dependants = append(dependants, installedPkgName)
continue continue
} }
// Loop through each virtual package // Loop through each virtual package
for _, vpkg := range pkg.PkgInfo.Provides { for _, vpkg := range pkgInfo.Provides {
// Add installed package to list if its dependencies contain a provided virtual package // Add installed package to list if its dependencies contain a provided virtual package
if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool { if slices.ContainsFunc(installedPkg.PkgInfo.Depends, func(n string) bool {
return n == vpkg return n == vpkg
}) { }) {
ret = append(ret, installedPkgName) dependants = append(dependants, installedPkgName)
break break
} }
} }
} }
return ret, nil return dependants
} }
+1 -5
View File
@@ -221,11 +221,7 @@ func RemovePackages(rootDir string, force, cleanupDependencies bool, packages ..
// Get packages and their dependants // Get packages and their dependants
packageDepndants := make(map[string][]string, 0) packageDepndants := make(map[string][]string, 0)
for _, action := range operation.Actions { for _, action := range operation.Actions {
dependants, err := GetPackageDependants(action.(*RemovePackageAction).BpmPackage.PkgInfo.Name, rootDir) dependants := action.(*RemovePackageAction).BpmPackage.PkgInfo.GetPackageDependants(rootDir)
if err != nil {
return nil, fmt.Errorf("could not get package dependants: %s", err)
}
packageDepndants[action.(*RemovePackageAction).BpmPackage.PkgInfo.Name] = dependants packageDepndants[action.(*RemovePackageAction).BpmPackage.PkgInfo.Name] = dependants
} }
+9 -1
View File
@@ -1,9 +1,17 @@
module git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib module git.enumerated.dev/bubble-package-manager/bpm/src/bpmlib
go 1.23 go 1.24.0
require ( require (
github.com/drone/envsubst v1.0.3 github.com/drone/envsubst v1.0.3
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f
github.com/schollz/progressbar/v3 v3.18.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require (
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/rivo/uniseg v0.4.7 // indirect
golang.org/x/sys v0.37.0 // indirect
golang.org/x/term v0.36.0 // indirect
)
+20
View File
@@ -1,9 +1,29 @@
github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g= github.com/drone/envsubst v1.0.3 h1:PCIBwNDYjs50AsLZPYdfhSATKaRg/FJmDc2D6+C2x8g=
github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= github.com/drone/envsubst v1.0.3/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f h1:xt29M2T6STgldg+WEP51gGePQCsQvklmP2eIhPIBK3g= github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f 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=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/schollz/progressbar/v3 v3.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA=
github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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=
+3
View File
@@ -75,6 +75,9 @@ func GetInstalledPackages(rootDir string) (ret []string, err error) {
ret = append(ret, bpmpkg.PkgInfo.Name) ret = append(ret, bpmpkg.PkgInfo.Name)
} }
// Sort packages
slices.Sort(ret)
return ret, nil return ret, nil
} }
+13 -16
View File
@@ -9,29 +9,22 @@ import (
"strings" "strings"
) )
func retrieveUrlData(u string) ([]byte, error) { func downloadFile(barText, u, filepath string, perm os.FileMode) error {
resp, err := http.Get(u)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// Load data into byte buffer
buffer, err := io.ReadAll(resp.Body)
return buffer, nil
}
func downloadFile(u, filepath string, perm os.FileMode) error {
if strings.HasSuffix(filepath, "/") { if strings.HasSuffix(filepath, "/") {
return fmt.Errorf("Filepath must not end in '/'") return fmt.Errorf("Filepath must not end in '/'")
} }
data, err := retrieveUrlData(u) req, err := http.NewRequest("GET", u, nil)
if err != nil { if err != nil {
return err return err
} }
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
// Create parent directories // Create parent directories
err = os.MkdirAll(path.Dir(filepath), 0755) err = os.MkdirAll(path.Dir(filepath), 0755)
if err != nil { if err != nil {
@@ -45,8 +38,12 @@ func downloadFile(u, filepath string, perm os.FileMode) error {
} }
defer file.Close() defer file.Close()
// Create progress bar
bar := createProgressBar(resp.ContentLength, barText, false)
defer bar.Close()
// Copy data // Copy data
_, err = file.Write(data) _, err = io.Copy(io.MultiWriter(file, bar), resp.Body)
if err != nil { if err != nil {
return err return err
} }
+13 -12
View File
@@ -176,10 +176,7 @@ func (operation *BPMOperation) RemoveNeededPackages() error {
} }
for pkg, action := range removeActions { for pkg, action := range removeActions {
dependants, err := GetPackageDependants(action.BpmPackage.PkgInfo.Name, operation.RootDir) dependants := action.BpmPackage.PkgInfo.GetPackageDependants(operation.RootDir)
if err != nil {
return errors.New("could not get dependant packages for package (" + pkg + ")")
}
dependants = slices.DeleteFunc(dependants, func(d string) bool { dependants = slices.DeleteFunc(dependants, func(d string) bool {
if _, ok := removeActions[d]; ok { if _, ok := removeActions[d]; ok {
return true return true
@@ -481,7 +478,7 @@ func (operation *BPMOperation) FetchPackages() (err error) {
var bpmpkg *BPMPackage var bpmpkg *BPMPackage
// Check if package has already been fetched from download link // Check if package has already been fetched from download link
if _, ok := fetchedPackages[entry.Filepath]; !ok { if fetchedFilepath, ok := fetchedPackages[entry.Filepath]; !ok {
// Fetch package from database // Fetch package from database
fetchedPackage, err := entry.Database.FetchPackage(entry.Info.Name) fetchedPackage, err := entry.Database.FetchPackage(entry.Info.Name)
if err != nil { if err != nil {
@@ -496,16 +493,22 @@ func (operation *BPMOperation) FetchPackages() (err error) {
// Add fetched package to map // Add fetched package to map
fetchedPackages[entry.Filepath] = fetchedPackage fetchedPackages[entry.Filepath] = fetchedPackage
fmt.Printf("Package (%s) was successfully fetched!\n", entry.Info.Name)
} else { } else {
// Read fetched package // Read fetched package
bpmpkg, err = ReadPackage(fetchedPackages[entry.Filepath]) bpmpkg, err = ReadPackage(fetchedFilepath)
if err != nil { if err != nil {
return fmt.Errorf("could not read package (%s): %s\n", entry.Info.Name, err) return fmt.Errorf("could not read package (%s): %s\n", entry.Info.Name, err)
} }
fmt.Printf("Package (%s) was successfully fetched!\n", entry.Info.Name) // Get size of fetched archive
stat, err := os.Stat(fetchedFilepath)
if err != nil {
return err
}
bar := createProgressBar(stat.Size(), "Downloading "+entry.Info.Name, false)
bar.Set64(stat.Size())
bar.Close()
} }
if bpmpkg.PkgInfo.IsSplitPackage() { if bpmpkg.PkgInfo.IsSplitPackage() {
@@ -592,7 +595,7 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
// Compile source package if not compiled already // Compile source package if not compiled already
if _, ok := operation.compiledPackages[pkgNameToInstall]; !ok { if _, ok := operation.compiledPackages[pkgNameToInstall]; !ok {
outputBpmPackages, err := CompileSourcePackage(value.File, compiledDir, false) outputBpmPackages, err := CompileSourcePackage(value.File, compiledDir, false, false, verbose)
if err != nil { if err != nil {
return fmt.Errorf("could not compile source package (%s): %s\n", value.File, err) return fmt.Errorf("could not compile source package (%s): %s\n", value.File, err)
} }
@@ -625,8 +628,6 @@ func (operation *BPMOperation) Execute(verbose, force bool) (err error) {
if err != nil { if err != nil {
return fmt.Errorf("could not set installation reason for package (%s): %s\n", value.BpmPackage.PkgInfo.Name, err) return fmt.Errorf("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!") fmt.Println("Operation complete!")
+63 -27
View File
@@ -43,6 +43,7 @@ type PackageInfo struct {
Conflicts []string `yaml:"conflicts,omitempty"` Conflicts []string `yaml:"conflicts,omitempty"`
Replaces []string `yaml:"replaces,omitempty"` Replaces []string `yaml:"replaces,omitempty"`
Provides []string `yaml:"provides,omitempty"` Provides []string `yaml:"provides,omitempty"`
Options []string `yaml:"options,omitempty"`
Downloads []PackageDownload `yaml:"downloads,omitempty"` Downloads []PackageDownload `yaml:"downloads,omitempty"`
SplitPackages []*PackageInfo `yaml:"split_packages,omitempty"` SplitPackages []*PackageInfo `yaml:"split_packages,omitempty"`
} }
@@ -434,6 +435,7 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
Conflicts: make([]string, 0), Conflicts: make([]string, 0),
Replaces: make([]string, 0), Replaces: make([]string, 0),
Provides: make([]string, 0), Provides: make([]string, 0),
Options: make([]string, 0),
Downloads: make([]PackageDownload, 0), Downloads: make([]PackageDownload, 0),
SplitPackages: make([]*PackageInfo, 0), SplitPackages: make([]*PackageInfo, 0),
} }
@@ -507,48 +509,56 @@ func ReadPackageInfo(contents string) (*PackageInfo, error) {
return pkgInfo, nil return pkgInfo, nil
} }
func CreateReadableInfo(showArchitecture, showType, showPackageRelations, showInstallationReason bool, pkgInfo *PackageInfo, rootDir string) string { func (bpmpkg *BPMPackage) CreateReadableInfo(rootDir string, humanReadableSize bool) string {
ret := make([]string, 0) ret := make([]string, 0)
appendArray := func(label string, array []string) { appendArray := func(label string, array []string) {
if len(array) == 0 { if len(array) == 0 {
return return
} }
// Sort array
slices.Sort(array)
ret = append(ret, fmt.Sprintf("%s: %s", label, strings.Join(array, ", "))) ret = append(ret, fmt.Sprintf("%s: %s", label, strings.Join(array, ", ")))
} }
ret = append(ret, "Name: "+pkgInfo.Name)
ret = append(ret, "Description: "+pkgInfo.Description) ret = append(ret, "Name: "+bpmpkg.PkgInfo.Name)
ret = append(ret, "Version: "+pkgInfo.GetFullVersion()) ret = append(ret, "Description: "+bpmpkg.PkgInfo.Description)
ret = append(ret, "URL: "+pkgInfo.Url) ret = append(ret, "Version: "+bpmpkg.PkgInfo.GetFullVersion())
ret = append(ret, "License: "+pkgInfo.License) if bpmpkg.PkgInfo.Url != "" {
if showArchitecture { ret = append(ret, "URL: "+bpmpkg.PkgInfo.Url)
ret = append(ret, "Architecture: "+pkgInfo.Arch)
} }
if showType { if bpmpkg.PkgInfo.License != "" {
ret = append(ret, "Type: "+pkgInfo.Type) ret = append(ret, "License: "+bpmpkg.PkgInfo.License)
} }
if showPackageRelations { ret = append(ret, "Architecture: "+bpmpkg.PkgInfo.Arch)
appendArray("Dependencies", pkgInfo.Depends) if bpmpkg.PkgInfo.Type == "source" && bpmpkg.PkgInfo.OutputArch != "" && bpmpkg.PkgInfo.OutputArch != GetArch() {
if pkgInfo.Type == "source" { ret = append(ret, "Output architecture: "+bpmpkg.PkgInfo.Arch)
appendArray("Make Dependencies", pkgInfo.MakeDepends)
} }
appendArray("Optional dependencies", pkgInfo.OptionalDepends) ret = append(ret, "Type: "+bpmpkg.PkgInfo.Type)
dependants, err := GetPackageDependants(pkgInfo.Name, rootDir) appendArray("Dependencies", bpmpkg.PkgInfo.Depends)
if err == nil { if bpmpkg.PkgInfo.Type == "source" {
appendArray("Make Dependencies", bpmpkg.PkgInfo.MakeDepends)
}
appendArray("Optional dependencies", bpmpkg.PkgInfo.OptionalDepends)
dependants := bpmpkg.PkgInfo.GetPackageDependants(rootDir)
if len(dependants) > 0 {
appendArray("Dependant packages", dependants) appendArray("Dependant packages", dependants)
} }
appendArray("Conflicting packages", pkgInfo.Conflicts) appendArray("Conflicting packages", bpmpkg.PkgInfo.Conflicts)
appendArray("Provided packages", pkgInfo.Provides) appendArray("Provided packages", bpmpkg.PkgInfo.Provides)
appendArray("Replaces packages", pkgInfo.Replaces) appendArray("Replaces packages", bpmpkg.PkgInfo.Replaces)
}
if pkgInfo.Type == "source" && len(pkgInfo.SplitPackages) != 0 { if bpmpkg.PkgInfo.Type == "source" && len(bpmpkg.PkgInfo.SplitPackages) != 0 {
splitPkgs := make([]string, len(pkgInfo.SplitPackages)) splitPkgs := make([]string, len(bpmpkg.PkgInfo.SplitPackages))
for i, splitPkgInfo := range pkgInfo.SplitPackages { for i, splitPkgInfo := range bpmpkg.PkgInfo.SplitPackages {
splitPkgs[i] = splitPkgInfo.Name splitPkgs[i] = splitPkgInfo.Name
} }
appendArray("Split Packages", splitPkgs) appendArray("Split Packages", splitPkgs)
} }
if IsPackageInstalled(pkgInfo.Name, rootDir) && showInstallationReason {
installationReason := GetInstallationReason(pkgInfo.Name, rootDir) if rootDir != "" && IsPackageInstalled(bpmpkg.PkgInfo.Name, rootDir) {
installationReason := GetInstallationReason(bpmpkg.PkgInfo.Name, rootDir)
var installationReasonString string var installationReasonString string
switch installationReason { switch installationReason {
case InstallationReasonManual: case InstallationReasonManual:
@@ -562,6 +572,16 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations, showIn
} }
ret = append(ret, "Installation Reason: "+installationReasonString) ret = append(ret, "Installation Reason: "+installationReasonString)
} }
if bpmpkg.PkgInfo.Type == "binary" {
installedSize := int64(bpmpkg.GetInstalledSize())
var installedSizeStr string
if humanReadableSize {
installedSizeStr = bytesToHumanReadable(installedSize)
} else {
installedSizeStr = strconv.FormatInt(installedSize, 10)
}
ret = append(ret, "Installed size: "+installedSizeStr)
}
return strings.Join(ret, "\n") return strings.Join(ret, "\n")
} }
@@ -583,6 +603,10 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
return err return err
} }
// Initialize progress bar
bar := createProgressBar(int64(bpmpkg.GetInstalledSize()), "Installing "+bpmpkg.PkgInfo.Name, verbose)
defer bar.Close()
tarballFile, err := readTarballFile(filename, "files.tar.gz") tarballFile, err := readTarballFile(filename, "files.tar.gz")
if err != nil { if err != nil {
return err return err
@@ -594,6 +618,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
return err return err
} }
packageFilesReader := tar.NewReader(archive) packageFilesReader := tar.NewReader(archive)
for { for {
header, err := packageFilesReader.Next() header, err := packageFilesReader.Next()
if err == io.EOF { if err == io.EOF {
@@ -630,6 +655,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
if verbose { if verbose {
fmt.Printf("Created directory %s (%o)\n", extractFilename, header.Mode) fmt.Printf("Created directory %s (%o)\n", extractFilename, header.Mode)
} }
bar.Add64(header.Size)
case tar.TypeReg: case tar.TypeReg:
skip := false skip := false
if _, err := os.Stat(extractFilename); err == nil { if _, err := os.Stat(extractFilename); err == nil {
@@ -686,6 +712,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
if verbose { if verbose {
fmt.Printf("Created File: %s (%o)\n", extractFilename, header.Mode) fmt.Printf("Created File: %s (%o)\n", extractFilename, header.Mode)
} }
bar.Add64(header.Size)
case tar.TypeSymlink: case tar.TypeSymlink:
err := os.Remove(extractFilename) err := os.Remove(extractFilename)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
@@ -700,6 +727,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
if verbose { if verbose {
fmt.Println("Created Symlink: " + extractFilename + " -> " + header.Linkname) fmt.Println("Created Symlink: " + extractFilename + " -> " + header.Linkname)
} }
bar.Add64(header.Size)
case tar.TypeLink: case tar.TypeLink:
if verbose { if verbose {
fmt.Println("Detected Hard Link: " + extractFilename + " -> " + path.Join(rootDir, strings.TrimPrefix(header.Linkname, "files/"))) fmt.Println("Detected Hard Link: " + extractFilename + " -> " + path.Join(rootDir, strings.TrimPrefix(header.Linkname, "files/")))
@@ -709,6 +737,7 @@ func extractPackage(bpmpkg *BPMPackage, verbose bool, filename, rootDir string)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return err return err
} }
bar.Add64(header.Size)
default: default:
return errors.New("unknown type (" + strconv.Itoa(int(header.Typeflag)) + ") in " + extractFilename) return errors.New("unknown type (" + strconv.Itoa(int(header.Typeflag)) + ") in " + extractFilename)
} }
@@ -961,8 +990,11 @@ func removePackage(pkg string, verbose bool, rootDir string) error {
log.Printf("Warning: %s\n", err) log.Printf("Warning: %s\n", err)
} }
// Get BPM package
bpmpkg := GetPackage(pkg, rootDir)
// Fetching and reversing package file entry list // Fetching and reversing package file entry list
fileEntries := GetPackage(pkg, rootDir).PkgFiles fileEntries := bpmpkg.PkgFiles
sort.Slice(fileEntries, func(i, j int) bool { sort.Slice(fileEntries, func(i, j int) bool {
return fileEntries[i].Path < fileEntries[j].Path return fileEntries[i].Path < fileEntries[j].Path
}) })
@@ -972,8 +1004,12 @@ func removePackage(pkg string, verbose bool, rootDir string) error {
return err return err
} }
bar := createProgressBar(int64(bpmpkg.GetInstalledSize()), "Removing "+bpmpkg.PkgInfo.Name, verbose)
defer bar.Close()
// Removing package files // Removing package files
for _, entry := range fileEntries { for _, entry := range fileEntries {
bar.Add64(int64(entry.SizeInBytes))
file := path.Join(rootDir, entry.Path) file := path.Join(rootDir, entry.Path)
lstat, err := os.Lstat(file) lstat, err := os.Lstat(file)
if os.IsNotExist(err) { if os.IsNotExist(err) {
+28
View File
@@ -2,10 +2,14 @@ package bpmlib
import ( import (
"fmt" "fmt"
"io"
"math" "math"
"os" "os"
"path" "path"
"syscall" "syscall"
"time"
"github.com/schollz/progressbar/v3"
) )
type BPMLock struct { type BPMLock struct {
@@ -64,6 +68,30 @@ func GetArch() string {
return string(byteString[:indexLength]) return string(byteString[:indexLength])
} }
func createProgressBar(max int64, description string, hideBar bool) *progressbar.ProgressBar {
var output io.Writer
if hideBar {
output = io.Discard
} else {
output = os.Stderr
}
return progressbar.NewOptions64(max,
progressbar.OptionSetDescription(description),
progressbar.OptionSetWriter(output),
progressbar.OptionShowBytes(true),
progressbar.OptionShowTotalBytes(true),
progressbar.OptionSetWidth(20),
progressbar.OptionThrottle(65*time.Millisecond),
progressbar.OptionShowCount(),
progressbar.OptionOnCompletion(func() {
fmt.Fprint(output, "\n")
}),
progressbar.OptionSpinnerType(14),
progressbar.OptionSetRenderBlankState(true),
progressbar.OptionSetTheme(progressbar.ThemeASCII))
}
func stringSliceRemove(s []string, r string) []string { func stringSliceRemove(s []string, r string) []string {
for i, v := range s { for i, v := range s {
if v == r { if v == r {