diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-12-25 12:07:06 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-12-27 09:09:18 +0100 |
| commit | 6f03c356200becfa347b8abade66ac74f52c10c9 (patch) | |
| tree | b06738f1401e8c9694529f9252a7c355ef0589d4 /pkg/git/git.go | |
| parent | 73aba437a774237b1130837b856f3b40b3ec3bf0 (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/git/git.go')
| -rw-r--r-- | pkg/git/git.go | 91 |
1 files changed, 91 insertions, 0 deletions
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, |
