8 Commits
5 changed files with 210 additions and 4 deletions
+22 -3
View File
@@ -12,6 +12,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"slices" "slices"
"strconv"
"strings" "strings"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
@@ -24,8 +25,9 @@ var skipCheck = flag.BoolP("skip-checks", "s", false, "Skip 'check' function whi
var keepCompilationFiles = flag.BoolP("keep", "k", false, "Keep compilation files after successful package compilation") var keepCompilationFiles = flag.BoolP("keep", "k", false, "Keep compilation files after successful package compilation")
var installDepends = flag.BoolP("depends", "d", false, "Install package dependencies for compilation") var installDepends = flag.BoolP("depends", "d", false, "Install package dependencies for compilation")
var installPackage = flag.BoolP("install", "i", false, "Install compiled BPM package after compilation finishes") var installPackage = flag.BoolP("install", "i", false, "Install compiled BPM package after compilation finishes")
var compilationJobs = flag.IntP("jobs", "j", 0, "Set the amount of concurrent processes to use for source package compilation")
var moveToBinaryDir = flag.BoolP("move", "m", false, "Move output packages to the current repository's binary directory") var moveToBinaryDir = flag.BoolP("move", "m", false, "Move output packages to the current repository's binary directory")
var updateChecksums = flag.BoolP("update-checksums", "u", false, "Update the checksums for all download entries") var updateInfo = flag.BoolP("update-info", "u", false, "Update the pkg.info file")
var yesAll = flag.BoolP("yes", "y", false, "Accept all confirmation prompts") var yesAll = flag.BoolP("yes", "y", false, "Accept all confirmation prompts")
func main() { func main() {
@@ -56,6 +58,12 @@ func runChecks() {
} }
func createArchive() string { func createArchive() string {
// Read BPM utils config
config, err := bpmutilsshared.ReadBPMUtilsConfig()
if err != nil {
log.Fatalf("Error: failed to read config: %s", err)
}
filesToInclude := make([]string, 0) filesToInclude := make([]string, 0)
// Include base files // Include base files
@@ -84,8 +92,9 @@ func createArchive() string {
log.Fatalf("Error: could not read package info: %s", err) log.Fatalf("Error: could not read package info: %s", err)
} }
// Update checksums // Update pkg.info file
if *updateChecksums { if *updateInfo {
// Update download checksums
for i, download := range pkgInfo.Downloads { for i, download := range pkgInfo.Downloads {
if download.Checksum == "skip" { if download.Checksum == "skip" {
continue continue
@@ -99,6 +108,13 @@ func createArchive() string {
pkgInfo.Downloads[i] = download pkgInfo.Downloads[i] = download
} }
// Add default maintainer
if config.AddDefaultMaintainer {
if !slices.Contains(pkgInfo.Maintainers, config.DefaultMaintainer) {
pkgInfo.Maintainers = append(pkgInfo.Maintainers, config.DefaultMaintainer)
}
}
// Save yaml back to file // Save yaml back to file
var data bytes.Buffer var data bytes.Buffer
encoder := yaml.NewEncoder(&data) encoder := yaml.NewEncoder(&data)
@@ -174,6 +190,9 @@ func compilePackage(archive string) {
if *installDepends { if *installDepends {
args = append(args, "-d") args = append(args, "-d")
} }
if *compilationJobs > 0 {
args = append(args, "-j"+strconv.Itoa(*compilationJobs))
}
if *yesAll { if *yesAll {
args = append(args, "-y") args = append(args, "-y")
} }
+170
View File
@@ -3,6 +3,7 @@ package main
import ( import (
bpmutilsshared "bpm-utils-shared" bpmutilsshared "bpm-utils-shared"
"bufio" "bufio"
"bytes"
"context" "context"
"fmt" "fmt"
"io/fs" "io/fs"
@@ -85,6 +86,7 @@ func main() {
flagset := flag.NewFlagSet("check-versions", flag.ExitOnError) flagset := flag.NewFlagSet("check-versions", flag.ExitOnError)
flagset.BoolP("verbose", "v", false, "Show additional information about the current operation") flagset.BoolP("verbose", "v", false, "Show additional information about the current operation")
flagset.BoolP("force", "f", false, "Force current operation to bypass certain conditions") flagset.BoolP("force", "f", false, "Force current operation to bypass certain conditions")
flagset.BoolP("apply", "a", false, "Apply new versions to packages")
setupFlagsAndHelp(flagset, fmt.Sprintf("bpm-repo %s <options>", subcommand), "Manage BPM repositories and databases", os.Args[2:]) setupFlagsAndHelp(flagset, fmt.Sprintf("bpm-repo %s <options>", subcommand), "Manage BPM repositories and databases", os.Args[2:])
currentFlagSet = flagset currentFlagSet = flagset
@@ -95,6 +97,22 @@ func main() {
} }
checkVersionsFunc(repo) checkVersionsFunc(repo)
case "compile-all", "a":
// Setup flags and help
flagset := flag.NewFlagSet("check-versions", flag.ExitOnError)
flagset.BoolP("verbose", "v", false, "Show additional information about the current operation")
flagset.BoolP("modified", "m", true, "Skip non-modified source packages")
flagset.BoolP("show-order", "o", false, "Show the order in which all packages will be compiled and exit")
setupFlagsAndHelp(flagset, fmt.Sprintf("bpm-repo %s <options>", subcommand), "Manage BPM repositories and databases", os.Args[2:])
currentFlagSet = flagset
// Get current database
repo := bpmutilsshared.GetRepository()
if repo == "" {
log.Fatal("Error: this command may only be run inside a BPM repository")
}
compileAllPackagesFunc(repo)
default: default:
log.Println("Error: unknown subcommand") log.Println("Error: unknown subcommand")
listSubcommands() listSubcommands()
@@ -126,6 +144,7 @@ func checkVersionsFunc(repo string) {
// Get flags // Get flags
verbose, _ := currentFlagSet.GetBool("verbose") verbose, _ := currentFlagSet.GetBool("verbose")
force, _ := currentFlagSet.GetBool("force") force, _ := currentFlagSet.GetBool("force")
apply, _ := currentFlagSet.GetBool("apply")
// Read environment files // Read environment files
err := readEnvFile(repo) err := readEnvFile(repo)
@@ -241,6 +260,31 @@ func checkVersionsFunc(repo string) {
OldVersion: pkgInfo.Version, OldVersion: pkgInfo.Version,
NewVersion: latestVersion, NewVersion: latestVersion,
} }
// Apply new version
pkgInfo.Version = latestVersion
pkgInfo.Revision = 1
if apply {
var data bytes.Buffer
encoder := yaml.NewEncoder(&data)
encoder.SetIndent(2)
encoder.Encode(pkgInfo)
// Write package information
fmt.Println(path.Join(dir, "pkg.info"))
err := os.WriteFile(path.Join(dir, "pkg.info"), data.Bytes(), 0644)
if err != nil {
log.Printf("Warning: could not write new version for package (%s) to file: %s", pkgInfo.Name, err)
}
// Generate source package
cmd := exec.Command("bpm-package")
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Printf("Warning: could not generate source pacakge (%s): %s", pkgInfo.Name, err)
}
}
continue continue
} }
@@ -334,6 +378,130 @@ func listPackagesFunc(repo string) {
} }
} }
func compileAllPackagesFunc(repo string) {
// Get flags
verbose, _ := currentFlagSet.GetBool("verbose")
modifiedOnly, _ := currentFlagSet.GetBool("modified")
showOrder, _ := currentFlagSet.GetBool("show-order")
// Read BPM utils config
config, err := bpmutilsshared.ReadBPMUtilsConfig()
if err != nil {
log.Fatalf("Error: failed to read config: %s", err)
}
// Ensure database is updated
bpmutilsshared.UpdateDatabases(repo)
// Read databases
sourceDatabase, err := bpmutilsshared.ReadDatabase(path.Join(repo, "source/database.bpmdb"))
if err != nil {
log.Fatalf("Error: could not read source database: %s", err)
}
binaryDatabase, _ := bpmutilsshared.ReadDatabase(path.Join(repo, "binary/database.bpmdb"))
// Get all packages from database entries
packages := slices.Collect(maps.Values(sourceDatabase.Entries))
sort.Slice(packages, func(a, b int) bool {
return packages[a].PackageInfo.Name < packages[b].PackageInfo.Name
})
// Toposort packages using Depth-first search algorithm
sorted := make([]bpmutilsshared.PackageInfo, 0)
marked := make(map[string]int) // 0 = Unmarked, 1 = Temporary mark, 2 = Permanent mark
var visit func(pkgInfo *bpmutilsshared.PackageInfo) error
visit = func(pkgInfo *bpmutilsshared.PackageInfo) error {
if mark, _ := marked[pkgInfo.Name]; mark == 2 {
return nil
} else if mark == 1 {
return fmt.Errorf("Circular dependency found!")
}
marked[pkgInfo.Name] = 1
// Get all dependencies
depends := slices.Clone(pkgInfo.Depends)
depends = append(depends, pkgInfo.MakeDepends...)
for _, depend := range depends {
// Find package in repository
dependEntry, ok := sourceDatabase.Entries[depend]
if !ok {
// Search for virtual package
for _, entry := range sourceDatabase.Entries {
if slices.Contains(entry.PackageInfo.Provides, depend) {
dependEntry = entry
ok = true
break
}
}
if !ok {
continue
}
}
err := visit(dependEntry.PackageInfo)
if err != nil && verbose {
fmt.Printf("Circular dependency found! (%s -> %s)\n", pkgInfo.Name, dependEntry.PackageInfo.Name)
}
}
marked[pkgInfo.Name] = 2
sorted = append(sorted, *pkgInfo)
return nil
}
for _, entry := range packages {
if mark, _ := marked[entry.PackageInfo.Name]; mark != 2 {
visit(entry.PackageInfo)
}
}
// Compile all packages in order
for _, pkgInfo := range sorted {
if modifiedOnly && binaryDatabase != nil {
if binaryPkgInfo, ok := binaryDatabase.Entries[pkgInfo.Name]; ok && binaryPkgInfo.PackageInfo.GetFullVersion() == pkgInfo.GetFullVersion() {
continue
}
}
if showOrder {
fmt.Println(pkgInfo.Name)
continue
}
// Ensure installed packages are up-to-date
cmd := exec.Command(config.PrivilegeEscalatorCmd, "sh", "-c", "bpm u -y")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatalf("Error: could not update packages): %s", err)
}
// Compile source package
cmd = exec.Command("bpm-package", "-cdvumy")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = path.Join(repo, "source", pkgInfo.Name)
if err := cmd.Run(); err != nil {
log.Fatalf("Error: could not compile package (%s): %s", pkgInfo.Name, err)
}
}
// Ensure installed packages are up-to-date
cmd := exec.Command(config.PrivilegeEscalatorCmd, "sh", "-c", "bpm u -y")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatalf("Error: could not update packages): %s", err)
}
}
func readEnvFile(repo string) error { func readEnvFile(repo string) error {
data, err := os.ReadFile(path.Join(repo, ".env")) data, err := os.ReadFile(path.Join(repo, ".env"))
if os.IsNotExist(err) { if os.IsNotExist(err) {
@@ -367,6 +535,8 @@ func listSubcommands() {
fmt.Println(" u, update-db Update update source and binary databases in current repositor") fmt.Println(" u, update-db Update update source and binary databases in current repositor")
fmt.Println(" v, check-versions Manage BPM repositories and databases") fmt.Println(" v, check-versions Manage BPM repositories and databases")
fmt.Println(" l, list List packages") fmt.Println(" l, list List packages")
fmt.Println(" a, compile-all Compile all packages in the current repository")
} }
func setupFlagsAndHelp(flagset *flag.FlagSet, usage, desc string, args []string) { func setupFlagsAndHelp(flagset *flag.FlagSet, usage, desc string, args []string) {
+10 -1
View File
@@ -108,12 +108,18 @@ func replaceVariables(templateContents string) string {
} }
func createDirectory() { func createDirectory() {
// Read BPM utils config
config, err := bpmutilsshared.ReadBPMUtilsConfig()
if err != nil {
log.Fatalf("Error: failed to read config: %s", err)
}
// Trim spaces // Trim spaces
*directory = strings.TrimSpace(*directory) *directory = strings.TrimSpace(*directory)
*name = strings.TrimSpace(*name) *name = strings.TrimSpace(*name)
// Create directory // Create directory
err := os.Mkdir(*directory, 0755) err = os.Mkdir(*directory, 0755)
if err != nil { if err != nil {
log.Fatalf("Error: could not create directory: %s", err) log.Fatalf("Error: could not create directory: %s", err)
} }
@@ -137,6 +143,9 @@ func createDirectory() {
}, },
}, },
} }
if config.DefaultMaintainer != "" {
pkgInfo.Maintainers = append(pkgInfo.Maintainers, config.DefaultMaintainer)
}
var buffer bytes.Buffer var buffer bytes.Buffer
encoder := yaml.NewEncoder(&buffer) encoder := yaml.NewEncoder(&buffer)
+2
View File
@@ -8,6 +8,8 @@ import (
type BPMUtilsConfig struct { type BPMUtilsConfig struct {
PrivilegeEscalatorCmd string `yaml:"privilege_escalator_cmd"` PrivilegeEscalatorCmd string `yaml:"privilege_escalator_cmd"`
DefaultMaintainer string `yaml:"default_maintainer,omitempty"`
AddDefaultMaintainer bool `yaml:"add_default_maintainer,omitempty"`
} }
func ReadBPMUtilsConfig() (*BPMUtilsConfig, error) { func ReadBPMUtilsConfig() (*BPMUtilsConfig, error) {
+6
View File
@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"strconv"
"strings" "strings"
"github.com/drone/envsubst" "github.com/drone/envsubst"
@@ -17,6 +18,7 @@ type PackageInfo struct {
Revision int `yaml:"revision,omitempty"` Revision int `yaml:"revision,omitempty"`
Url string `yaml:"url,omitempty"` Url string `yaml:"url,omitempty"`
License string `yaml:"license,omitempty"` License string `yaml:"license,omitempty"`
Maintainers []string `yaml:"maintainers,omitempty"`
Arch string `yaml:"architecture,omitempty"` Arch string `yaml:"architecture,omitempty"`
OutputArch string `yaml:"output_architecture,omitempty"` OutputArch string `yaml:"output_architecture,omitempty"`
Type string `yaml:"type,omitempty"` Type string `yaml:"type,omitempty"`
@@ -75,6 +77,10 @@ func ReadPackageInfo(data []byte) (*PackageInfo, error) {
return pkgInfo, nil return pkgInfo, nil
} }
func (pkgInfo *PackageInfo) GetFullVersion() string {
return pkgInfo.Version + "-" + strconv.Itoa(pkgInfo.Revision)
}
func ReadPacakgeInfoFromTarball(path string) (*PackageInfo, error) { func ReadPacakgeInfoFromTarball(path string) (*PackageInfo, error) {
// Extract package info using tar // Extract package info using tar
cmd := exec.Command("tar", "-x", "-f", path, "pkg.info", "-O") cmd := exec.Command("tar", "-x", "-f", path, "pkg.info", "-O")