Files
Alok Kumar Mishra 5554d959db go ver bump and fixes (#92)
* go ver bump and fixes

* grep closed the pipe when rz-pm list still writing to stdout, and hence writing on broken pipe would ofc throw sigpipe (https://go.dev/src/os/signal/doc.go#:~:text=91-,%23%20SIGPIPE,-92%C2%A0%C2%A0)

moreover, info jsdec just does the job
2026-04-03 21:38:50 +08:00

226 lines
5.7 KiB
Go

package pkg
import (
"errors"
"fmt"
"log"
"math"
"os"
"path/filepath"
"strings"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/storer"
"gopkg.in/yaml.v2"
)
type Database struct {
Path string
}
var ErrRizinPackageWrongHash = errors.New("wrong hash")
const dbPath string = "db"
func InitDatabase(path string, rizinVersion string, updateDB bool) (Database, error) {
d := Database{path}
if updateDB {
err := d.updateDatabase(rizinVersion)
if err != nil {
return Database{}, fmt.Errorf("could not download the rz-pm database")
}
}
return d, nil
}
func getBranchName(s string) plumbing.ReferenceName {
return plumbing.ReferenceName("refs/remotes/origin/v" + s)
}
func remoteBranches(s storer.ReferenceStorer) (storer.ReferenceIter, error) {
refs, err := s.IterReferences()
if err != nil {
return nil, err
}
return storer.NewReferenceFilteredIter(func(ref *plumbing.Reference) bool {
return ref.Name().IsRemote()
}, refs), nil
}
func (d Database) switchTag(repo *git.Repository, w *git.Worktree, rizinVersion string) (string, error) {
branches, err := remoteBranches(repo.Storer)
if err != nil {
return "", err
}
versionPieces := strings.SplitN(rizinVersion, ".", 3)
var switchBranch string
var switchHash plumbing.Hash
var switchBranchPrio int = math.MaxInt
_ = branches.ForEach(func(b *plumbing.Reference) error {
pieces := strings.Split(b.Name().String(), "/")
branchName := pieces[len(pieces)-1]
if branchName == "v"+rizinVersion && switchBranchPrio > 0 {
switchBranch = branchName
switchBranchPrio = 0
switchHash = b.Hash()
} else if branchName == "v"+versionPieces[0]+"."+versionPieces[1] && switchBranchPrio > 1 {
switchBranch = branchName
switchBranchPrio = 1
switchHash = b.Hash()
} else if branchName == "v"+versionPieces[0] && switchBranchPrio > 2 {
switchBranch = branchName
switchBranchPrio = 2
switchHash = b.Hash()
}
return nil
})
if switchBranchPrio == math.MaxInt {
return "", fmt.Errorf("could not find a tag for version %s", rizinVersion)
}
localBranchName := plumbing.ReferenceName("refs/heads/" + switchBranch)
create := false
if _, err := repo.Storer.Reference(localBranchName); err != nil {
ref := plumbing.NewHashReference(localBranchName, switchHash)
err = repo.Storer.SetReference(ref)
if err != nil {
return "", err
}
create = true
}
err = w.Checkout(&git.CheckoutOptions{Branch: localBranchName, Create: create})
if err != nil {
return "", err
}
return switchBranch, nil
}
func (d Database) updateDatabase(rizinVersion string) error {
repo, err := git.PlainOpen(d.Path)
if err == git.ErrRepositoryNotExists {
log.Printf("Downloading rz-pm-db repository...\n")
repo, err = git.PlainClone(d.Path, false, &git.CloneOptions{
URL: RZPM_DB_REPO_URL,
})
}
if err != nil {
return err
}
w, err := repo.Worktree()
if err != nil {
return err
}
log.Printf("Updating rz-pm-db repository...\n")
err = w.Pull(&git.PullOptions{RemoteName: "origin"})
//below branch selction logic should also be used for a sucessfull pull, for a non-default branch
if err != nil && err != git.NoErrAlreadyUpToDate {
return err
}
h, err := repo.Head()
if err != nil {
return err
}
branchName := h.Name().String()
branchNamePieces := strings.Split(branchName, "/")
branchName = strings.TrimLeft(branchNamePieces[len(branchNamePieces)-1], "v")
if !strings.HasPrefix(rizinVersion, branchName) {
tagName, err := d.switchTag(repo, w, rizinVersion)
if err != nil {
log.Printf("Failed to switch rz-pm-db to version %s, default to main branch", rizinVersion)
err = w.Checkout(&git.CheckoutOptions{Branch: "refs/heads/master"})
if err != nil {
return err
}
} else {
log.Printf("Switched rz-pm-db to %s...\n", tagName)
}
}
return nil
}
func ParsePackageFile(path string) (Package, error) {
content, err := os.ReadFile(path)
if err != nil {
return RizinPackage{}, err
}
var p RizinPackage
err = yaml.Unmarshal(content, &p)
if err != nil {
return RizinPackage{}, err
}
if p.PackageName == "" || p.PackageVersion == "" || p.PackageSummary == "" {
return RizinPackage{}, fmt.Errorf("wrong file plugin format: name, version, and summary are mandatory")
}
//as every installable package needs a source.
if p.PackageSource == nil {
return RizinPackage{}, fmt.Errorf("wrong file plugin format: source is mandatory")
}
if p.PackageSource.URL == "" || p.PackageSource.BuildSystem == "" {
return RizinPackage{}, fmt.Errorf("wrong file plugin format: Source URL and Build System are mandatory")
}
if !p.isGitRepo() && p.PackageSource.Hash == "" {
return RizinPackage{}, fmt.Errorf("wrong file plugin format: Source Hash is mandatory for non-git plugins")
} else if p.isGitRepo() && p.PackageSource.Hash != "" {
return RizinPackage{}, fmt.Errorf("wrong file plugin format: Source Hash should not be used for git plugins")
}
return p, nil
}
func (d Database) ListAvailablePackages() ([]Package, error) {
dbPath := filepath.Join(d.Path, dbPath)
files, err := os.ReadDir(dbPath)
if err != nil {
return nil, err
}
packages := []Package{}
for _, file := range files {
// skip directories
if file.IsDir() {
continue
}
name := filepath.Join(dbPath, file.Name())
p, err := ParsePackageFile(name)
if err != nil {
fmt.Printf("Warning: could not read %s: %v\n", name, err)
continue
}
packages = append(packages, p)
}
return packages, nil
}
func (d Database) GetPackage(name string) (Package, error) {
packages, err := d.ListAvailablePackages()
if err != nil {
return RizinPackage{}, err
}
for _, pkg := range packages {
if pkg.Name() == name {
return pkg, nil
}
}
return RizinPackage{}, fmt.Errorf("package '%s' not found", name)
}