mirror of
https://github.com/EnumeratedDev/bpm-utils.git
synced 2026-09-16 11:06:11 +00:00
Add 'check-versions' flag to bpm-repo
This commit is contained in:
+1
-4
@@ -5,10 +5,7 @@ go 1.23
|
||||
require (
|
||||
bpm-utils-shared v1.0.0
|
||||
github.com/spf13/pflag v1.0.10
|
||||
)
|
||||
|
||||
require (
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
replace bpm-utils-shared => ../bpm-utils-shared
|
||||
|
||||
+186
-2
@@ -4,21 +4,30 @@ import (
|
||||
bpmutilsshared "bpm-utils-shared"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"maps"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var createRepo = flag.BoolP("create", "c", false, "Create a new BPM repository")
|
||||
var updateDatabases = flag.BoolP("update-databases", "u", false, "Update update source and binary databases in current repository")
|
||||
var checkVersions = flag.BoolP("check-versions", "v", false, "Get the latest version of each package")
|
||||
var listPackages = flag.BoolP("list", "l", false, "List packages")
|
||||
|
||||
var verbose = flag.Bool("verbose", false, "Show additional information about current operation")
|
||||
var force = flag.BoolP("force", "f", false, "Force current operation to bypass certain conditions")
|
||||
|
||||
func main() {
|
||||
// Setup flags and help
|
||||
bpmutilsshared.SetupHelp("bpm-repo <options>", "Manage BPM repositories and databases")
|
||||
@@ -52,6 +61,14 @@ func main() {
|
||||
}
|
||||
|
||||
bpmutilsshared.UpdateDatabases(repo)
|
||||
} else if *checkVersions {
|
||||
// Get current database
|
||||
repo := bpmutilsshared.GetRepository()
|
||||
if repo == "" {
|
||||
log.Fatal("Error: this command may only be run inside a BPM repository")
|
||||
}
|
||||
|
||||
checkVersionsFunc(repo)
|
||||
} else if *listPackages {
|
||||
// Get current database
|
||||
repo := bpmutilsshared.GetRepository()
|
||||
@@ -59,7 +76,7 @@ func main() {
|
||||
log.Fatal("Error: this command may only be run inside a BPM repository")
|
||||
}
|
||||
|
||||
listPacakges(repo)
|
||||
listPackagesFunc(repo)
|
||||
} else {
|
||||
bpmutilsshared.ShowHelp()
|
||||
}
|
||||
@@ -80,7 +97,149 @@ func createRepository(name, description string) {
|
||||
fmt.Println("Repository created successfully!")
|
||||
}
|
||||
|
||||
func listPacakges(repo string) {
|
||||
func checkVersionsFunc(repo string) {
|
||||
// Read environment files
|
||||
err := readEnvFile(repo)
|
||||
if err != nil {
|
||||
log.Fatalf("Error: could not read environment file: %s", err)
|
||||
}
|
||||
|
||||
// Read version cache
|
||||
type CachedVersionEntry struct {
|
||||
LatestVersion string `yaml:"latest_version"`
|
||||
Timestamp int64 `yaml:"timestamp"`
|
||||
}
|
||||
cachedVersions := make(map[string]CachedVersionEntry)
|
||||
data, err := os.ReadFile(path.Join(repo, ".version-cache"))
|
||||
if err == nil {
|
||||
err := yaml.Unmarshal(data, &cachedVersions)
|
||||
if err != nil {
|
||||
cachedVersions = make(map[string]CachedVersionEntry)
|
||||
}
|
||||
}
|
||||
|
||||
directories := make([]string, 0)
|
||||
if flag.NArg() > 0 {
|
||||
for _, dir := range flag.Args() {
|
||||
if _, err := os.Stat(path.Join(repo, "source", dir, "pkg.info")); err != nil {
|
||||
log.Fatalf("Error: could not find pkg.info file in directory (%s): %s", dir, err)
|
||||
}
|
||||
directories = append(directories, path.Join(repo, "source", dir))
|
||||
}
|
||||
} else {
|
||||
// Loop through each directory with a 'pkg.info' file
|
||||
err := filepath.Walk(path.Join(repo, "source"), func(path string, info fs.FileInfo, err error) error {
|
||||
if filepath.Base(path) == "pkg.info" {
|
||||
directories = append(directories, filepath.Dir(path))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Error: could not loop through all packages: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
pkgsWithoutScript := make([]string, 0)
|
||||
pkgsWithError := make(map[string]error)
|
||||
pkgsWithUpdates := make(map[string]struct {
|
||||
OldVersion string
|
||||
NewVersion string
|
||||
}, 0)
|
||||
pkgsUpToDate := 0
|
||||
for _, dir := range directories {
|
||||
pkgInfo, err := bpmutilsshared.ReadPacakgeInfoFromFile(path.Join(dir, "pkg.info"))
|
||||
if err != nil {
|
||||
log.Fatalf("Could not read package info: %s", err)
|
||||
}
|
||||
|
||||
// Check cached latest version
|
||||
latestVersion := ""
|
||||
if cachedVersion, ok := cachedVersions[pkgInfo.Name]; ok && !*force && time.Since(time.UnixMilli(cachedVersion.Timestamp)).Milliseconds() < 604800000 {
|
||||
latestVersion = cachedVersion.LatestVersion
|
||||
} else {
|
||||
// Check whether check-version.sh script exists
|
||||
if _, err := os.Stat(path.Join(dir, "check-version.sh")); err != nil {
|
||||
pkgsWithoutScript = append(pkgsWithoutScript, pkgInfo.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
// Execute check-version.sh script
|
||||
cmd := exec.Command("bash", "-e", path.Join(dir, "check-version.sh"))
|
||||
cmd.Environ()
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
pkgsWithError[pkgInfo.Name] = err
|
||||
continue
|
||||
}
|
||||
latestVersion = strings.TrimSpace(string(output))
|
||||
|
||||
// Ensure latest version is valid
|
||||
if latestVersion == "" || latestVersion == "null" {
|
||||
pkgsWithError[pkgInfo.Name] = fmt.Errorf("invalid version number \"%s\"", latestVersion)
|
||||
continue
|
||||
}
|
||||
|
||||
// Cache latest version
|
||||
cachedVersions[pkgInfo.Name] = CachedVersionEntry{
|
||||
LatestVersion: latestVersion,
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
}
|
||||
}
|
||||
|
||||
// Compare versions
|
||||
if pkgInfo.Version != latestVersion {
|
||||
pkgsWithUpdates[pkgInfo.Name] = struct {
|
||||
OldVersion string
|
||||
NewVersion string
|
||||
}{
|
||||
OldVersion: pkgInfo.Version,
|
||||
NewVersion: latestVersion,
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
pkgsUpToDate++
|
||||
}
|
||||
|
||||
// Save cached versions to file
|
||||
data, err = yaml.Marshal(cachedVersions)
|
||||
if err == nil {
|
||||
err := os.WriteFile(path.Join(repo, ".version-cache"), data, 0644)
|
||||
if err != nil {
|
||||
log.Printf("Warning: could not write cached versions to file: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Print updates
|
||||
keys := slices.Collect(maps.Keys(pkgsWithUpdates))
|
||||
sort.Strings(keys)
|
||||
for _, pkg := range keys {
|
||||
fmt.Printf("Update available for package (%s): %s -> %s\n", pkg, pkgsWithUpdates[pkg].OldVersion, pkgsWithUpdates[pkg].NewVersion)
|
||||
}
|
||||
|
||||
if *verbose {
|
||||
// Print packages without check-version.sh script
|
||||
for _, pkg := range pkgsWithoutScript {
|
||||
log.Printf("Warning: package (%s) has no check-version.sh script\n", pkg)
|
||||
}
|
||||
|
||||
// Print errors
|
||||
keys = slices.Collect(maps.Keys(pkgsWithError))
|
||||
sort.Strings(keys)
|
||||
for _, pkg := range keys {
|
||||
log.Printf("Error: check-version.sh script for package (%s) failed: %s", pkg, pkgsWithError[pkg])
|
||||
}
|
||||
}
|
||||
|
||||
// Print summary
|
||||
fmt.Println("----- Summary -----")
|
||||
fmt.Println("Available updates:", len(pkgsWithUpdates))
|
||||
fmt.Println("Up to date:", pkgsUpToDate)
|
||||
fmt.Println("Missing script:", len(pkgsWithoutScript))
|
||||
fmt.Println("Errors:", len(pkgsWithError))
|
||||
}
|
||||
|
||||
func listPackagesFunc(repo string) {
|
||||
// Read databases
|
||||
sourceDatabase, err := bpmutilsshared.ReadDatabase(path.Join(repo, "source/database.bpmdb"))
|
||||
if err != nil {
|
||||
@@ -122,3 +281,28 @@ func listPacakges(repo string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readEnvFile(repo string) error {
|
||||
data, err := os.ReadFile(path.Join(repo, ".env"))
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" || line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
|
||||
splitLine := strings.Split(line, "=")
|
||||
if len(splitLine) != 2 {
|
||||
return fmt.Errorf("invalid format")
|
||||
}
|
||||
|
||||
os.Setenv(splitLine[0], splitLine[1])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package bpm_utils_shared
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -60,3 +61,38 @@ func ReadPacakgeInfo(path string) (*PackageInfo, error) {
|
||||
|
||||
return pkgInfo, nil
|
||||
}
|
||||
|
||||
func ReadPacakgeInfoFromFile(path string) (*PackageInfo, error) {
|
||||
// Extract package info using tar
|
||||
output, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkgInfo := &PackageInfo{
|
||||
Name: "",
|
||||
Description: "",
|
||||
Version: "",
|
||||
Revision: 1,
|
||||
Url: "",
|
||||
License: "",
|
||||
Arch: "",
|
||||
Type: "",
|
||||
Keep: make([]string, 0),
|
||||
Depends: make([]string, 0),
|
||||
MakeDepends: make([]string, 0),
|
||||
OptionalDepends: make([]string, 0),
|
||||
Conflicts: make([]string, 0),
|
||||
Replaces: make([]string, 0),
|
||||
Provides: make([]string, 0),
|
||||
SplitPackages: make([]*PackageInfo, 0),
|
||||
}
|
||||
|
||||
// Unmarshal yaml
|
||||
err = yaml.Unmarshal(output, pkgInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pkgInfo, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user