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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 package main
2
3 import "encoding/json"
4 import "errors"
5 import "fmt"
6 import "net/http"
7 import "reflect"
8 import "sort"
9 import "strings"
10
11 import "infra/libs/gitiles"
12
13 const crRevAPI = "https://cr-rev.appspot.com/_ah/api/crrev/v1/"
14
15 /* Takes a user-suppiled commit specifier, and resolves it to a commit hash and
16 repo url.
17
18 Possible input formats:
19 "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
20 "29345" # Cr-Commit-Position assuming refs/heads/master and src.git
21
22 The disambiguation is done by crrev.com, and so this function may return an
23 error if the service is not available.
24
25 Additionally, this function may return an error if the commit specifier is
26 not unambiguous.
27 */
28 func disambiguateCommit(commitSpec string) (commit, url string, err error) {
29 commit = commitSpec
30 url = ""
31
32 resp, err := http.Get(crRevAPI + "redirect/" + commitSpec)
33 if err != nil {
34 return
35 }
36 defer resp.Body.Close()
37
38 // TODO(iannucci,stip): generate this from endpoints source code
39 jsonData := make(map[string]string)
40 if err = json.NewDecoder(resp.Body).Decode(&jsonData); err != nil {
41 return
42 }
43 commit, ok := jsonData["git_sha"]
44 if !ok {
45 err = errors.New("disambiguateCommit: expected 'git_sha' key")
46 return
47 }
48
49 rawURL, ok := jsonData["redirect_url"]
50 if !ok {
51 err = errors.New("disambiguateCommit: expected 'redirect_url' ke y")
52 return
53 }
54
55 url = strings.Split(rawURL, "+")[0]
56 return
57 }
58
59 type sortKey struct {
60 inexactMatch bool
61 partialMatch bool
62 nonHead bool
63 refLength int
64 }
65
66 // Sorts refCandidate's so that the least one is the best one.
67 func (s *sortKey) less(other sortKey) bool {
68 return ((!s.inexactMatch && other.inexactMatch) ||
69 (!s.partialMatch && other.partialMatch) ||
70 (!s.nonHead && other.nonHead) ||
71 (s.refLength < other.refLength))
72 }
73
74 type refCandidate struct {
75 Ref string
76 Confident bool
77 SortKey sortKey
78 }
79
80 type sortableRefCandidates []refCandidate
81
82 func (r sortableRefCandidates) Len() int { return len(r) }
83 func (r sortableRefCandidates) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
84 func (r sortableRefCandidates) Less(i, j int) bool {
85 return r[i].SortKey.less(r[j].SortKey)
86 }
87
88 func disambiguateRef(gitiles *gitiles.Gitiles, commit, refFragment string) (ref string, err error) {
89 result := <-gitiles.JSON(map[string]map[string]string{}, "+refs")
90 if result.Err != nil {
91 err = result.Err
92 return
93 }
94 refs := *result.DataPtr.(*map[string]map[string]string)
95
96 reverse := func(s []string) (ret []string) {
97 ret = make([]string, len(s))
98 for idx, el := range s {
99 ret[len(ret)-idx-1] = el
100 }
101 return
102 }
103
104 targetParts := reverse(strings.Split(refFragment, "/"))
105
106 // guess that we'll have about 10
107 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.
108
109 for ref := range refs {
110 if strings.HasSuffix(ref, refFragment) {
111 refParts := reverse(strings.Split(ref, "/"))
112
113 exactMatch := reflect.DeepEqual(refParts, targetParts)
114 wholeMatch := true
115 shortest := -1
116 if len(refParts) < len(targetParts) {
117 shortest = len(refParts)
118 } else {
119 shortest = len(targetParts)
120 }
121 for i := 0; i < shortest; i++ {
122 if refParts[i] != targetParts[i] {
123 wholeMatch = false
124 break
125 }
126 }
127 hasHeads := strings.Contains(ref, "heads")
128
129 confident := exactMatch || (wholeMatch && hasHeads)
130
131 candidates = append(candidates, refCandidate{
132 Ref: ref,
133 Confident: confident,
134 SortKey: sortKey{
135 inexactMatch: !exactMatch,
136 partialMatch: !wholeMatch,
137 nonHead: !hasHeads,
138 refLength: len(ref),
139 },
140 })
141 }
142 }
143
144 if len(candidates) == 0 {
145 err = fmt.Errorf("disambiguateRef: no matching ref for %s", refF ragment)
146 return
147 }
148
149 sort.Sort(candidates)
150 // TODO(riannucci): if we have logging, it would be good to dump all
151 // candidates in debug mode.
152 if !candidates[0].Confident {
153 fmt.Printf("No confident matches for refs given '%s'. Candidates :\n",
154 refFragment)
155 for _, c := range candidates {
156 fmt.Printf(" %s\n", c.Ref)
157 }
158 err = errors.New("disambiguateRef: no confident refs")
159 }
160
161 ref = candidates[0].Ref
162 return
163 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698