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

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: Lots of fixes 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..c0f96b3553144d991dae9645b08fef5dce517624
--- /dev/null
+++ b/go/src/infra/tools/drover/disambiguate.go
@@ -0,0 +1,167 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package main
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net/http"
+ "reflect"
+ "sort"
+ "strings"
+
+ "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, err := gitiles.JSON(map[string]map[string]string{}, "+refs")
+ if err != nil {
+ return
+ }
+ refs := *result.(*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
+ candidates := make(sortableRefCandidates, 0, 10)
+
+ 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(iannucci): 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