Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(82)

Unified Diff: go/src/infra/tools/drover/disambiguate.go

Issue 662113003: Drover's back, baby! (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git/+/master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: go/src/infra/tools/drover/disambiguate.go
diff --git a/go/src/infra/tools/drover/disambiguate.go b/go/src/infra/tools/drover/disambiguate.go
new file mode 100644
index 0000000000000000000000000000000000000000..83fdd521490f6920362e69303bc4649fdc46a624
--- /dev/null
+++ b/go/src/infra/tools/drover/disambiguate.go
@@ -0,0 +1,163 @@
+package main
+
+import "encoding/json"
+import "errors"
+import "fmt"
+import "net/http"
+import "reflect"
+import "sort"
+import "strings"
+
+import "infra/libs/gitiles"
+
+const crRevAPI = "https://cr-rev.appspot.com/_ah/api/crrev/v1/"
+
+/* Takes a user-suppiled commit specifier, and resolves it to a commit hash and
+repo url.
+
+Possible input formats:
+ "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+ "29345" # Cr-Commit-Position assuming refs/heads/master and src.git
+
+The disambiguation is done by crrev.com, and so this function may return an
+error if the service is not available.
+
+Additionally, this function may return an error if the commit specifier is
+not unambiguous.
+*/
+func disambiguateCommit(commitSpec string) (commit, url string, err error) {
+ commit = commitSpec
+ url = ""
+
+ resp, err := http.Get(crRevAPI + "redirect/" + commitSpec)
+ if err != nil {
+ return
+ }
+ defer resp.Body.Close()
+
+ // TODO(iannucci,stip): generate this from endpoints source code
+ jsonData := make(map[string]string)
+ if err = json.NewDecoder(resp.Body).Decode(&jsonData); err != nil {
+ return
+ }
+ commit, ok := jsonData["git_sha"]
+ if !ok {
+ err = errors.New("disambiguateCommit: expected 'git_sha' key")
+ return
+ }
+
+ rawURL, ok := jsonData["redirect_url"]
+ if !ok {
+ err = errors.New("disambiguateCommit: expected 'redirect_url' key")
+ return
+ }
+
+ url = strings.Split(rawURL, "+")[0]
+ return
+}
+
+type sortKey struct {
+ inexactMatch bool
+ partialMatch bool
+ nonHead bool
+ refLength int
+}
+
+// Sorts refCandidate's so that the least one is the best one.
+func (s *sortKey) less(other sortKey) bool {
+ return ((!s.inexactMatch && other.inexactMatch) ||
+ (!s.partialMatch && other.partialMatch) ||
+ (!s.nonHead && other.nonHead) ||
+ (s.refLength < other.refLength))
+}
+
+type refCandidate struct {
+ Ref string
+ Confident bool
+ SortKey sortKey
+}
+
+type sortableRefCandidates []refCandidate
+
+func (r sortableRefCandidates) Len() int { return len(r) }
+func (r sortableRefCandidates) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
+func (r sortableRefCandidates) Less(i, j int) bool {
+ return r[i].SortKey.less(r[j].SortKey)
+}
+
+func disambiguateRef(gitiles *gitiles.Gitiles, commit, refFragment string) (ref string, err error) {
+ result := <-gitiles.JSON(map[string]map[string]string{}, "+refs")
+ if result.Err != nil {
+ err = result.Err
+ return
+ }
+ refs := *result.DataPtr.(*map[string]map[string]string)
+
+ reverse := func(s []string) (ret []string) {
+ ret = make([]string, len(s))
+ for idx, el := range s {
+ ret[len(ret)-idx-1] = el
+ }
+ return
+ }
+
+ targetParts := reverse(strings.Split(refFragment, "/"))
+
+ // guess that we'll have about 10
+ var candidates sortableRefCandidates = make([]refCandidate, 0, 10)
M-A Ruel 2014/10/18 00:47:06 candidates := make(sortableRefCandidates, 0, 10)
iannucci 2014/10/20 21:11:58 Der... dunno what I was thinking.
+
+ for ref := range refs {
+ if strings.HasSuffix(ref, refFragment) {
+ refParts := reverse(strings.Split(ref, "/"))
+
+ exactMatch := reflect.DeepEqual(refParts, targetParts)
+ wholeMatch := true
+ shortest := -1
+ if len(refParts) < len(targetParts) {
+ shortest = len(refParts)
+ } else {
+ shortest = len(targetParts)
+ }
+ for i := 0; i < shortest; i++ {
+ if refParts[i] != targetParts[i] {
+ wholeMatch = false
+ break
+ }
+ }
+ hasHeads := strings.Contains(ref, "heads")
+
+ confident := exactMatch || (wholeMatch && hasHeads)
+
+ candidates = append(candidates, refCandidate{
+ Ref: ref,
+ Confident: confident,
+ SortKey: sortKey{
+ inexactMatch: !exactMatch,
+ partialMatch: !wholeMatch,
+ nonHead: !hasHeads,
+ refLength: len(ref),
+ },
+ })
+ }
+ }
+
+ if len(candidates) == 0 {
+ err = fmt.Errorf("disambiguateRef: no matching ref for %s", refFragment)
+ return
+ }
+
+ sort.Sort(candidates)
+ // TODO(riannucci): if we have logging, it would be good to dump all
+ // candidates in debug mode.
+ if !candidates[0].Confident {
+ fmt.Printf("No confident matches for refs given '%s'. Candidates:\n",
+ refFragment)
+ for _, c := range candidates {
+ fmt.Printf(" %s\n", c.Ref)
+ }
+ err = errors.New("disambiguateRef: no confident refs")
+ }
+
+ ref = candidates[0].Ref
+ return
+}

Powered by Google App Engine
This is Rietveld 408576698