浏览代码

Merge pull request #206 from Morganamilo/lsremote

 Use git ls-remote to track devel updates
Morgana 7 年之前
父节点
当前提交
251d31c970
共有 7 个文件被更改,包括 188 次插入169 次删除
  1. 9 6
      clean.go
  2. 3 7
      cmd.go
  3. 12 2
      config.go
  4. 9 16
      install.go
  5. 4 4
      upgrade.go
  6. 100 118
      vcs.go
  7. 51 16
      vcs_test.go

+ 9 - 6
clean.go

@@ -4,16 +4,19 @@ package main
 
 // RemovePackage removes package from VCS information
 func removeVCSPackage(pkgs []string) {
+	updated := false
+
 	for _, pkgName := range pkgs {
-		for i, e := range savedInfo {
-			if e.Package == pkgName {
-				savedInfo[i] = savedInfo[len(savedInfo)-1]
-				savedInfo = savedInfo[:len(savedInfo)-1]
-			}
+		_, ok := savedInfo[pkgName]
+		if ok {
+			delete(savedInfo, pkgName)
+			updated = true
 		}
 	}
 
-	_ = saveVCSInfo()
+	if updated {
+		saveVCSInfo()
+	}
 }
 
 // CleanDependencies removes all dangling dependencies in system

+ 3 - 7
cmd.go

@@ -90,9 +90,9 @@ func initYay() (err error) {
 		cacheHome = os.Getenv("HOME") + "/.cache/yay"
 	}
 
-	configFile = configHome + "/config.json"
-	vcsFile = configHome + "/yay_vcs.json"
-	completionFile = cacheHome + "/aur_"
+	configFile = configHome + "/" + configFileName
+	vcsFile = cacheHome + "/" + vcsFileName
+	completionFile = cacheHome + "/" + completionFilePrefix
 
 	////////////////
 	// yay config //
@@ -420,10 +420,6 @@ func handleYay() (err error) {
 		if err != nil {
 			return
 		}
-		err = saveVCSInfo()
-		if err != nil {
-			return
-		}
 	} else if cmdArgs.existsArg("c", "clean") {
 		err = cleanDependencies()
 	} else if len(cmdArgs.targets) > 0 {

+ 12 - 2
config.go

@@ -43,10 +43,20 @@ type Configuration struct {
 
 var version = "3.373"
 
+// configFileName holds the name of the config file.
+const configFileName string = "config.json"
+
+// vcsFileName holds the name of the vcs file.
+const vcsFileName string = "vcs.json"
+
+// completionFilePrefix holds the prefix used for storing shell completion files.
+const completionFilePrefix string = "aur_"
+
 // baseURL givers the AUR default address.
 const baseURL string = "https://aur.archlinux.org"
 
-var savedInfo infos
+// savedInfo holds the current vcs info
+var savedInfo vcsInfo
 
 // configfile holds yay config file path.
 var configFile string
@@ -54,7 +64,7 @@ var configFile string
 // vcsfile holds yay vcs info file path.
 var vcsFile string
 
-//completion file
+// completion file
 var completionFile string
 
 // changedConfig holds whether or not the config has changed

+ 9 - 16
install.go

@@ -339,20 +339,6 @@ func askEditPkgBuilds(pkgs []*rpc.Pkg, bases map[string][]*rpc.Pkg) error {
 	return nil
 }
 
-func updateVSCdb(pkgs []*rpc.Pkg, pkgbuild *gopkg.PKGBUILD) {
-	for _, pkgsource := range pkgbuild.Source {
-		owner, repo := parseSource(pkgsource)
-		if owner != "" && repo != "" {
-			for _, pkg := range pkgs {
-				err := branchInfo(pkg.Name, owner, repo)
-				if err != nil {
-					fmt.Println(err)
-				}
-			}
-		}
-	}
-}
-
 func parsesrcinfosFile(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD, bases map[string][]*rpc.Pkg) error {
 	for k, pkg := range pkgs {
 		dir := config.BuildDir + pkg.PackageBase + "/"
@@ -366,7 +352,11 @@ func parsesrcinfosFile(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD, bas
 		}
 
 		srcinfos[pkg.PackageBase] = pkgbuild
-		updateVSCdb(bases[pkg.PackageBase], pkgbuild)
+
+		for _, pkg := range bases[pkg.PackageBase] {
+			updateVCSData(pkg.Name, pkgbuild.Source)
+		}
+
 	}
 
 	return nil
@@ -493,7 +483,10 @@ func buildInstallPkgBuilds(pkgs []*rpc.Pkg, srcinfos map[string]*gopkg.PKGBUILD,
 			return err
 		}
 
-		updateVSCdb(bases[pkg.PackageBase], srcinfo)
+		for _, pkg := range bases[pkg.PackageBase] {
+			updateVCSData(pkg.Name, srcinfo.Source)
+		}
+
 		if len(depArguments.targets) > 0 {
 			_, stderr, err := passToPacmanCapture(depArguments)
 			if err != nil {

+ 4 - 4
upgrade.go

@@ -144,12 +144,12 @@ loop:
 }
 
 func upDevel(remote []alpm.Package, packageC chan upgrade, done chan bool) {
-	for _, e := range savedInfo {
+	for vcsName, e := range savedInfo {
 		if e.needsUpdate() {
 			found := false
 			var pkg alpm.Package
 			for _, r := range remote {
-				if r.Name() == e.Package {
+				if r.Name() == vcsName {
 					found = true
 					pkg = r
 				}
@@ -159,10 +159,10 @@ func upDevel(remote []alpm.Package, packageC chan upgrade, done chan bool) {
 					fmt.Print(magenta("Warning: "))
 					fmt.Printf("%s ignoring package upgrade (%s => %s)\n", cyan(pkg.Name()), pkg.Version(), "git")
 				} else {
-					packageC <- upgrade{e.Package, "devel", pkg.Version(), "commit-" + e.SHA[0:6]}
+					packageC <- upgrade{pkg.Name(), "devel", pkg.Version(), "latest-commit"}
 				}
 			} else {
-				removeVCSPackage([]string{e.Package})
+				removeVCSPackage([]string{vcsName})
 			}
 		}
 	}

+ 100 - 118
vcs.go

@@ -1,38 +1,21 @@
 package main
 
 import (
+	"bytes"
 	"encoding/json"
-	"fmt"
-	"io/ioutil"
-	"net/http"
 	"os"
+	"os/exec"
 	"strings"
+	"time"
 )
 
-// branch contains the information of a repository branch
-type branch struct {
-	Name   string `json:"name"`
-	Commit struct {
-		SHA string `json:"sha"`
-	} `json:"commit"`
-}
-
-type branches []branch
-
 // Info contains the last commit sha of a repo
-type Info struct {
-	Package string `json:"pkgname"`
-	URL     string `json:"url"`
-	SHA     string `json:"sha"`
-}
-
-type infos []Info
-
-// Repo contains information about the repository
-type repo struct {
-	Name          string `json:"name"`
-	FullName      string `json:"full_name"`
-	DefaultBranch string `json:"default_branch"`
+type vcsInfo map[string]shaInfos
+type shaInfos map[string]shaInfo
+type shaInfo struct {
+	Protocols []string `json:"protocols"`
+	Brach     string   `json:"branch"`
+	SHA       string   `json:"sha"`
 }
 
 // createDevelDB forces yay to create a DB of the existing development packages
@@ -50,128 +33,127 @@ func createDevelDB() error {
 	return err
 }
 
-// parseSource returns owner and repo from source
-func parseSource(source string) (owner string, repo string) {
+// parseSource returns the git url, default branch and protocols it supports
+func parseSource(source string) (url string, branch string, protocols []string) {
 	if !(strings.Contains(source, "git://") ||
 		strings.Contains(source, ".git") ||
 		strings.Contains(source, "git+https://")) {
-		return
+		return "", "", nil
 	}
-	split := strings.Split(source, "github.com/")
-	if len(split) > 1 {
-		secondSplit := strings.Split(split[1], "/")
-		if len(secondSplit) > 1 {
-			owner = secondSplit[0]
-			thirdSplit := strings.Split(secondSplit[1], ".git")
-			if len(thirdSplit) > 0 {
-				repo = thirdSplit[0]
-			}
-		}
-	}
-	return
-}
+	split := strings.Split(source, "::")
+	source = split[len(split)-1]
+	split = strings.SplitN(source, "://", 2)
 
-func (info *Info) needsUpdate() bool {
-	var newRepo repo
-	var newBranches branches
-	if strings.HasSuffix(info.URL, "/branches") {
-		info.URL = info.URL[:len(info.URL)-9]
-	}
-	infoResp, infoErr := http.Get(info.URL)
-	if infoErr != nil {
-		fmt.Println(infoErr)
-		return false
+	if len(split) != 2 {
+		return "", "", nil
 	}
-	defer infoResp.Body.Close()
 
-	infoBody, _ := ioutil.ReadAll(infoResp.Body)
-	var err = json.Unmarshal(infoBody, &newRepo)
-	if err != nil {
-		fmt.Printf("Cannot update '%v'\nError: %v\nStatus code: %v\nBody: %v\n",
-			info.Package, err, infoResp.StatusCode, string(infoBody))
-		return false
+	protocols = strings.Split(split[0], "+")
+	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
+			return "", "", nil
+		}
+
+		if len(secondSplit) == 2 {
+			url = split[0]
+			branch = secondSplit[1]
+		}
+	} else {
+		url = split[0]
+		branch = "HEAD"
 	}
 
-	defaultBranch := newRepo.DefaultBranch
-	branchesURL := info.URL + "/branches"
+	return
+}
 
-	branchResp, branchErr := http.Get(branchesURL)
-	if branchErr != nil {
-		fmt.Println(branchErr)
-		return false
+func updateVCSData(pkgName string, sources []string) {
+	if savedInfo == nil {
+		savedInfo = make(vcsInfo)
 	}
-	defer branchResp.Body.Close()
 
-	branchBody, _ := ioutil.ReadAll(branchResp.Body)
-	err = json.Unmarshal(branchBody, &newBranches)
-	if err != nil {
-		fmt.Printf("Cannot update '%v'\nError: %v\nStatus code: %v\nBody: %v\n",
-			info.Package, err, branchResp.StatusCode, string(branchBody))
-		return false
-	}
+	info := make(shaInfos)
 
-	for _, e := range newBranches {
-		if e.Name == defaultBranch {
-			return e.Commit.SHA != info.SHA
+	for _, source := range sources {
+		url, branch, protocols := parseSource(source)
+		if url == "" || branch == "" {
+			continue
+		}
+
+		commit := getCommit(url, branch, protocols)
+		if commit == "" {
+			continue
 		}
-	}
-	return false
-}
 
-func inStore(pkgName string) *Info {
-	for i, e := range savedInfo {
-		if pkgName == e.Package {
-			return &savedInfo[i]
+		info[url] = shaInfo{
+			protocols,
+			branch,
+			commit,
 		}
+
+		savedInfo[pkgName] = info
+		saveVCSInfo()
 	}
-	return nil
 }
 
-// branchInfo updates saved information
-func branchInfo(pkgName string, owner string, repoName string) (err error) {
-	updated := false
-	var newRepo repo
-	var newBranches branches
-	url := "https://api.github.com/repos/" + owner + "/" + repoName
-	repoResp, err := http.Get(url)
-	if err != nil {
-		return
-	}
-	defer repoResp.Body.Close()
+func getCommit(url string, branch string, protocols []string) string {
+	for _, protocol := range protocols {
+		var outbuf bytes.Buffer
 
-	_ = json.NewDecoder(repoResp.Body).Decode(&newRepo)
-	defaultBranch := newRepo.DefaultBranch
-	branchesURL := url + "/branches"
+		cmd := exec.Command("git", "ls-remote", protocol+"://"+url, branch)
+		cmd.Stdout = &outbuf
 
-	branchResp, err := http.Get(branchesURL)
-	if err != nil {
-		return
-	}
-	defer branchResp.Body.Close()
+		err := cmd.Start()
+		if err != nil {
+			continue
+		}
 
-	_ = json.NewDecoder(branchResp.Body).Decode(&newBranches)
+		//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() {
+			cmd.Process.Kill()
+		})
 
-	packinfo := inStore(pkgName)
+		err = cmd.Wait()
+		timer.Stop()
 
-	for _, e := range newBranches {
-		if e.Name == defaultBranch {
-			updated = true
+		if err != nil {
+			continue
+		}
+		err = cmd.Run()
 
-			if packinfo != nil {
-				packinfo.Package = pkgName
-				packinfo.URL = url
-				packinfo.SHA = e.Commit.SHA
-			} else {
-				savedInfo = append(savedInfo, Info{Package: pkgName, URL: url, SHA: e.Commit.SHA})
-			}
+		stdout := outbuf.String()
+		split := strings.Fields(stdout)
+
+		if len(split) < 2 {
+			continue
 		}
+
+		commit := split[0]
+		return commit
 	}
 
-	if updated {
-		saveVCSInfo()
+	return ""
+}
+
+func (infos shaInfos) needsUpdate() bool {
+	for url, info := range infos {
+		hash := getCommit(url, info.Brach, info.Protocols)
+		if hash != info.SHA {
+			return true
+		}
 	}
 
-	return
+	return false
+}
+
+func inStore(pkgName string) shaInfos {
+	return savedInfo[pkgName]
 }
 
 func saveVCSInfo() error {

+ 51 - 16
vcs_test.go

@@ -4,29 +4,64 @@ import (
 	"testing"
 )
 
+func isEqual(a, b []string) bool {
+
+	if a == nil && b == nil {
+		return true
+	}
+
+	if a == nil || b == nil {
+		return false
+	}
+
+	if len(a) != len(b) {
+		return false
+	}
+
+	for i := range a {
+		if a[i] != b[i] {
+			return false
+		}
+	}
+
+	return true
+}
+
 func TestParsing(t *testing.T) {
 	type source struct {
-		sourceurl string
-		owner     string
-		repo      string
+		URL       string
+		Branch    string
+		Protocols []string
 	}
 
-	neovim := source{sourceurl: "git+https://github.com/neovim/neovim.git"}
-	neovim.owner, neovim.repo = parseSource(neovim.sourceurl)
-
-	if neovim.owner != "neovim" || neovim.repo != "neovim" {
-		t.Fatalf("Expected to find neovim/neovim, found %+v/%+v", neovim.owner, neovim.repo)
+	urls := []string{
+		"git+https://github.com/neovim/neovim.git",
+		"git://github.com/jguer/yay.git#branch=master",
+		"git://github.com/davidgiven/ack",
+		"git://github.com/jguer/yay.git#tag=v3.440",
+		"git://github.com/jguer/yay.git#commit=e5470c88c6e2f9e0f97deb4728659ffa70ef5d0c",
+		"a+b+c+d+e+f://github.com/jguer/yay.git#branch=foo",
 	}
 
-	yay := source{sourceurl: "git://github.com/jguer/yay.git#branch=master"}
-	yay.owner, yay.repo = parseSource(yay.sourceurl)
-	if yay.owner != "jguer" || yay.repo != "yay" {
-		t.Fatalf("Expected to find jguer/yay, found %+v/%+v", yay.owner, yay.repo)
+	sources := []source{
+		{"github.com/neovim/neovim.git", "HEAD", []string{"git", "https"}},
+		{"github.com/jguer/yay.git", "master", []string{"git"}},
+		{"github.com/davidgiven/ack", "HEAD", []string{"git"}},
+		{"", "", nil},
+		{"", "", nil},
+		{"github.com/jguer/yay.git", "foo", []string{"a", "b", "c", "d", "e", "f"}},
 	}
 
-	ack := source{sourceurl: "git://github.com/davidgiven/ack"}
-	ack.owner, ack.repo = parseSource(ack.sourceurl)
-	if ack.owner != "davidgiven" || ack.repo != "ack" {
-		t.Fatalf("Expected to find davidgiven/ack, found %+v/%+v", ack.owner, ack.repo)
+	for n, url := range urls {
+		url, branch, protocols := parseSource(url)
+		compare := sources[n]
+
+		if url != compare.URL ||
+			branch != compare.Branch ||
+			!isEqual(protocols, compare.Protocols) {
+
+			t.Fatalf("Expected url=%+v branch=%+v protocols=%+v\ngot url=%+v branch=%+v protocols=%+v", url, branch, protocols, compare.URL, compare.Branch, compare.Protocols)
+		}
 	}
+
 }