aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-12-25 12:07:06 +0100
committerDmitry Vyukov <dvyukov@google.com>2017-12-27 09:09:18 +0100
commit6f03c356200becfa347b8abade66ac74f52c10c9 (patch)
treeb06738f1401e8c9694529f9252a7c355ef0589d4 /pkg
parent73aba437a774237b1130837b856f3b40b3ec3bf0 (diff)
dashboard/app: extract fixing tags from commits
Support the new scheme of associating fixing commits with bugs. Now we provide a tag along the lines of: Reported-by: <syzbot+a4a91f6fc35e102@syzkaller.appspotmail.com> The tag is supposed to be added to the commit. Then we parse commit logs and extract these tags. The final part on the dashboard is not ready yet, but syz-ci should already parse and send the tags.
Diffstat (limited to 'pkg')
-rw-r--r--pkg/email/parser.go14
-rw-r--r--pkg/email/parser_test.go2
-rw-r--r--pkg/git/git.go91
-rw-r--r--pkg/git/git_test.go48
4 files changed, 149 insertions, 6 deletions
diff --git a/pkg/email/parser.go b/pkg/email/parser.go
index c59736a1b..e1787ea9d 100644
--- a/pkg/email/parser.go
+++ b/pkg/email/parser.go
@@ -34,7 +34,7 @@ const commandPrefix = "#syz "
var groupsLinkRe = regexp.MustCompile("\nTo view this discussion on the web visit (https://groups\\.google\\.com/.*?)\\.(?:\r)?\n")
-func Parse(r io.Reader, ownEmail string) (*Email, error) {
+func Parse(r io.Reader, ownEmails []string) (*Email, error) {
msg, err := mail.ReadMessage(r)
if err != nil {
return nil, fmt.Errorf("failed to read email: %v", err)
@@ -52,13 +52,17 @@ func Parse(r io.Reader, ownEmail string) (*Email, error) {
cc, _ := msg.Header.AddressList("Cc")
bugID := ""
var ccList []string
- if addr, err := mail.ParseAddress(ownEmail); err == nil {
- ownEmail = addr.Address
+ ownAddrs := make(map[string]bool)
+ for _, email := range ownEmails {
+ ownAddrs[email] = true
+ if addr, err := mail.ParseAddress(email); err == nil {
+ ownAddrs[addr.Address] = true
+ }
}
fromMe := false
for _, addr := range from {
cleaned, _, _ := RemoveAddrContext(addr.Address)
- if addr, err := mail.ParseAddress(cleaned); err == nil && addr.Address == ownEmail {
+ if addr, err := mail.ParseAddress(cleaned); err == nil && ownAddrs[addr.Address] {
fromMe = true
}
}
@@ -67,7 +71,7 @@ func Parse(r io.Reader, ownEmail string) (*Email, error) {
if addr, err := mail.ParseAddress(cleaned); err == nil {
cleaned = addr.Address
}
- if cleaned == ownEmail {
+ if ownAddrs[cleaned] {
if bugID == "" {
bugID = context
}
diff --git a/pkg/email/parser_test.go b/pkg/email/parser_test.go
index 727dafc02..2341fc385 100644
--- a/pkg/email/parser_test.go
+++ b/pkg/email/parser_test.go
@@ -97,7 +97,7 @@ func TestCanonicalEmail(t *testing.T) {
func TestParse(t *testing.T) {
for i, test := range parseTests {
body := func(t *testing.T, test ParseTest) {
- email, err := Parse(strings.NewReader(test.email), "bot <foo@bar.com>")
+ email, err := Parse(strings.NewReader(test.email), []string{"bot <foo@bar.com>"})
if err != nil {
t.Fatal(err)
}
diff --git a/pkg/git/git.go b/pkg/git/git.go
index 133f6ef9b..9930b449a 100644
--- a/pkg/git/git.go
+++ b/pkg/git/git.go
@@ -5,9 +5,13 @@
package git
import (
+ "bufio"
"bytes"
"fmt"
+ "io"
+ "net/mail"
"os"
+ "os/exec"
"regexp"
"strings"
"time"
@@ -125,6 +129,93 @@ func ListRecentCommits(dir, baseCommit string) ([]string, error) {
return strings.Split(string(output), "\n"), nil
}
+type FixCommit struct {
+ Tag string
+ Title string
+}
+
+// ExtractFixTagsFromCommits extracts fixing tags for bugs from git log.
+// Given email = "user@domain.com", it searches for tags of the form "user+tag@domain.com"
+// and return pairs {tag, commit title}.
+func ExtractFixTagsFromCommits(dir, baseCommit, email string) ([]FixCommit, error) {
+ since := time.Now().Add(-time.Hour * 24 * 365).Format("01-02-2006")
+ cmd := exec.Command("git", "log", "--no-merges", "--since", since, baseCommit)
+ cmd.Dir = dir
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, err
+ }
+ if err := cmd.Start(); err != nil {
+ return nil, err
+ }
+ defer cmd.Wait()
+ return extractFixTags(stdout, email)
+}
+
+func extractFixTags(r io.Reader, email string) ([]FixCommit, error) {
+ user, domain, err := splitEmail(email)
+ if err != nil {
+ return nil, err
+ }
+ var (
+ s = bufio.NewScanner(r)
+ commits []FixCommit
+ commitTitle = ""
+ commitStart = []byte("commit ")
+ bodyPrefix = []byte(" ")
+ userBytes = []byte(user + "+")
+ domainBytes = []byte(domain)
+ )
+ for s.Scan() {
+ ln := s.Bytes()
+ if bytes.HasPrefix(ln, commitStart) {
+ commitTitle = ""
+ continue
+ }
+ if !bytes.HasPrefix(ln, bodyPrefix) {
+ continue
+ }
+ ln = ln[len(bodyPrefix):]
+ if len(ln) == 0 {
+ continue
+ }
+ if commitTitle == "" {
+ commitTitle = string(ln)
+ continue
+ }
+ userPos := bytes.Index(ln, userBytes)
+ if userPos == -1 {
+ continue
+ }
+ domainPos := bytes.Index(ln[userPos+len(userBytes)+1:], domainBytes)
+ if domainPos == -1 {
+ continue
+ }
+ startPos := userPos + len(userBytes)
+ endPos := userPos + len(userBytes) + domainPos + 1
+ tag := string(ln[startPos:endPos])
+ commits = append(commits, FixCommit{tag, commitTitle})
+ }
+ return commits, s.Err()
+}
+
+func splitEmail(email string) (user, domain string, err error) {
+ addr, err := mail.ParseAddress(email)
+ if err != nil {
+ return "", "", err
+ }
+ at := strings.IndexByte(addr.Address, '@')
+ if at == -1 {
+ return "", "", fmt.Errorf("no @ in email address")
+ }
+ user = addr.Address[:at]
+ domain = addr.Address[at:]
+ if plus := strings.IndexByte(user, '+'); plus != -1 {
+ user = user[:plus]
+ }
+ return
+}
+
// CanonicalizeCommit returns commit title that can be used when checking
// if a particular commit is present in a git tree.
// Some trees add prefixes to commit titles during backporting,
diff --git a/pkg/git/git_test.go b/pkg/git/git_test.go
index 3cc1300dd..8e23e84b2 100644
--- a/pkg/git/git_test.go
+++ b/pkg/git/git_test.go
@@ -4,6 +4,8 @@
package git
import (
+ "reflect"
+ "strings"
"testing"
)
@@ -72,3 +74,49 @@ func TestCheckBranch(t *testing.T) {
}
}
}
+
+func TestExtractFixTags(t *testing.T) {
+ commits, err := extractFixTags(strings.NewReader(extractFixTagsInput), extractFixTagsEmail)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(commits, extractFixTagsOutput) {
+ t.Fatalf("got : %+v\twant: %+v", commits, extractFixTagsOutput)
+ }
+}
+
+const extractFixTagsEmail = "\"syzbot\" <syzbot@my.mail.com>"
+
+var extractFixTagsOutput = []FixCommit{
+ {"8e4090902540da8c6e8f", "dashboard/app: bump max repros per bug to 10"},
+ {"8e4090902540da8c6e8f", "executor: remove dead code"},
+ {"a640a0fc325c29c3efcb", "executor: remove dead code"},
+ {"8e4090902540da8c6e8fa640a0fc325c29c3efcb", "pkg/csource: fix string escaping bug"},
+}
+
+var extractFixTagsInput = `
+commit 73aba437a774237b1130837b856f3b40b3ec3bf0 (HEAD -> master, origin/master)
+Author: me <foo@bar.com>
+Date: Fri Dec 22 19:59:56 2017 +0100
+
+ dashboard/app: bump max repros per bug to 10
+
+ Reported-by: syzbot+8e4090902540da8c6e8f@my.mail.com
+
+commit 26cd53f078db858a6ccca338e13e7f4d1d291c22
+Author: me <foo@bar.com>
+Date: Fri Dec 22 13:42:27 2017 +0100
+
+ executor: remove dead code
+
+ Reported-by: syzbot+8e4090902540da8c6e8f@my.mail.com
+ Reported-by: syzbot <syzbot+a640a0fc325c29c3efcb@my.mail.com>
+
+commit 7b62abdb0abadbaf7b3f3a23ab4d78485fbf9059
+Author: Dmitry Vyukov <dvyukov@google.com>
+Date: Fri Dec 22 11:59:09 2017 +0100
+
+ pkg/csource: fix string escaping bug
+
+ Reported-and-tested-by: syzbot+8e4090902540da8c6e8fa640a0fc325c29c3efcb@my.mail.com
+`