Add repository functionality to BPM #4

Merged
EnumDev merged 9 commits from remote-repositories-functionality into master 2024-09-09 08:45:45 +00:00
7 changed files with 728 additions and 210 deletions

View File

@ -1,4 +1,8 @@
compilation_env: [] compilation_env: []
silent_compilation: false silent_compilation: false
compilation_dir: "/var/tmp/" compilation_dir: "/var/tmp/"
binary_output_dir: "/var/lib/bpm/compiled/" binary_output_dir: "/var/lib/bpm/compiled/"
repositories:
- name: example-repository
source: https://my-repo.xyz/
disabled: true

5
go.mod
View File

@ -2,4 +2,7 @@ module gitlab.com/bubble-package-manager/bpm
go 1.22 go 1.22
require gopkg.in/yaml.v3 v3.0.1 // indirect require (
github.com/elliotchance/orderedmap/v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

2
go.sum
View File

@ -1,3 +1,5 @@
github.com/elliotchance/orderedmap/v2 v2.4.0 h1:6tUmMwD9F998FNpwFxA5E6NQvSpk2PVw7RKsVq3+2Cw=
github.com/elliotchance/orderedmap/v2 v2.4.0/go.mod h1:85lZyVbpGaGvHvnKa7Qhx7zncAdBIBq6u56Hb1PRU5Q=
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=

553
main.go
View File

@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"flag" "flag"
"fmt" "fmt"
"github.com/elliotchance/orderedmap/v2"
"gitlab.com/bubble-package-manager/bpm/utils" "gitlab.com/bubble-package-manager/bpm/utils"
"log" "log"
"os" "os"
@ -29,10 +30,13 @@ var yesAll = false
var buildSource = false var buildSource = false
var skipCheck = false var skipCheck = false
var keepTempDir = false var keepTempDir = false
var forceInstall = false var force = false
var showInstalled = false
var pkgListNumbers = false var pkgListNumbers = false
var pkgListNames = false var pkgListNames = false
var reinstall = false
var reinstallAll = false
var noOptional = false
var nosync = true
func main() { func main() {
utils.ReadConfig() utils.ReadConfig()
@ -47,7 +51,10 @@ const (
version version
info info
list list
search
install install
update
sync
remove remove
file file
) )
@ -60,8 +67,14 @@ func getCommandType() commandType {
return info return info
case "list": case "list":
return list return list
case "search":
return search
case "install": case "install":
return install return install
case "update":
return update
case "sync":
return sync
case "remove": case "remove":
return remove return remove
case "file": case "file":
@ -84,26 +97,12 @@ func resolveCommand() {
} }
for n, pkg := range packages { for n, pkg := range packages {
var info *utils.PackageInfo var info *utils.PackageInfo
if _, err := os.Stat(pkg); err == nil && !showInstalled { info = utils.GetPackageInfo(pkg, rootDir, false)
info, err = utils.ReadPackage(pkg) if info == nil {
if err != nil { log.Fatalf("Package (%s) is not installed\n", pkg)
fmt.Printf("File (%s) could not be read\n", pkg)
continue
}
} else {
info = utils.GetPackageInfo(pkg, rootDir, false)
if info == nil {
fmt.Printf("Package (%s) could not be found\n", pkg)
continue
}
} }
fmt.Println("----------------") fmt.Println("----------------")
if showInstalled { fmt.Println(utils.CreateReadableInfo(true, true, true, info, rootDir))
fmt.Println(utils.CreateReadableInfo(false, false, true, false, true, info, rootDir))
} else {
fmt.Println(utils.CreateReadableInfo(true, true, true, true, true, info, rootDir))
}
if n == len(packages)-1 { if n == len(packages)-1 {
fmt.Println("----------------") fmt.Println("----------------")
} }
@ -131,113 +130,417 @@ func resolveCommand() {
fmt.Printf("Package (%s) could not be found\n", pkg) fmt.Printf("Package (%s) could not be found\n", pkg)
continue continue
} }
fmt.Println("----------------\n" + utils.CreateReadableInfo(true, true, true, true, true, info, rootDir)) fmt.Println("----------------\n" + utils.CreateReadableInfo(true, true, true, info, rootDir))
if n == len(packages)-1 { if n == len(packages)-1 {
fmt.Println("----------------") fmt.Println("----------------")
} }
} }
} }
case search:
searchTerms := subcommandArgs
if len(searchTerms) == 0 {
fmt.Println("No search terms given")
os.Exit(0)
}
for _, term := range searchTerms {
nameResults := make([]*utils.PackageInfo, 0)
descResults := make([]*utils.PackageInfo, 0)
for _, repo := range utils.BPMConfig.Repositories {
for _, entry := range repo.Entries {
if strings.Contains(entry.Info.Name, term) {
nameResults = append(nameResults, entry.Info)
} else if strings.Contains(entry.Info.Description, term) {
descResults = append(descResults, entry.Info)
}
}
}
results := append(nameResults, descResults...)
if len(results) == 0 {
log.Fatalf("No results for term (%s) were found\n", term)
}
fmt.Printf("Results for term (%s)\n", term)
for i, result := range results {
fmt.Println("----------------")
fmt.Printf("%d) %s: %s (%s)\n", i+1, result.Name, result.Description, result.Version)
}
}
case install: case install:
if os.Getuid() != 0 { if os.Getuid() != 0 {
fmt.Println("This subcommand needs to be run with superuser permissions") fmt.Println("This subcommand needs to be run with superuser permissions")
os.Exit(0) os.Exit(0)
} }
files := subcommandArgs pkgs := subcommandArgs
if len(files) == 0 { if len(pkgs) == 0 {
fmt.Println("No files were given to install") fmt.Println("No packages or files were given to install")
return return
} }
for _, file := range files {
pkgInfo, err := utils.ReadPackage(file) pkgsToInstall := orderedmap.NewOrderedMap[string, *struct {
if err != nil { isDependency bool
log.Fatalf("Could not read package\nError: %s\n", err) pkgInfo *utils.PackageInfo
} }]()
if !yesAll { pkgsToFetch := orderedmap.NewOrderedMap[string, *struct {
fmt.Println("----------------\n" + utils.CreateReadableInfo(true, true, true, true, false, pkgInfo, rootDir)) isDependency bool
fmt.Println("----------------") pkgInfo *utils.PackageInfo
} }]()
verb := "install" unresolvedDepends := make([]string, 0)
if pkgInfo.Type == "source" {
if _, err := os.Stat("/bin/fakeroot"); os.IsNotExist(err) { // Search for packages
fmt.Printf("Skipping... cannot %s package (%s) due to fakeroot not being installed", verb, pkgInfo.Name) for _, pkg := range pkgs {
if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() {
pkgInfo, err := utils.ReadPackage(pkg)
if err != nil {
log.Fatalf("Could not read package. Error: %s\n", err)
}
if !reinstall && utils.IsPackageInstalled(pkgInfo.Name, rootDir) && utils.GetPackageInfo(pkgInfo.Name, rootDir, true).Version == pkgInfo.Version {
continue continue
} }
verb = "build" pkgsToInstall.Set(pkg, &struct {
} isDependency bool
if !forceInstall { pkgInfo *utils.PackageInfo
if pkgInfo.Arch != "any" && pkgInfo.Arch != utils.GetArch() { }{isDependency: false, pkgInfo: pkgInfo})
fmt.Printf("skipping... cannot %s a package with a different architecture\n", verb) } else {
continue entry, _, err := utils.GetRepositoryEntry(pkg)
} if err != nil {
if unresolved := utils.CheckDependencies(pkgInfo, true, true, rootDir); len(unresolved) != 0 { log.Fatalf("Could not find package (%s) in any repository\n", pkg)
fmt.Printf("skipping... cannot %s package (%s) due to missing dependencies: %s\n", verb, pkgInfo.Name, strings.Join(unresolved, ", ")) }
continue if !reinstall && utils.IsPackageInstalled(entry.Info.Name, rootDir) && utils.GetPackageInfo(entry.Info.Name, rootDir, true).Version == entry.Info.Version {
}
if conflicts := utils.CheckConflicts(pkgInfo, true, rootDir); len(conflicts) != 0 {
fmt.Printf("skipping... cannot %s package (%s) due to conflicting packages: %s\n", verb, pkgInfo.Name, strings.Join(conflicts, ", "))
continue continue
} }
pkgsToFetch.Set(entry.Info.Name, &struct {
isDependency bool
pkgInfo *utils.PackageInfo
}{isDependency: false, pkgInfo: entry.Info})
} }
if rootDir != "/" { }
fmt.Println("Warning: Operating in " + rootDir)
} // Check for dependencies and conflicts
if !yesAll { clone := pkgsToFetch.Copy()
reader := bufio.NewReader(os.Stdin) pkgsToFetch = orderedmap.NewOrderedMap[string, *struct {
if pkgInfo.Type == "source" { isDependency bool
fmt.Print("Would you like to view the source.sh file of this package? [Y\\n] ") pkgInfo *utils.PackageInfo
text, _ := reader.ReadString('\n') }]()
if strings.TrimSpace(strings.ToLower(text)) != "n" && strings.TrimSpace(strings.ToLower(text)) != "no" { for _, pkg := range clone.Keys() {
script, err := utils.GetSourceScript(file) value := clone.GetElement(pkg).Value
if err != nil { resolved, unresolved := value.pkgInfo.ResolveAll(&[]string{}, &[]string{}, false, !noOptional, !reinstall, rootDir)
log.Fatalf("Could not read source script\nError: %s\n", err) unresolvedDepends = append(unresolvedDepends, unresolved...)
} for _, depend := range resolved {
fmt.Println(script) if _, ok := pkgsToFetch.Get(depend); !ok && depend != value.pkgInfo.Name {
fmt.Println("-------EOF-------") if !reinstallAll && utils.IsPackageInstalled(depend, rootDir) {
}
}
}
if utils.IsPackageInstalled(pkgInfo.Name, rootDir) {
if !yesAll {
installedInfo := utils.GetPackageInfo(pkgInfo.Name, rootDir, false)
if strings.Compare(pkgInfo.Version, installedInfo.Version) > 0 {
fmt.Println("This file contains a newer version of this package (" + installedInfo.Version + " -> " + pkgInfo.Version + ")")
fmt.Print("Do you wish to update this package? [y\\N] ")
} else if strings.Compare(pkgInfo.Version, installedInfo.Version) < 0 {
fmt.Println("This file contains an older version of this package (" + installedInfo.Version + " -> " + pkgInfo.Version + ")")
fmt.Print("Do you wish to downgrade this package? (Not recommended) [y\\N] ")
} else if strings.Compare(pkgInfo.Version, installedInfo.Version) == 0 {
fmt.Println("This package is already installed on the system and is up to date")
fmt.Printf("Do you wish to re%s this package? [y\\N] ", verb)
}
reader := bufio.NewReader(os.Stdin)
text, _ := reader.ReadString('\n')
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
fmt.Printf("Skipping package (%s)...\n", pkgInfo.Name)
continue continue
} }
} entry, _, err := utils.GetRepositoryEntry(depend)
} else if !yesAll { if err != nil {
reader := bufio.NewReader(os.Stdin) log.Fatalf("Could not find package (%s) in any repository\n", pkg)
fmt.Printf("Do you wish to %s this package? [y\\N] ", verb) }
text, _ := reader.ReadString('\n') pkgsToFetch.Set(depend, &struct {
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" { isDependency bool
fmt.Printf("Skipping package (%s)...\n", pkgInfo.Name) pkgInfo *utils.PackageInfo
continue }{isDependency: true, pkgInfo: entry.Info})
} }
} }
pkgsToFetch.Set(pkg, value)
}
for _, pkg := range pkgsToInstall.Keys() {
value, _ := pkgsToInstall.Get(pkg)
resolved, unresolved := value.pkgInfo.ResolveAll(&[]string{}, &[]string{}, false, !noOptional, !reinstall, rootDir)
unresolvedDepends = append(unresolvedDepends, unresolved...)
for _, depend := range resolved {
if _, ok := clone.Get(depend); !ok && depend != value.pkgInfo.Name {
if !reinstallAll && utils.IsPackageInstalled(depend, rootDir) {
continue
}
entry, _, err := utils.GetRepositoryEntry(depend)
if err != nil {
log.Fatalf("Could not find package (%s) in any repository\n", pkg)
}
pkgsToFetch.Set(depend, &struct {
isDependency bool
pkgInfo *utils.PackageInfo
}{isDependency: true, pkgInfo: entry.Info})
}
}
}
// Show summary
if len(unresolvedDepends) != 0 {
if force {
log.Fatalf("The following dependencies could not be found in any repositories: %s\n", strings.Join(unresolvedDepends, ", "))
} else {
log.Println("Warning: The following dependencies could not be found in any repositories: " + strings.Join(unresolvedDepends, ", "))
}
}
if pkgsToInstall.Len()+pkgsToFetch.Len() == 0 {
fmt.Println("All packages are up to date!")
os.Exit(0)
}
for _, pkg := range pkgsToInstall.Keys() {
pkgInfo, err := utils.ReadPackage(pkg)
if err != nil {
if err != nil {
log.Fatalf("Could not read package. Error: %s\n", err)
}
}
installedInfo := utils.GetPackageInfo(pkgInfo.Name, rootDir, false)
if installedInfo == nil {
fmt.Printf("%s: %s (Install)\n", pkgInfo.Name, pkgInfo.Version)
} else if strings.Compare(pkgInfo.Version, installedInfo.Version) < 0 {
fmt.Printf("%s: %s -> %s (Downgrade)\n", pkgInfo.Name, installedInfo.Version, pkgInfo.Version)
} else if strings.Compare(pkgInfo.Version, installedInfo.Version) > 0 {
fmt.Printf("%s: %s -> %s (Upgrade)\n", pkgInfo.Name, installedInfo.Version, pkgInfo.Version)
} else {
fmt.Printf("%s: %s (Reinstall)\n", pkgInfo.Name, pkgInfo.Version)
}
}
for _, pkg := range pkgsToFetch.Keys() {
pkgInfo := pkgsToFetch.GetElement(pkg).Value.pkgInfo
installedInfo := utils.GetPackageInfo(pkgInfo.Name, rootDir, false)
if installedInfo == nil {
fmt.Printf("%s: %s (Install)\n", pkgInfo.Name, pkgInfo.Version)
} else if strings.Compare(pkgInfo.Version, installedInfo.Version) < 0 {
fmt.Printf("%s: %s -> %s (Downgrade)\n", pkgInfo.Name, installedInfo.Version, pkgInfo.Version)
} else if strings.Compare(pkgInfo.Version, installedInfo.Version) > 0 {
fmt.Printf("%s: %s -> %s (Upgrade)\n", pkgInfo.Name, installedInfo.Version, pkgInfo.Version)
} else {
fmt.Printf("%s: %s (Reinstall)\n", pkgInfo.Name, pkgInfo.Version)
}
}
if rootDir != "/" {
fmt.Println("Warning: Operating in " + rootDir)
}
if !yesAll {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("Do you wish to install these %d packages? [y\\N] ", pkgsToInstall.Len()+pkgsToFetch.Len())
text, _ := reader.ReadString('\n')
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
fmt.Println("Cancelling...")
os.Exit(1)
}
}
// Fetch packages from repositories
fmt.Println("Fetching packages from available repositories...")
for _, pkg := range pkgsToFetch.Keys() {
isDependency, _ := pkgsToFetch.Get(pkg)
entry, repo, err := utils.GetRepositoryEntry(pkg)
if err != nil {
log.Fatalf("Could not find package (%s) in any repository\n", pkg)
}
fetchedPackage, err := repo.FetchPackage(entry.Info.Name)
if err != nil {
log.Fatalf("Could not fetch package (%s). Error: %s\n", pkg, err)
}
pkgsToInstall.Set(fetchedPackage, isDependency)
}
// Install fetched packages
for _, pkg := range pkgsToInstall.Keys() {
value, _ := pkgsToInstall.Get(pkg)
pkgInfo := value.pkgInfo
var err error
if value.isDependency {
err = utils.InstallPackage(pkg, rootDir, verbose, true, buildSource, skipCheck, keepTempDir)
} else {
err = utils.InstallPackage(pkg, rootDir, verbose, force, buildSource, skipCheck, keepTempDir)
}
err = utils.InstallPackage(file, rootDir, verbose, forceInstall, buildSource, skipCheck, keepTempDir)
if err != nil { if err != nil {
if pkgInfo.Type == "source" && keepTempDir { if pkgInfo.Type == "source" && keepTempDir {
fmt.Println("BPM temp directory was created at /var/tmp/bpm_source-" + pkgInfo.Name) fmt.Println("BPM temp directory was created at /var/tmp/bpm_source-" + pkgInfo.Name)
} }
log.Fatalf("Could not install package\nError: %s\n", err) log.Fatalf("Could not install package (%s). Error: %s\n", pkg, err)
} }
fmt.Printf("Package (%s) was successfully installed!\n", pkgInfo.Name) fmt.Printf("Package (%s) was successfully installed!\n", pkgInfo.Name)
if value.isDependency {
err := utils.SetInstallationReason(pkgInfo.Name, utils.Dependency, rootDir)
if err != nil {
log.Fatalf("Could not set installation reason for package\nError: %s\n", err)
}
}
if pkgInfo.Type == "source" && keepTempDir { if pkgInfo.Type == "source" && keepTempDir {
fmt.Println("** It is recommended you delete the temporary bpm folder in /var/tmp **") fmt.Println("** It is recommended you delete the temporary bpm folder in /var/tmp **")
} }
} }
case update:
if os.Getuid() != 0 {
fmt.Println("This subcommand needs to be run with superuser permissions")
os.Exit(0)
}
// Sync repositories
if !nosync {
for _, repo := range utils.BPMConfig.Repositories {
fmt.Printf("Fetching package database for repository (%s)...\n", repo.Name)
err := repo.SyncLocalDatabase()
if err != nil {
log.Fatal(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("Could not get installed packages! Error: %s\n", err)
}
toUpdate := orderedmap.NewOrderedMap[string, *struct {
isDependency bool
entry *utils.RepositoryEntry
}]()
for _, pkg := range pkgs {
entry, _, err := utils.GetRepositoryEntry(pkg)
if err != nil {
continue
}
installedInfo := utils.GetPackageInfo(pkg, rootDir, true)
if installedInfo == nil {
log.Fatalf(pkg)
}
if strings.Compare(entry.Info.Version, installedInfo.Version) > 0 {
toUpdate.Set(entry.Info.Name, &struct {
isDependency bool
entry *utils.RepositoryEntry
}{isDependency: false, entry: entry})
} else if reinstall {
toUpdate.Set(entry.Info.Name, &struct {
isDependency bool
entry *utils.RepositoryEntry
}{isDependency: false, entry: entry})
}
}
if toUpdate.Len() == 0 {
fmt.Println("All packages are up to date!")
os.Exit(0)
}
// Check for new dependencies in updated packages
unresolved := make([]string, 0)
clone := toUpdate.Copy()
for _, key := range clone.Keys() {
pkg, _ := clone.Get(key)
r, u := pkg.entry.Info.ResolveAll(&[]string{}, &[]string{}, false, !noOptional, true, rootDir)
unresolved = append(unresolved, u...)
for _, depend := range r {
if _, ok := toUpdate.Get(depend); !ok {
entry, _, err := utils.GetRepositoryEntry(depend)
if err != nil {
log.Fatalf("Could not find package (%s) in any repository\n", depend)
}
toUpdate.Set(depend, &struct {
isDependency bool
entry *utils.RepositoryEntry
}{isDependency: true, entry: entry})
}
}
}
if len(unresolved) != 0 {
if force {
log.Fatalf("The following dependencies could not be found in any repositories: %s\n", strings.Join(unresolved, ", "))
} else {
log.Println("Warning: The following dependencies could not be found in any repositories: " + strings.Join(unresolved, ", "))
}
}
for _, key := range toUpdate.Keys() {
value, _ := toUpdate.Get(key)
installedInfo := utils.GetPackageInfo(value.entry.Info.Name, rootDir, true)
if installedInfo == nil {
fmt.Printf("%s: %s (Install)\n", value.entry.Info.Name, value.entry.Info.Version)
continue
}
if strings.Compare(value.entry.Info.Version, installedInfo.Version) > 0 {
fmt.Printf("%s: %s -> %s (Upgrade)\n", value.entry.Info.Name, installedInfo.Version, value.entry.Info.Version)
} else if reinstall {
fmt.Printf("%s: %s -> %s (Reinstall)\n", value.entry.Info.Name, installedInfo.Version, value.entry.Info.Version)
}
}
// Update confirmation prompt
if !yesAll {
fmt.Printf("Are you sure you wish to update all %d packages? [y\\N] ", toUpdate.Len())
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 update...")
os.Exit(0)
}
}
// Fetch packages
pkgsToInstall := orderedmap.NewOrderedMap[string, *struct {
isDependency bool
entry *utils.RepositoryEntry
}]()
fmt.Println("Fetching packages from available repositories...")
for _, pkg := range toUpdate.Keys() {
isDependency, _ := toUpdate.Get(pkg)
entry, repo, err := utils.GetRepositoryEntry(pkg)
if err != nil {
log.Fatalf("Could not find package (%s) in any repository\n", pkg)
}
fetchedPackage, err := repo.FetchPackage(entry.Info.Name)
if err != nil {
log.Fatalf("Could not fetch package (%s). Error: %s\n", pkg, err)
}
pkgsToInstall.Set(fetchedPackage, isDependency)
}
// Install fetched packages
for _, pkg := range pkgsToInstall.Keys() {
value, _ := pkgsToInstall.Get(pkg)
pkgInfo := value.entry.Info
var err error
if value.isDependency {
err = utils.InstallPackage(pkg, rootDir, verbose, true, buildSource, skipCheck, keepTempDir)
} else {
err = utils.InstallPackage(pkg, rootDir, verbose, force, buildSource, skipCheck, keepTempDir)
}
if err != nil {
if pkgInfo.Type == "source" && keepTempDir {
fmt.Println("BPM temp directory was created at /var/tmp/bpm_source-" + pkgInfo.Name)
}
log.Fatalf("Could not install package (%s). Error: %s\n", pkg, err)
}
fmt.Printf("Package (%s) was successfully installed!\n", pkgInfo.Name)
if value.isDependency {
err := utils.SetInstallationReason(pkgInfo.Name, utils.Dependency, rootDir)
if err != nil {
log.Fatalf("Could not set installation reason for package\nError: %s\n", err)
}
}
if pkgInfo.Type == "source" && keepTempDir {
fmt.Println("** It is recommended you delete the temporary bpm folder in /var/tmp **")
}
}
case sync:
if os.Getuid() != 0 {
fmt.Println("This subcommand needs to be run with superuser permissions")
os.Exit(0)
}
if !yesAll {
fmt.Printf("Are you sure you wish to sync all databases? [y\\N] ")
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 sync...")
os.Exit(0)
}
}
for _, repo := range utils.BPMConfig.Repositories {
fmt.Printf("Fetching package database for repository (%s)...\n", repo.Name)
err := repo.SyncLocalDatabase()
if err != nil {
log.Fatal(err)
}
}
fmt.Println("All package databases synced successfully!")
case remove: case remove:
if os.Getuid() != 0 { if os.Getuid() != 0 {
fmt.Println("This subcommand needs to be run with superuser permissions") fmt.Println("This subcommand needs to be run with superuser permissions")
@ -254,7 +557,7 @@ func resolveCommand() {
fmt.Printf("Package (%s) could not be found\n", pkg) fmt.Printf("Package (%s) could not be found\n", pkg)
continue continue
} }
fmt.Println("----------------\n" + utils.CreateReadableInfo(true, true, true, true, true, pkgInfo, rootDir)) fmt.Println("----------------\n" + utils.CreateReadableInfo(false, false, false, pkgInfo, rootDir))
fmt.Println("----------------") fmt.Println("----------------")
if rootDir != "/" { if rootDir != "/" {
fmt.Println("Warning: Operating in " + rootDir) fmt.Println("Warning: Operating in " + rootDir)
@ -333,22 +636,36 @@ 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] | 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(" -i shows information about the currently installed package")
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 install [-R, -v, -y, -f, -o, -c, -b, -k] <files...> | installs the following files") 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] <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 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(" -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(" -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(" -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-all Same as --reinstall but also reinstalls dependencies")
fmt.Println(" --no-optional Prevents installation of optional dependencies")
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(" -v Show additional information about what BPM is doing")
fmt.Println(" -y skips the confirmation prompt")
fmt.Println(" -f skips dependency, conflict and architecture checking")
fmt.Println(" --reinstall Fetches and reinstalls all packages even if they do not have a newer version available")
fmt.Println(" --no-sync Skips package database syncing")
fmt.Println("-> bpm sync [-R, -v, -y] | Syncs package databases without updating packages")
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(" -y skips the confirmation prompt")
fmt.Println("-> bpm remove [-R, -v, -y] <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(" -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")
@ -368,7 +685,6 @@ 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(&showInstalled, "i", false, "Shows information about the currently installed package")
infoFlagSet.Usage = printHelp infoFlagSet.Usage = printHelp
// Install flags // Install flags
installFlagSet := flag.NewFlagSet("Install flags", flag.ExitOnError) installFlagSet := flag.NewFlagSet("Install flags", flag.ExitOnError)
@ -380,8 +696,26 @@ func resolveFlags() {
installFlagSet.BoolVar(&buildSource, "b", false, "Build binary package from source package") installFlagSet.BoolVar(&buildSource, "b", false, "Build binary package from source package")
installFlagSet.BoolVar(&skipCheck, "s", false, "Skip check function during source compilation") installFlagSet.BoolVar(&skipCheck, "s", false, "Skip check function during source compilation")
installFlagSet.BoolVar(&keepTempDir, "k", false, "Keep temporary directory after source compilation") installFlagSet.BoolVar(&keepTempDir, "k", false, "Keep temporary directory after source compilation")
installFlagSet.BoolVar(&forceInstall, "f", false, "Force installation by skipping architecture and dependency resolution") installFlagSet.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(&reinstallAll, "reinstall-all", false, "Same as --reinstall but also reinstalls dependencies")
installFlagSet.BoolVar(&noOptional, "no-optional", false, "Prevents installation of optional dependencies")
installFlagSet.Usage = printHelp installFlagSet.Usage = printHelp
// Update flags
updateFlagSet := flag.NewFlagSet("Update flags", flag.ExitOnError)
updateFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
updateFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing")
updateFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
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.Usage = printHelp
// Sync flags
syncFlagSet := flag.NewFlagSet("Sync flags", flag.ExitOnError)
syncFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
syncFlagSet.BoolVar(&verbose, "v", false, "Show additional information about what BPM is doing")
syncFlagSet.BoolVar(&yesAll, "y", false, "Skip confirmation prompts")
syncFlagSet.Usage = printHelp
// Remove flags // Remove flags
removeFlagSet := flag.NewFlagSet("Remove flags", flag.ExitOnError) removeFlagSet := flag.NewFlagSet("Remove flags", flag.ExitOnError)
removeFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root") removeFlagSet.StringVar(&rootDir, "R", "/", "Set the destination root")
@ -415,6 +749,18 @@ func resolveFlags() {
return return
} }
subcommandArgs = installFlagSet.Args() subcommandArgs = installFlagSet.Args()
} else if getCommandType() == update {
err := updateFlagSet.Parse(subcommandArgs)
if err != nil {
return
}
subcommandArgs = updateFlagSet.Args()
} else if getCommandType() == sync {
err := syncFlagSet.Parse(subcommandArgs)
if err != nil {
return
}
subcommandArgs = syncFlagSet.Args()
} else if getCommandType() == remove { } else if getCommandType() == remove {
err := removeFlagSet.Parse(subcommandArgs) err := removeFlagSet.Parse(subcommandArgs)
if err != nil { if err != nil {
@ -428,5 +774,8 @@ func resolveFlags() {
} }
subcommandArgs = fileFlagSet.Args() subcommandArgs = fileFlagSet.Args()
} }
if reinstallAll {
reinstall = true
}
} }
} }

View File

@ -2,33 +2,48 @@ package utils
import ( import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"log"
"os" "os"
) )
type BPMConfigStruct struct { type BPMConfigStruct struct {
CompilationEnv []string `yaml:"compilation_env"` CompilationEnv []string `yaml:"compilation_env"`
SilentCompilation bool `yaml:"silent_compilation"` SilentCompilation bool `yaml:"silent_compilation"`
BinaryOutputDir string `yaml:"binary_output_dir"` BinaryOutputDir string `yaml:"binary_output_dir"`
CompilationDir string `yaml:"compilation_dir"` CompilationDir string `yaml:"compilation_dir"`
Repositories []*Repository `yaml:"repositories"`
} }
var BPMConfig BPMConfigStruct = BPMConfigStruct{ var BPMConfig BPMConfigStruct
CompilationEnv: make([]string, 0),
SilentCompilation: false,
BinaryOutputDir: "/var/lib/bpm/compiled/",
CompilationDir: "/var/tmp/",
}
func ReadConfig() { func ReadConfig() {
if _, err := os.Stat("/etc/bpm.conf"); os.IsNotExist(err) { if _, err := os.Stat("/etc/bpm.conf"); os.IsNotExist(err) {
return log.Fatal(err)
} }
bytes, err := os.ReadFile("/etc/bpm.conf") bytes, err := os.ReadFile("/etc/bpm.conf")
if err != nil { if err != nil {
return log.Fatal(err)
}
BPMConfig = BPMConfigStruct{
CompilationEnv: make([]string, 0),
SilentCompilation: false,
BinaryOutputDir: "/var/lib/bpm/compiled/",
CompilationDir: "/var/tmp/",
} }
err = yaml.Unmarshal(bytes, &BPMConfig) err = yaml.Unmarshal(bytes, &BPMConfig)
if err != nil { if err != nil {
return 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)
}
} }
} }

View File

@ -20,23 +20,19 @@ import (
) )
type PackageInfo struct { type PackageInfo struct {
Name string `yaml:"name,omitempty"` Name string `yaml:"name,omitempty"`
Description string `yaml:"description,omitempty"` Description string `yaml:"description,omitempty"`
Version string `yaml:"version,omitempty"` Version string `yaml:"version,omitempty"`
Url string `yaml:"url,omitempty"` Url string `yaml:"url,omitempty"`
License string `yaml:"license,omitempty"` License string `yaml:"license,omitempty"`
Arch string `yaml:"architecture,omitempty"` Arch string `yaml:"architecture,omitempty"`
Type string `yaml:"type,omitempty"` Type string `yaml:"type,omitempty"`
Keep []string `yaml:"keep,omitempty"` Keep []string `yaml:"keep,omitempty"`
Depends []string `yaml:"depends,omitempty"` Depends []string `yaml:"depends,omitempty"`
ConditionalDepends map[string][]string `yaml:"conditional_depends,omitempty"` MakeDepends []string `yaml:"make_depends,omitempty"`
MakeDepends []string `yaml:"make_depends,omitempty"` OptionalDepends []string `yaml:"optional_depends,omitempty"`
ConditionalMakeDepends map[string][]string `yaml:"conditional_make_depends,omitempty"` Conflicts []string `yaml:"conflicts,omitempty"`
Conflicts []string `yaml:"conflicts,omitempty"` Provides []string `yaml:"provides,omitempty"`
ConditionalConflicts map[string][]string `yaml:"conditional_conflicts,omitempty"`
Optional []string `yaml:"optional,omitempty"`
ConditionalOptional map[string][]string `yaml:"conditional_optional,omitempty"`
Provides []string `yaml:"provides,omitempty"`
} }
type InstallationReason string type InstallationReason string
@ -312,23 +308,19 @@ func ExecutePackageScripts(filename, rootDir string, operation Operation, postOp
func ReadPackageInfo(contents string, defaultValues bool) (*PackageInfo, error) { func ReadPackageInfo(contents string, defaultValues bool) (*PackageInfo, error) {
pkgInfo := PackageInfo{ pkgInfo := PackageInfo{
Name: "", Name: "",
Description: "", Description: "",
Version: "", Version: "",
Url: "", Url: "",
License: "", License: "",
Arch: "", Arch: "",
Type: "", Type: "",
Keep: make([]string, 0), Keep: make([]string, 0),
Depends: make([]string, 0), Depends: make([]string, 0),
ConditionalDepends: make(map[string][]string), MakeDepends: make([]string, 0),
MakeDepends: make([]string, 0), OptionalDepends: make([]string, 0),
ConditionalMakeDepends: make(map[string][]string), Conflicts: make([]string, 0),
Conflicts: make([]string, 0), Provides: make([]string, 0),
ConditionalConflicts: make(map[string][]string),
Optional: make([]string, 0),
ConditionalOptional: make(map[string][]string),
Provides: make([]string, 0),
} }
err := yaml.Unmarshal([]byte(contents), &pkgInfo) err := yaml.Unmarshal([]byte(contents), &pkgInfo)
if err != nil { if err != nil {
@ -361,7 +353,7 @@ func CreateInfoFile(pkgInfo *PackageInfo) string {
return string(bytes) return string(bytes)
} }
func CreateReadableInfo(showArchitecture, showType, showPackageRelations, showRemoteInfo, showInstallationReason 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) {
if len(array) == 0 { if len(array) == 0 {
@ -369,18 +361,6 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations, showRe
} }
ret = append(ret, fmt.Sprintf("%s: %s", label, strings.Join(array, ", "))) ret = append(ret, fmt.Sprintf("%s: %s", label, strings.Join(array, ", ")))
} }
appendMap := func(label string, m map[string][]string) {
if len(m) == 0 {
return
}
ret = append(ret, label+":")
for k, v := range m {
if len(v) == 0 {
continue
}
ret = append(ret, fmt.Sprintf(" %s: %s", k, strings.Join(v, ", ")))
}
}
ret = append(ret, "Name: "+pkgInfo.Name) ret = append(ret, "Name: "+pkgInfo.Name)
ret = append(ret, "Description: "+pkgInfo.Description) ret = append(ret, "Description: "+pkgInfo.Description)
ret = append(ret, "Version: "+pkgInfo.Version) ret = append(ret, "Version: "+pkgInfo.Version)
@ -395,22 +375,12 @@ func CreateReadableInfo(showArchitecture, showType, showPackageRelations, showRe
if showPackageRelations { if showPackageRelations {
appendArray("Dependencies", pkgInfo.Depends) appendArray("Dependencies", pkgInfo.Depends)
appendArray("Make Dependencies", pkgInfo.MakeDepends) appendArray("Make Dependencies", pkgInfo.MakeDepends)
appendArray("Provided packages", pkgInfo.Provides) appendArray("Optional dependencies", pkgInfo.OptionalDepends)
appendArray("Conflicting packages", pkgInfo.Conflicts) appendArray("Conflicting packages", pkgInfo.Conflicts)
appendArray("Optional dependencies", pkgInfo.Optional) appendArray("Provided packages", pkgInfo.Provides)
appendMap("Conditional dependencies", pkgInfo.ConditionalDepends)
appendMap("Conditional make dependencies", pkgInfo.ConditionalMakeDepends)
appendMap("Conditional conflicting packages", pkgInfo.ConditionalConflicts)
appendMap("Conditional optional dependencies", pkgInfo.ConditionalOptional)
}
if showInstallationReason {
if IsPackageInstalled(pkgInfo.Name, rootDir) {
ret = append(ret, "Installed: yes")
ret = append(ret, "Installation Reason: "+string(GetInstallationReason(pkgInfo.Name, rootDir)))
} else {
ret = append(ret, "Installed: no")
}
} }
ret = append(ret, "Installation Reason: "+string(GetInstallationReason(pkgInfo.Name, rootDir)))
return strings.Join(ret, "\n") return strings.Join(ret, "\n")
} }
@ -1016,7 +986,7 @@ func InstallPackage(filename, rootDir string, verbose, force, binaryPkgFromSrc,
if pkgInfo.Arch != "any" && pkgInfo.Arch != GetArch() { if pkgInfo.Arch != "any" && pkgInfo.Arch != GetArch() {
return errors.New("cannot install a package with a different architecture") return errors.New("cannot install a package with a different architecture")
} }
if unresolved := CheckDependencies(pkgInfo, true, true, rootDir); len(unresolved) != 0 { if unresolved := pkgInfo.CheckDependencies(pkgInfo.Type == "source", true, rootDir); len(unresolved) != 0 {
return errors.New("Could not resolve all dependencies. Missing " + strings.Join(unresolved, ", ")) return errors.New("Could not resolve all dependencies. Missing " + strings.Join(unresolved, ", "))
} }
} }
@ -1239,7 +1209,19 @@ func GetSourceScript(filename string) (string, error) {
return "", errors.New("package does not contain a source.sh file") return "", errors.New("package does not contain a source.sh file")
} }
func CheckDependencies(pkgInfo *PackageInfo, checkMake, checkConditional bool, rootDir string) []string { func (pkgInfo *PackageInfo) GetAllDependencies(checkMake, checkOptional bool) []string {
allDepends := make([]string, 0)
allDepends = append(allDepends, pkgInfo.Depends...)
if checkMake {
allDepends = append(allDepends, pkgInfo.MakeDepends...)
}
if checkOptional {
allDepends = append(allDepends, pkgInfo.OptionalDepends...)
}
return allDepends
}
func (pkgInfo *PackageInfo) CheckDependencies(checkMake, checkOptional bool, rootDir string) []string {
var ret []string var ret []string
for _, dependency := range pkgInfo.Depends { for _, dependency := range pkgInfo.Depends {
if !IsPackageProvided(dependency, rootDir) { if !IsPackageProvided(dependency, rootDir) {
@ -1253,53 +1235,47 @@ func CheckDependencies(pkgInfo *PackageInfo, checkMake, checkConditional bool, r
} }
} }
} }
if checkConditional { if checkOptional {
for condition, dependencies := range pkgInfo.ConditionalDepends { for _, dependency := range pkgInfo.OptionalDepends {
if !IsPackageInstalled(condition, rootDir) { if !IsPackageProvided(dependency, rootDir) {
continue ret = append(ret, dependency)
}
for _, dependency := range dependencies {
if !IsPackageProvided(dependency, rootDir) {
ret = append(ret, dependency)
}
}
}
if checkMake {
for condition, dependencies := range pkgInfo.ConditionalMakeDepends {
if !IsPackageInstalled(condition, rootDir) {
continue
}
for _, dependency := range dependencies {
if !IsPackageInstalled(dependency, rootDir) {
ret = append(ret, dependency)
}
}
} }
} }
} }
return ret return ret
} }
func CheckConflicts(pkgInfo *PackageInfo, checkConditional bool, 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 {
if IsPackageInstalled(conflict, rootDir) { if IsPackageInstalled(conflict, rootDir) {
ret = append(ret, conflict) ret = append(ret, conflict)
} }
} }
if checkConditional { return ret
for condition, conflicts := range pkgInfo.ConditionalConflicts { }
if !IsPackageInstalled(condition, rootDir) {
func (pkgInfo *PackageInfo) ResolveAll(resolved, unresolved *[]string, checkMake, checkOptional, ignoreInstalled bool, rootDir string) ([]string, []string) {
*unresolved = append(*unresolved, pkgInfo.Name)
for _, depend := range pkgInfo.GetAllDependencies(checkMake, checkOptional) {
if !slices.Contains(*resolved, depend) {
if slices.Contains(*unresolved, depend) || (ignoreInstalled && IsPackageInstalled(depend, rootDir)) {
continue continue
} }
for _, conflict := range conflicts { entry, _, err := GetRepositoryEntry(depend)
if IsPackageInstalled(conflict, rootDir) { if err != nil {
ret = append(ret, conflict) if !slices.Contains(*unresolved, depend) {
*unresolved = append(*unresolved, depend)
} }
continue
} }
entry.Info.ResolveAll(resolved, unresolved, checkMake, checkOptional, ignoreInstalled, rootDir)
} }
} }
return ret *resolved = append(*resolved, pkgInfo.Name)
*unresolved = stringSliceRemove(*unresolved, pkgInfo.Name)
return *resolved, *unresolved
} }
func IsPackageInstalled(pkg, rootDir string) bool { func IsPackageInstalled(pkg, rootDir string) bool {
@ -1320,7 +1296,11 @@ func IsPackageProvided(pkg, rootDir string) bool {
if p == pkg { if p == pkg {
return true return true
} }
if slices.Contains(GetPackageInfo(p, rootDir, true).Provides, pkg) { i := GetPackageInfo(p, rootDir, true)
if i == nil {
continue
}
if slices.Contains(i.Provides, pkg) {
return true return true
} }
} }

165
utils/repo_utils.go Normal file
View File

@ -0,0 +1,165 @@
package utils
import (
"errors"
"gopkg.in/yaml.v3"
"io"
"net/http"
"net/url"
"os"
"path"
"strings"
)
type Repository struct {
Name string `yaml:"name"`
Source string `yaml:"source"`
Disabled *bool `yaml:"disabled"`
Entries map[string]*RepositoryEntry
}
type RepositoryEntry struct {
Info *PackageInfo `yaml:"info"`
Download string `yaml:"download"`
}
func (repo *Repository) ContainsPackage(pkg string) bool {
_, ok := repo.Entries[pkg]
return ok
}
func (repo *Repository) ReadLocalDatabase() error {
repoFile := "/var/lib/bpm/repositories/" + repo.Name + ".bpmdb"
if _, err := os.Stat(repoFile); err != nil {
return nil
}
bytes, err := os.ReadFile(repoFile)
if err != nil {
return err
}
data := string(bytes)
for _, b := range strings.Split(data, "---") {
entry := RepositoryEntry{
Info: &PackageInfo{
Name: "",
Description: "",
Version: "",
Url: "",
License: "",
Arch: "",
Type: "",
Keep: make([]string, 0),
Depends: make([]string, 0),
MakeDepends: make([]string, 0),
OptionalDepends: make([]string, 0),
Conflicts: make([]string, 0),
Provides: make([]string, 0),
},
Download: "",
}
err := yaml.Unmarshal([]byte(b), &entry)
if err != nil {
return err
}
repo.Entries[entry.Info.Name] = &entry
}
return nil
}
func (repo *Repository) SyncLocalDatabase() error {
repoFile := "/var/lib/bpm/repositories/" + repo.Name + ".bpmdb"
err := os.MkdirAll(path.Dir(repoFile), 0755)
if err != nil {
return err
}
u, err := url.JoinPath(repo.Source, "database.bpmdb")
if err != nil {
return err
}
resp, err := http.Get(u)
if err != nil {
return err
}
defer resp.Body.Close()
out, err := os.Create(repoFile)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, resp.Body)
return nil
}
func GetRepository(name string) *Repository {
for _, repo := range BPMConfig.Repositories {
if repo.Name == name {
return repo
}
}
return nil
}
func GetRepositoryEntry(str string) (*RepositoryEntry, *Repository, error) {
split := strings.Split(str, "/")
if len(split) == 1 {
pkgName := strings.TrimSpace(split[0])
if pkgName == "" {
return nil, nil, errors.New("could not find repository entry for this package")
}
for _, repo := range BPMConfig.Repositories {
if repo.ContainsPackage(pkgName) {
return repo.Entries[pkgName], repo, nil
}
}
return nil, nil, errors.New("could not find repository entry for this package")
} else if len(split) == 2 {
repoName := strings.TrimSpace(split[0])
pkgName := strings.TrimSpace(split[1])
if repoName == "" || pkgName == "" {
return nil, nil, errors.New("could not find repository entry for this package")
}
repo := GetRepository(repoName)
if repo == nil || !repo.ContainsPackage(pkgName) {
return nil, nil, errors.New("could not find repository entry for this package")
}
return repo.Entries[pkgName], repo, nil
} else {
return nil, nil, errors.New("could not find repository entry for this package")
}
}
func (repo *Repository) FetchPackage(pkg string) (string, error) {
if !repo.ContainsPackage(pkg) {
return "", errors.New("Could not fetch package '" + pkg + "'")
}
entry := repo.Entries[pkg]
URL, err := url.JoinPath(repo.Source, entry.Download)
if err != nil {
return "", err
}
resp, err := http.Get(URL)
if err != nil {
return "", err
}
defer resp.Body.Close()
err = os.MkdirAll("/var/cache/bpm/packages/", 0755)
if err != nil {
return "", err
}
out, err := os.Create("/var/cache/bpm/packages/" + path.Base(entry.Download))
if err != nil {
return "", err
}
defer out.Close()
_, err = io.Copy(out, resp.Body)
return "/var/cache/bpm/packages/" + path.Base(entry.Download), nil
}