Merge branch 'remote-repositories-functionality' into 'master'

Add repository functionality to BPM

See merge request bubble-package-manager/bpm!4
This commit is contained in:
EnumDev 2024-09-09 08:45:45 +00:00
commit 368b098888
7 changed files with 728 additions and 210 deletions

View File

@ -2,3 +2,7 @@ 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=

525
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, err = utils.ReadPackage(pkg)
if err != nil {
fmt.Printf("File (%s) could not be read\n", pkg)
continue
}
} else {
info = utils.GetPackageInfo(pkg, rootDir, false) info = utils.GetPackageInfo(pkg, rootDir, false)
if info == nil { if info == nil {
fmt.Printf("Package (%s) could not be found\n", pkg) log.Fatalf("Package (%s) is not installed\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 {
isDependency bool
pkgInfo *utils.PackageInfo
}]()
pkgsToFetch := orderedmap.NewOrderedMap[string, *struct {
isDependency bool
pkgInfo *utils.PackageInfo
}]()
unresolvedDepends := make([]string, 0)
// Search for packages
for _, pkg := range pkgs {
if stat, err := os.Stat(pkg); err == nil && !stat.IsDir() {
pkgInfo, err := utils.ReadPackage(pkg)
if err != nil { if err != nil {
log.Fatalf("Could not read package\nError: %s\n", err) log.Fatalf("Could not read package. Error: %s\n", err)
} }
if !yesAll { if !reinstall && utils.IsPackageInstalled(pkgInfo.Name, rootDir) && utils.GetPackageInfo(pkgInfo.Name, rootDir, true).Version == pkgInfo.Version {
fmt.Println("----------------\n" + utils.CreateReadableInfo(true, true, true, true, false, pkgInfo, rootDir))
fmt.Println("----------------")
}
verb := "install"
if pkgInfo.Type == "source" {
if _, err := os.Stat("/bin/fakeroot"); os.IsNotExist(err) {
fmt.Printf("Skipping... cannot %s package (%s) due to fakeroot not being installed", verb, pkgInfo.Name)
continue continue
} }
verb = "build" pkgsToInstall.Set(pkg, &struct {
isDependency bool
pkgInfo *utils.PackageInfo
}{isDependency: false, pkgInfo: pkgInfo})
} else {
entry, _, err := utils.GetRepositoryEntry(pkg)
if err != nil {
log.Fatalf("Could not find package (%s) in any repository\n", pkg)
} }
if !forceInstall { if !reinstall && utils.IsPackageInstalled(entry.Info.Name, rootDir) && utils.GetPackageInfo(entry.Info.Name, rootDir, true).Version == entry.Info.Version {
if pkgInfo.Arch != "any" && pkgInfo.Arch != utils.GetArch() {
fmt.Printf("skipping... cannot %s a package with a different architecture\n", verb)
continue continue
} }
if unresolved := utils.CheckDependencies(pkgInfo, true, true, rootDir); len(unresolved) != 0 { pkgsToFetch.Set(entry.Info.Name, &struct {
fmt.Printf("skipping... cannot %s package (%s) due to missing dependencies: %s\n", verb, pkgInfo.Name, strings.Join(unresolved, ", ")) isDependency bool
pkgInfo *utils.PackageInfo
}{isDependency: false, pkgInfo: entry.Info})
}
}
// Check for dependencies and conflicts
clone := pkgsToFetch.Copy()
pkgsToFetch = orderedmap.NewOrderedMap[string, *struct {
isDependency bool
pkgInfo *utils.PackageInfo
}]()
for _, pkg := range clone.Keys() {
value := clone.GetElement(pkg).Value
resolved, unresolved := value.pkgInfo.ResolveAll(&[]string{}, &[]string{}, false, !noOptional, !reinstall, rootDir)
unresolvedDepends = append(unresolvedDepends, unresolved...)
for _, depend := range resolved {
if _, ok := pkgsToFetch.Get(depend); !ok && depend != value.pkgInfo.Name {
if !reinstallAll && utils.IsPackageInstalled(depend, rootDir) {
continue continue
} }
if conflicts := utils.CheckConflicts(pkgInfo, true, rootDir); len(conflicts) != 0 { entry, _, err := utils.GetRepositoryEntry(depend)
fmt.Printf("skipping... cannot %s package (%s) due to conflicting packages: %s\n", verb, pkgInfo.Name, strings.Join(conflicts, ", ")) 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})
}
}
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 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 != "/" { if rootDir != "/" {
fmt.Println("Warning: Operating in " + rootDir) fmt.Println("Warning: Operating in " + rootDir)
} }
if !yesAll { if !yesAll {
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
if pkgInfo.Type == "source" { fmt.Printf("Do you wish to install these %d packages? [y\\N] ", pkgsToInstall.Len()+pkgsToFetch.Len())
fmt.Print("Would you like to view the source.sh file of this package? [Y\\n] ")
text, _ := reader.ReadString('\n')
if strings.TrimSpace(strings.ToLower(text)) != "n" && strings.TrimSpace(strings.ToLower(text)) != "no" {
script, err := utils.GetSourceScript(file)
if err != nil {
log.Fatalf("Could not read source script\nError: %s\n", err)
}
fmt.Println(script)
fmt.Println("-------EOF-------")
}
}
}
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') text, _ := reader.ReadString('\n')
if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" { if strings.TrimSpace(strings.ToLower(text)) != "y" && strings.TrimSpace(strings.ToLower(text)) != "yes" {
fmt.Printf("Skipping package (%s)...\n", pkgInfo.Name) fmt.Println("Cancelling...")
continue os.Exit(1)
}
}
} else if !yesAll {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("Do you wish to %s this package? [y\\N] ", verb)
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
} }
} }
err = utils.InstallPackage(file, rootDir, verbose, forceInstall, buildSource, skipCheck, keepTempDir) // 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)
}
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,6 +2,7 @@ package utils
import ( import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"log"
"os" "os"
) )
@ -10,25 +11,39 @@ type BPMConfigStruct struct {
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
func ReadConfig() {
if _, err := os.Stat("/etc/bpm.conf"); os.IsNotExist(err) {
log.Fatal(err)
}
bytes, err := os.ReadFile("/etc/bpm.conf")
if err != nil {
log.Fatal(err)
}
BPMConfig = BPMConfigStruct{
CompilationEnv: make([]string, 0), CompilationEnv: make([]string, 0),
SilentCompilation: false, SilentCompilation: false,
BinaryOutputDir: "/var/lib/bpm/compiled/", BinaryOutputDir: "/var/lib/bpm/compiled/",
CompilationDir: "/var/tmp/", CompilationDir: "/var/tmp/",
}
func ReadConfig() {
if _, err := os.Stat("/etc/bpm.conf"); os.IsNotExist(err) {
return
}
bytes, err := os.ReadFile("/etc/bpm.conf")
if err != nil {
return
} }
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

@ -29,13 +29,9 @@ type PackageInfo struct {
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"`
ConditionalMakeDepends map[string][]string `yaml:"conditional_make_depends,omitempty"` OptionalDepends []string `yaml:"optional_depends,omitempty"`
Conflicts []string `yaml:"conflicts,omitempty"` Conflicts []string `yaml:"conflicts,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"` Provides []string `yaml:"provides,omitempty"`
} }
@ -321,13 +317,9 @@ func ReadPackageInfo(contents string, defaultValues bool) (*PackageInfo, error)
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),
ConditionalMakeDepends: make(map[string][]string), OptionalDepends: make([]string, 0),
Conflicts: make([]string, 0), Conflicts: make([]string, 0),
ConditionalConflicts: make(map[string][]string),
Optional: make([]string, 0),
ConditionalOptional: make(map[string][]string),
Provides: make([]string, 0), Provides: make([]string, 0),
} }
err := yaml.Unmarshal([]byte(contents), &pkgInfo) err := yaml.Unmarshal([]byte(contents), &pkgInfo)
@ -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))) ret = append(ret, "Installation Reason: "+string(GetInstallationReason(pkgInfo.Name, rootDir)))
} else {
ret = append(ret, "Installed: no")
}
}
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) {
continue
}
for _, dependency := range dependencies {
if !IsPackageProvided(dependency, rootDir) { if !IsPackageProvided(dependency, rootDir) {
ret = append(ret, dependency) 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)
} }
} }
} *resolved = append(*resolved, pkgInfo.Name)
} *unresolved = stringSliceRemove(*unresolved, pkgInfo.Name)
return ret 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
}