yay/vcs.go

255 lines
5 KiB
Go
Raw Normal View History

package main
2017-05-01 01:23:03 +00:00
2017-05-01 01:34:40 +00:00
import (
"bytes"
2017-05-01 01:34:40 +00:00
"encoding/json"
2018-03-22 18:23:20 +00:00
"fmt"
"os"
2017-05-01 01:34:40 +00:00
"strings"
2018-08-02 22:30:48 +00:00
"sync"
"time"
2018-03-22 18:23:20 +00:00
gosrc "github.com/Morganamilo/go-srcinfo"
"github.com/Jguer/yay/v9/pkg/stringset"
2017-05-01 01:34:40 +00:00
)
2017-05-01 01:23:03 +00:00
// Info contains the last commit sha of a repo
type vcsInfo map[string]shaInfos
type shaInfos map[string]shaInfo
type shaInfo struct {
Protocols []string `json:"protocols"`
2018-06-04 19:36:10 +00:00
Branch string `json:"branch"`
SHA string `json:"sha"`
}
2017-10-14 16:11:47 +00:00
// createDevelDB forces yay to create a DB of the existing development packages
func createDevelDB() error {
2018-08-02 22:30:48 +00:00
var mux sync.Mutex
var wg sync.WaitGroup
2018-03-22 18:23:20 +00:00
_, _, _, remoteNames, err := filterPackages()
if err != nil {
return err
}
info, err := aurInfoPrint(remoteNames)
2018-03-22 18:23:20 +00:00
if err != nil {
return err
}
2018-03-25 21:31:20 +00:00
bases := getBases(info)
toSkip := pkgbuildsToSkip(bases, stringset.FromSlice(remoteNames))
_, err = downloadPkgbuilds(bases, toSkip, config.BuildDir)
if err != nil {
return err
}
srcinfos, err := parseSrcinfoFiles(bases, false)
if err != nil {
return err
}
2018-03-22 18:23:20 +00:00
for i := range srcinfos {
for iP := range srcinfos[i].Packages {
2018-08-02 22:30:48 +00:00
wg.Add(1)
go updateVCSData(srcinfos[i].Packages[iP].Pkgname, srcinfos[i].Source, &mux, &wg)
2018-03-22 18:23:20 +00:00
}
}
2018-08-02 22:30:48 +00:00
wg.Wait()
fmt.Println(bold(yellow(arrow) + bold(" GenDB finished. No packages were installed")))
return err
2017-05-01 01:23:03 +00:00
}
// parseSource returns the git url, default branch and protocols it supports
func parseSource(source string) (url, branch string, protocols []string) {
split := strings.Split(source, "::")
source = split[len(split)-1]
split = strings.SplitN(source, "://", 2)
2017-05-01 01:34:40 +00:00
if len(split) != 2 {
2018-03-05 23:41:54 +00:00
return "", "", nil
}
protocols = strings.SplitN(split[0], "+", 2)
git := false
for _, protocol := range protocols {
if protocol == "git" {
git = true
break
}
}
protocols = protocols[len(protocols)-1:]
if !git {
return "", "", nil
}
split = strings.SplitN(split[1], "#", 2)
if len(split) == 2 {
secondSplit := strings.SplitN(split[1], "=", 2)
if secondSplit[0] != "branch" {
// source has #commit= or #tag= which makes them not vcs
// packages because they reference a specific point
2018-03-05 23:41:54 +00:00
return "", "", nil
}
if len(secondSplit) == 2 {
url = split[0]
branch = secondSplit[1]
}
} else {
url = split[0]
branch = "HEAD"
}
url = strings.Split(url, "?")[0]
branch = strings.Split(branch, "?")[0]
return url, branch, protocols
}
func updateVCSData(pkgName string, sources []gosrc.ArchString, mux sync.Locker, wg *sync.WaitGroup) {
2018-08-02 22:30:48 +00:00
defer wg.Done()
if savedInfo == nil {
2018-08-02 22:30:48 +00:00
mux.Lock()
savedInfo = make(vcsInfo)
2018-08-02 22:30:48 +00:00
mux.Unlock()
2017-05-02 10:50:11 +00:00
}
info := make(shaInfos)
2018-08-02 22:30:48 +00:00
checkSource := func(source gosrc.ArchString) {
defer wg.Done()
url, branch, protocols := parseSource(source.Value)
if url == "" || branch == "" {
2018-08-02 22:30:48 +00:00
return
2017-05-02 10:50:11 +00:00
}
commit := getCommit(url, branch, protocols)
if commit == "" {
2018-08-02 22:30:48 +00:00
return
}
2018-08-09 16:01:29 +00:00
mux.Lock()
info[url] = shaInfo{
protocols,
branch,
commit,
}
savedInfo[pkgName] = info
fmt.Println(bold(yellow(arrow)) + " Found git repo: " + cyan(url))
err := saveVCSInfo()
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
2018-08-02 22:30:48 +00:00
mux.Unlock()
}
for _, source := range sources {
wg.Add(1)
go checkSource(source)
}
}
func getCommit(url, branch string, protocols []string) string {
if len(protocols) > 0 {
protocol := protocols[len(protocols)-1]
var outbuf bytes.Buffer
cmd := passToGit("", "ls-remote", protocol+"://"+url, branch)
cmd.Stdout = &outbuf
cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0")
err := cmd.Start()
if err != nil {
return ""
}
2017-05-01 01:34:40 +00:00
// for some reason
// git://bitbucket.org/volumesoffun/polyvox.git` hangs on my
// machine but using http:// instead of git does not hang.
// Introduce a time out so this can not hang
timer := time.AfterFunc(5*time.Second, func() {
err = cmd.Process.Kill()
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
})
2017-05-01 01:34:40 +00:00
err = cmd.Wait()
timer.Stop()
if err != nil {
return ""
}
stdout := outbuf.String()
split := strings.Fields(stdout)
if len(split) < 2 {
return ""
}
commit := split[0]
return commit
}
return ""
}
func (infos shaInfos) needsUpdate() bool {
// used to signal we have gone through all sources and found nothing
finished := make(chan struct{})
alive := 0
// if we find an update we use this to exit early and return true
hasUpdate := make(chan struct{})
checkHash := func(url string, info shaInfo) {
2018-06-04 19:12:26 +00:00
hash := getCommit(url, info.Branch, info.Protocols)
if hash != "" && hash != info.SHA {
hasUpdate <- struct{}{}
} else {
finished <- struct{}{}
}
}
for url, info := range infos {
alive++
go checkHash(url, info)
}
for {
select {
case <-hasUpdate:
return true
case <-finished:
alive--
if alive == 0 {
return false
}
}
}
}
func saveVCSInfo() error {
2017-08-04 09:26:53 +00:00
marshalledinfo, err := json.MarshalIndent(savedInfo, "", "\t")
2017-05-09 13:44:34 +00:00
if err != nil || string(marshalledinfo) == "null" {
return err
}
in, err := os.OpenFile(vcsFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
2017-05-02 10:50:11 +00:00
if err != nil {
return err
}
defer in.Close()
_, err = in.Write(marshalledinfo)
if err != nil {
return err
}
err = in.Sync()
return err
}