@@ -5,10 +5,11 @@ import (
"context"
"fmt"
"net/http"
"os"
"sort"
"strconv"
"strings"
"unicode"
"dmitri.shuralyov.com/service/change"
"github.com/andygrunwald/go-gerrit"
@@ -285,10 +286,41 @@ func (s service) ListTimeline(ctx context.Context, repo string, id uint64, opt *
Payload: change.MergedEvent{
CommitID: message.Message[46:86], // TODO: Make safer.
RefName: chg.Branch,
},
})
case "gerrit:newPatchSet":
// Parse a new patchset message, check if it has comments.
body, err := parsePSMessage(message.Message, message.RevisionNumber)
if err != nil {
return nil, err
}
if body == "" {
// No body means no comments.
break
}
var cs []change.InlineComment
for file, comments := range *comments {
for _, c := range comments {
if c.Updated.Equal(message.Date.Time) {
cs = append(cs, change.InlineComment{
File: file,
Line: c.Line,
Body: c.Message,
})
}
}
}
timeline = append(timeline, change.Review{
ID: fmt.Sprint(idx), // TODO: message.ID is not uint64; e.g., "bfba753d015916303152305cee7152ea7a112fe0".
User: s.gerritUser(message.Author),
CreatedAt: message.Date.Time,
State: change.Commented,
Body: body,
Editable: false,
Comments: cs,
})
}
continue
}
labels, body, ok := parseMessage(message.Message)
if !ok {
@@ -364,10 +396,57 @@ func parseMessage(m string) (labels string, body string, ok bool) {
}
return labels, body, true
}
// parsePSMessage parses an autogenerated:gerrit:newPatchSet
// message and returns its body, if any.
func parsePSMessage(m string, revisionNumber int) (body string, _ error) {
// "Uploaded patch set ".
if !strings.HasPrefix(m, "Uploaded patch set ") {
return "", fmt.Errorf("unexpected format")
}
m = m[len("Uploaded patch set "):]
// Revision number, e.g., "123".
i := matchNumber(m, revisionNumber)
if i == -1 {
return "", fmt.Errorf("unexpected format")
}
m = m[i:]
// ".".
if len(m) < 1 || m[0] != '.' {
return "", fmt.Errorf("unexpected format")
}
m = m[1:]
if m == "" {
// No body.
return "", nil
}
// "\n\n".
if !strings.HasPrefix(m, "\n\n") {
return "", fmt.Errorf("unexpected format")
}
m = m[len("\n\n"):]
// The remainer is the body.
return m, nil
}
// matchNumber returns the index after number in s,
// or -1 if number is not immediately present in s.
func matchNumber(s string, number int) int {
a := strconv.Itoa(number)
if !strings.HasPrefix(s, a) {
return -1
}
return len(a)
}
func reviewState(labels string) change.ReviewState {
for _, label := range strings.Split(labels, " ") {
switch label {
case "Code-Review+2":
return change.Approved