diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2019-02-14 10:35:03 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2019-02-17 15:08:45 +0100 |
| commit | 3e98cc30803fb5e41504dd08b1325cb074a8a3f2 (patch) | |
| tree | 5f0a7a4702bedbb9706c158e09e4be1875894625 /pkg/vcs | |
| parent | f42dee6d5e501a061cdbb807672361369bf28492 (diff) | |
dashboard/app: poll commits info
This implements 2 features:
- syz-ci polls a set of additional repos to discover fixing commits sooner
(e.g. it can now discover a fixing commit in netfilter tree before
it reaches any of the tested trees).
- syz-ci uploads info about commits to dashboard.
For example, a user marks a bug as fixed by commit "foo: bar".
syz-ci will find this commit in the main namespace repo
and upload commmit hash/date/author to dashboard. This in turn
allows to show links to fixing commits.
Fixes #691
Fixes #610
Diffstat (limited to 'pkg/vcs')
| -rw-r--r-- | pkg/vcs/akaros.go | 12 | ||||
| -rw-r--r-- | pkg/vcs/fuchsia.go | 10 | ||||
| -rw-r--r-- | pkg/vcs/git.go | 145 | ||||
| -rw-r--r-- | pkg/vcs/git_repo_test.go | 180 | ||||
| -rw-r--r-- | pkg/vcs/git_test.go | 64 | ||||
| -rw-r--r-- | pkg/vcs/netbsd.go | 28 | ||||
| -rw-r--r-- | pkg/vcs/openbsd.go | 28 | ||||
| -rw-r--r-- | pkg/vcs/vcs.go | 81 | ||||
| -rw-r--r-- | pkg/vcs/vcs_test.go | 70 |
9 files changed, 439 insertions, 179 deletions
diff --git a/pkg/vcs/akaros.go b/pkg/vcs/akaros.go index aaf9d985e..4f476542a 100644 --- a/pkg/vcs/akaros.go +++ b/pkg/vcs/akaros.go @@ -10,7 +10,7 @@ import ( ) type akaros struct { - git *git + *git dropbear *git } @@ -40,15 +40,7 @@ func (ctx *akaros) SwitchCommit(commit string) (*Commit, error) { return nil, fmt.Errorf("not implemented for akaros") } -func (ctx *akaros) HeadCommit() (*Commit, error) { - return nil, fmt.Errorf("not implemented for akaros") -} - -func (ctx *akaros) ListRecentCommits(baseCommit string) ([]string, error) { - return ctx.git.ListRecentCommits(baseCommit) -} - -func (ctx *akaros) ExtractFixTagsFromCommits(baseCommit, email string) ([]FixCommit, error) { +func (ctx *akaros) ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error) { return ctx.git.ExtractFixTagsFromCommits(baseCommit, email) } diff --git a/pkg/vcs/fuchsia.go b/pkg/vcs/fuchsia.go index f9a027f67..04d8e5d36 100644 --- a/pkg/vcs/fuchsia.go +++ b/pkg/vcs/fuchsia.go @@ -75,11 +75,19 @@ func (ctx *fuchsia) HeadCommit() (*Commit, error) { return nil, fmt.Errorf("not implemented for fuchsia") } +func (ctx *fuchsia) GetCommitByTitle(title string) (*Commit, error) { + return ctx.zircon.GetCommitByTitle(title) +} + +func (ctx *fuchsia) GetCommitsByTitles(titles []string) ([]*Commit, []string, error) { + return ctx.zircon.GetCommitsByTitles(titles) +} + func (ctx *fuchsia) ListRecentCommits(baseCommit string) ([]string, error) { return ctx.zircon.ListRecentCommits(baseCommit) } -func (ctx *fuchsia) ExtractFixTagsFromCommits(baseCommit, email string) ([]FixCommit, error) { +func (ctx *fuchsia) ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error) { return ctx.zircon.ExtractFixTagsFromCommits(baseCommit, email) } diff --git a/pkg/vcs/git.go b/pkg/vcs/git.go index 45a7e340c..4f23bb16c 100644 --- a/pkg/vcs/git.go +++ b/pkg/vcs/git.go @@ -148,10 +148,10 @@ func (git *git) getCommit(commit string) (*Commit, error) { if err != nil { return nil, err } - return gitParseCommit(output) + return gitParseCommit(output, nil, nil) } -func gitParseCommit(output []byte) (*Commit, error) { +func gitParseCommit(output, user, domain []byte) (*Commit, error) { lines := bytes.Split(output, []byte{'\n'}) if len(lines) < 4 || len(lines[0]) != 40 { return nil, fmt.Errorf("unexpected git log output: %q", output) @@ -163,7 +163,29 @@ func gitParseCommit(output []byte) (*Commit, error) { } cc := make(map[string]bool) cc[strings.ToLower(string(lines[2]))] = true + var tags []string for _, line := range lines[4:] { + if user != nil { + userPos := bytes.Index(line, user) + if userPos != -1 { + domainPos := bytes.Index(line[userPos+len(user)+1:], domain) + if domainPos != -1 { + startPos := userPos + len(user) + endPos := userPos + len(user) + domainPos + 1 + tag := string(line[startPos:endPos]) + present := false + for _, tag1 := range tags { + if tag1 == tag { + present = true + break + } + } + if !present { + tags = append(tags, tag) + } + } + } + } for _, re := range ccRes { matches := re.FindSubmatchIndex(line) if matches == nil { @@ -187,26 +209,82 @@ func gitParseCommit(output []byte) (*Commit, error) { Title: string(lines[1]), Author: string(lines[2]), CC: sortedCC, + Tags: tags, Date: date, } return com, nil } +func (git *git) GetCommitByTitle(title string) (*Commit, error) { + commits, _, err := git.GetCommitsByTitles([]string{title}) + if err != nil || len(commits) == 0 { + return nil, err + } + return commits[0], nil +} + +func (git *git) GetCommitsByTitles(titles []string) ([]*Commit, []string, error) { + var greps []string + m := make(map[string]string) + for _, title := range titles { + canonical := CanonicalizeCommit(title) + greps = append(greps, canonical) + m[canonical] = title + } + since := time.Now().Add(-time.Hour * 24 * 365 * 2).Format("01-02-2006") + commits, err := git.fetchCommits(since, "HEAD", "", "", greps, true) + if err != nil { + return nil, nil, err + } + var results []*Commit + for _, com := range commits { + canonical := CanonicalizeCommit(com.Title) + if orig := m[canonical]; orig != "" { + delete(m, canonical) + results = append(results, com) + com.Title = orig + } + } + var missing []string + for _, orig := range m { + missing = append(missing, orig) + } + return results, missing, nil +} + func (git *git) ListRecentCommits(baseCommit string) ([]string, error) { // On upstream kernel this produces ~11MB of output. // Somewhat inefficient to collect whole output in a slice // and then convert to string, but should be bearable. output, err := runSandboxed(git.dir, "git", "log", - "--pretty=format:%s", "--no-merges", "-n", "200000", baseCommit) + "--pretty=format:%s", "-n", "200000", baseCommit) if err != nil { return nil, err } return strings.Split(string(output), "\n"), nil } -func (git *git) ExtractFixTagsFromCommits(baseCommit, email string) ([]FixCommit, error) { +func (git *git) ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error) { + user, domain, err := splitEmail(email) + if err != nil { + return nil, fmt.Errorf("failed to parse email %q: %v", email, err) + } + grep := user + "+.*" + domain since := time.Now().Add(-time.Hour * 24 * 365).Format("01-02-2006") - cmd := exec.Command("git", "log", "--no-merges", "--since", since, baseCommit) + return git.fetchCommits(since, baseCommit, user, domain, []string{grep}, false) +} + +func (git *git) fetchCommits(since, base, user, domain string, greps []string, fixedStrings bool) ([]*Commit, error) { + const commitSeparator = "---===syzkaller-commit-separator===---" + args := []string{"log", "--since", since, "--format=%H%n%s%n%ae%n%ad%n%b%n" + commitSeparator} + if fixedStrings { + args = append(args, "--fixed-strings") + } + for _, grep := range greps { + args = append(args, "--grep", grep) + } + args = append(args, base) + cmd := exec.Command("git", args...) cmd.Dir = git.dir stdout, err := cmd.StdoutPipe() if err != nil { @@ -217,52 +295,33 @@ func (git *git) ExtractFixTagsFromCommits(baseCommit, email string) ([]FixCommit } defer cmd.Wait() defer cmd.Process.Kill() - return gitExtractFixTags(stdout, email) -} - -func gitExtractFixTags(r io.Reader, email string) ([]FixCommit, error) { - user, domain, err := splitEmail(email) - if err != nil { - return nil, fmt.Errorf("failed to parse email %q: %v", email, err) - } var ( - s = bufio.NewScanner(r) - commits []FixCommit - commitTitle = "" - commitStart = []byte("commit ") - bodyPrefix = []byte(" ") - userBytes = []byte(user + "+") - domainBytes = []byte(domain) + s = bufio.NewScanner(stdout) + buf = new(bytes.Buffer) + separator = []byte(commitSeparator) + commits []*Commit + userBytes []byte + domainBytes []byte ) + if user != "" { + userBytes = []byte(user + "+") + domainBytes = []byte(domain) + } for s.Scan() { ln := s.Bytes() - if bytes.HasPrefix(ln, commitStart) { - commitTitle = "" + if !bytes.Equal(ln, separator) { + buf.Write(ln) + buf.WriteByte('\n') 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 + com, err := gitParseCommit(buf.Bytes(), userBytes, domainBytes) + if err != nil { + return nil, err } - domainPos := bytes.Index(ln[userPos+len(userBytes)+1:], domainBytes) - if domainPos == -1 { - continue + if user == "" || len(com.Tags) != 0 { + commits = append(commits, com) } - startPos := userPos + len(userBytes) - endPos := userPos + len(userBytes) + domainPos + 1 - tag := string(ln[startPos:endPos]) - commits = append(commits, FixCommit{tag, commitTitle}) + buf.Reset() } return commits, s.Err() } diff --git a/pkg/vcs/git_repo_test.go b/pkg/vcs/git_repo_test.go index 848e369c9..f60bfc737 100644 --- a/pkg/vcs/git_repo_test.go +++ b/pkg/vcs/git_repo_test.go @@ -20,7 +20,13 @@ func init() { os.Setenv("SYZ_DISABLE_SANDBOXING", "yes") } +const ( + userEmail = `test@syzkaller.com` + extractFixTagsEmail = `"syzbot" <syzbot@my.mail.com>` +) + func TestGitRepo(t *testing.T) { + t.Parallel() baseDir, err := ioutil.TempDir("", "syz-git-test") if err != nil { t.Fatal(err) @@ -108,6 +114,170 @@ func TestGitRepo(t *testing.T) { } } +func TestMetadata(t *testing.T) { + t.Parallel() + repoDir, err := ioutil.TempDir("", "syz-git-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(repoDir) + repo := makeTestRepo(t, repoDir) + for i, test := range metadataTests { + repo.commitChange(test.description) + com, err := repo.repo.HeadCommit() + if err != nil { + t.Fatal(err) + } + checkCommit(t, i, test, com, false) + } + commits, err := repo.repo.ExtractFixTagsFromCommits("HEAD", extractFixTagsEmail) + if err != nil { + t.Fatal(err) + } + if len(metadataTests) != len(commits) { + t.Fatalf("want %v commits, got %v", len(metadataTests), len(commits)) + } + for i, test := range metadataTests { + checkCommit(t, i, test, commits[len(commits)-i-1], true) + for _, title := range []string{test.title, test.title2} { + if title == "" { + continue + } + com, err := repo.repo.GetCommitByTitle(title) + if err != nil { + t.Error(err) + } else if com == nil { + t.Errorf("no commits found by title %q", title) + } else if com.Title != title { + t.Errorf("wrong commit %q found by title %q", com.Title, title) + } + } + } +} + +func checkCommit(t *testing.T, idx int, test testCommit, com *Commit, checkTags bool) { + if !checkTags { + return + } + if test.title != com.Title { + t.Errorf("#%v: want title %q, got %q", idx, test.title, com.Title) + } + if test.author != com.Author { + t.Errorf("#%v: want author %q, got %q", idx, test.author, com.Author) + } + if diff := cmp.Diff(test.cc, com.CC); diff != "" { + t.Logf("%#v", com.CC) + t.Error(diff) + } + if diff := cmp.Diff(test.tags, com.Tags); checkTags && diff != "" { + t.Error(diff) + } +} + +type testCommit struct { + description string + title string + title2 string + author string + cc []string + tags []string +} + +// nolint: lll +var metadataTests = []testCommit{ + { + description: `dashboard/app: bump max repros per bug to 10 + +Reported-by: syzbot+8e4090902540da8c6e8f@my.mail.com +`, + title: "dashboard/app: bump max repros per bug to 10", + author: userEmail, + cc: []string{userEmail}, + tags: []string{"8e4090902540da8c6e8f"}, + }, + { + description: `executor: remove dead code + +Reported-by: syzbot+8e4090902540da8c6e8f@my.mail.com +Reported-by: syzbot <syzbot+a640a0fc325c29c3efcb@my.mail.com> +`, + title: "executor: remove dead code", + author: userEmail, + cc: []string{userEmail}, + tags: []string{"8e4090902540da8c6e8f", "a640a0fc325c29c3efcb"}, + }, + { + description: `pkg/csource: fix string escaping bug + +Reported-and-tested-by: syzbot+8e4090902540da8c6e8fa640a0fc325c29c3efcb@my.mail.com +Tested-by: syzbot+4234987263748623784623758235@my.mail.com +`, + title: "pkg/csource: fix string escaping bug", + author: userEmail, + cc: []string{"syzbot+4234987263748623784623758235@my.mail.com", "syzbot+8e4090902540da8c6e8fa640a0fc325c29c3efcb@my.mail.com", userEmail}, + tags: []string{"8e4090902540da8c6e8fa640a0fc325c29c3efcb", "4234987263748623784623758235"}, + }, + { + description: `When freeing a lockf struct that already is part of a linked list, make sure to update the next pointer for the preceding lock. Prevents a double free panic. + +ok millert@ +Reported-by: syzbot+6dd701dc797b23b8c761@my.mail.com +`, + title: "When freeing a lockf struct that already is part of a linked list, make sure to update the next pointer for the preceding lock. Prevents a double free panic.", + author: userEmail, + cc: []string{userEmail}, + tags: []string{"6dd701dc797b23b8c761"}, + }, + { + description: `ipmr: properly check rhltable_init() return value + +commit 8fb472c09b9d ("ipmr: improve hash scalability") +added a call to rhltable_init() without checking its return value. + +This problem was then later copied to IPv6 and factorized in commit +0bbbf0e7d0e7 ("ipmr, ip6mr: Unite creation of new mr_table") + +Fixes: 8fb472c09b9d ("ipmr: improve hash scalability") +Fixes: 0bbbf0e7d0e7 ("ipmr, ip6mr: Unite creation of new mr_table") +Reported-by: syzbot+6dd701dc797b23b8c761@my.mail.com +`, + title: "ipmr: properly check rhltable_init() return value", + title2: "net-backports: ipmr: properly check rhltable_init() return value", + author: userEmail, + cc: []string{userEmail}, + tags: []string{"6dd701dc797b23b8c761"}, + }, + { + description: `f2fs: sanity check for total valid node blocks + +Reported-by: syzbot+bf9253040425feb155ad@my.mail.com +Reported-by: syzbot+bf9253040425feb155ad@my.mail.com +`, + title: "f2fs: sanity check for total valid node blocks", + author: userEmail, + cc: []string{userEmail}, + tags: []string{"bf9253040425feb155ad"}, + }, + { + description: `USB: fix usbmon BUG trigger + +Automated tests triggered this by opening usbmon and accessing the +mmap while simultaneously resizing the buffers. This bug was with +us since 2006, because typically applications only size the buffers +once and thus avoid racing. Reported by Kirill A. Shutemov. + +Reported-by: <syzbot+f9831b881b3e849829fc@my.mail.com> +Signed-off-by: Pete Zaitcev <zaitcev@redhat.com> +Cc: stable <stable@vger.kernel.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +`, + title: "USB: fix usbmon BUG trigger", + author: userEmail, + cc: []string{userEmail}, + tags: []string{"f9831b881b3e849829fc"}, + }, +} + func createTestRepo(t *testing.T, baseDir, name string) *testRepo { repo := makeTestRepo(t, filepath.Join(baseDir, name)) repo.git("checkout", "-b", "master") @@ -127,6 +297,7 @@ type testRepo struct { dir string name string commits map[string]map[string]*Commit + repo *git } func makeTestRepo(t *testing.T, dir string) *testRepo { @@ -138,8 +309,11 @@ func makeTestRepo(t *testing.T, dir string) *testRepo { dir: dir, name: filepath.Base(dir), commits: make(map[string]map[string]*Commit), + repo: newGit(dir), } repo.git("init") + repo.git("config", "--add", "user.email", userEmail) + repo.git("config", "--add", "user.name", "Test Syzkaller") return repo } @@ -160,9 +334,13 @@ func (repo *testRepo) commitFileChange(branch, change string) { if repo.commits[branch] == nil { repo.commits[branch] = make(map[string]*Commit) } - com, err := newGit(repo.dir).HeadCommit() + com, err := repo.repo.HeadCommit() if err != nil { repo.t.Fatal(err) } repo.commits[branch][change] = com } + +func (repo *testRepo) commitChange(description string) { + repo.git("commit", "--allow-empty", "-m", description) +} diff --git a/pkg/vcs/git_test.go b/pkg/vcs/git_test.go index 5c036e14c..a22835a69 100644 --- a/pkg/vcs/git_test.go +++ b/pkg/vcs/git_test.go @@ -5,11 +5,8 @@ package vcs import ( "reflect" - "strings" "testing" "time" - - "github.com/google/go-cmp/cmp" ) func TestGitParseCommit(t *testing.T) { @@ -48,7 +45,7 @@ Signed-off-by: Linux Master <linux@linux-foundation.org> }, } for input, com := range tests { - res, err := gitParseCommit([]byte(input)) + res, err := gitParseCommit([]byte(input), nil, nil) if err != nil && com != nil { t.Fatalf("want %+v, got error: %v", com, err) } @@ -120,62 +117,3 @@ v1. t.Fatalf("got bad tags\ngot: %+v\nwant: %+v", got, want) } } - -func TestGitExtractFixTags(t *testing.T) { - commits, err := gitExtractFixTags(strings.NewReader(extractFixTagsInput), extractFixTagsEmail) - if err != nil { - t.Fatal(err) - } - if diff := cmp.Diff(extractFixTagsOutput, commits); diff != "" { - t.Fatal(diff) - } -} - -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"}, - {"4234987263748623784623758235", "pkg/csource: fix string escaping bug"}, - {"6dd701dc797b23b8c761", "When freeing a lockf struct that already is part of a linked list, make sure to"}, -} - -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 - Tested-by: syzbot+4234987263748623784623758235@my.mail.com - -commit 47546510aa98d3fbff3291a5dc3cefe712e70394 -Author: anton <openbsd@openbsd.org> -Date: Sat Oct 6 21:12:23 2018 +0000 - - When freeing a lockf struct that already is part of a linked list, make sure to - update the next pointer for the preceding lock. Prevents a double free panic. - - ok millert@ - Reported-by: syzbot+6dd701dc797b23b8c761@my.mail.com -` diff --git a/pkg/vcs/netbsd.go b/pkg/vcs/netbsd.go index c210e4878..b43523756 100644 --- a/pkg/vcs/netbsd.go +++ b/pkg/vcs/netbsd.go @@ -9,7 +9,7 @@ import ( ) type netbsd struct { - git *git + *git } func newNetBSD(vm, dir string) *netbsd { @@ -18,31 +18,7 @@ func newNetBSD(vm, dir string) *netbsd { } } -func (ctx *netbsd) Poll(repo, branch string) (*Commit, error) { - return ctx.git.Poll(repo, branch) -} - -func (ctx *netbsd) CheckoutBranch(repo, branch string) (*Commit, error) { - return nil, fmt.Errorf("not implemented for netbsd") -} - -func (ctx *netbsd) CheckoutCommit(repo, commit string) (*Commit, error) { - return nil, fmt.Errorf("not implemented for netbsd") -} - -func (ctx *netbsd) SwitchCommit(commit string) (*Commit, error) { - return nil, fmt.Errorf("not implemented for netbsd") -} - -func (ctx *netbsd) HeadCommit() (*Commit, error) { - return nil, fmt.Errorf("not implemented for netbsd") -} - -func (ctx *netbsd) ListRecentCommits(baseCommit string) ([]string, error) { - return ctx.git.ListRecentCommits(baseCommit) -} - -func (ctx *netbsd) ExtractFixTagsFromCommits(baseCommit, email string) ([]FixCommit, error) { +func (ctx *netbsd) ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error) { return ctx.git.ExtractFixTagsFromCommits(baseCommit, email) } diff --git a/pkg/vcs/openbsd.go b/pkg/vcs/openbsd.go index fdcc648f7..3998ce58a 100644 --- a/pkg/vcs/openbsd.go +++ b/pkg/vcs/openbsd.go @@ -9,7 +9,7 @@ import ( ) type openbsd struct { - git *git + *git } func newOpenBSD(vm, dir string) *openbsd { @@ -18,31 +18,7 @@ func newOpenBSD(vm, dir string) *openbsd { } } -func (ctx *openbsd) Poll(repo, branch string) (*Commit, error) { - return ctx.git.Poll(repo, branch) -} - -func (ctx *openbsd) CheckoutBranch(repo, branch string) (*Commit, error) { - return nil, fmt.Errorf("not implemented for openbsd") -} - -func (ctx *openbsd) CheckoutCommit(repo, commit string) (*Commit, error) { - return nil, fmt.Errorf("not implemented for openbsd") -} - -func (ctx *openbsd) SwitchCommit(commit string) (*Commit, error) { - return nil, fmt.Errorf("not implemented for openbsd") -} - -func (ctx *openbsd) HeadCommit() (*Commit, error) { - return nil, fmt.Errorf("not implemented for openbsd") -} - -func (ctx *openbsd) ListRecentCommits(baseCommit string) ([]string, error) { - return ctx.git.ListRecentCommits(baseCommit) -} - -func (ctx *openbsd) ExtractFixTagsFromCommits(baseCommit, email string) ([]FixCommit, error) { +func (ctx *openbsd) ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error) { return ctx.git.ExtractFixTagsFromCommits(baseCommit, email) } diff --git a/pkg/vcs/vcs.go b/pkg/vcs/vcs.go index b315a83ba..def15153c 100644 --- a/pkg/vcs/vcs.go +++ b/pkg/vcs/vcs.go @@ -33,13 +33,22 @@ type Repo interface { // HeadCommit returns info about the HEAD commit of the current branch of git repository. HeadCommit() (*Commit, error) + // GetCommitByTitle finds commit info by the title. + // Remote is not fetched, the commit needs to be reachable from the current repo state + // (e.g. do CheckoutBranch before). If the commit is not found, nil is returned. + GetCommitByTitle(title string) (*Commit, error) + + // GetCommitsByTitles is a batch version of GetCommitByTitle. + // Returns list of commits and titles of commits that are not found. + GetCommitsByTitles(titles []string) ([]*Commit, []string, error) + // ListRecentCommits returns list of recent commit titles starting from baseCommit. ListRecentCommits(baseCommit string) ([]string, error) // 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}. - ExtractFixTagsFromCommits(baseCommit, email string) ([]FixCommit, error) + // and returns commits with these tags. + ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error) // PreviousReleaseTags returns list of preceding release tags that are reachable from the given commit. PreviousReleaseTags(commit string) ([]string, error) @@ -57,14 +66,10 @@ type Commit struct { Title string Author string CC []string + Tags []string Date time.Time } -type FixCommit struct { - Tag string - Title string -} - type BisectResult int const ( @@ -153,7 +158,7 @@ func runSandboxed(dir, command string, args ...string) ([]byte, error) { var ( // nolint: lll - gitRepoRe = regexp.MustCompile(`^(git|ssh|http|https|ftp|ftps)://[a-zA-Z0-9-_]+(\.[a-zA-Z0-9-_]+)+(:[0-9]+)?/[a-zA-Z0-9-_./]+\.git(/)?$`) + gitRepoRe = regexp.MustCompile(`^(git|ssh|http|https|ftp|ftps)://[a-zA-Z0-9-_]+(\.[a-zA-Z0-9-_]+)+(:[0-9]+)?(/[a-zA-Z0-9-_./]+)?(/)?$`) gitBranchRe = regexp.MustCompile("^[a-zA-Z0-9-_/.]{2,200}$") gitHashRe = regexp.MustCompile("^[a-f0-9]{8,40}$") releaseTagRe = regexp.MustCompile(`^v([0-9]+).([0-9]+)(?:\.([0-9]+))?$`) @@ -189,3 +194,63 @@ var commitPrefixes = []string{ "FROMGIT:", "net-backports:", } + +const SyzkallerRepo = "https://github.com/google/syzkaller" + +func CommitLink(url, hash string) string { + return link(url, hash, 0) +} + +func TreeLink(url, hash string) string { + return link(url, hash, 1) +} + +func LogLink(url, hash string) string { + return link(url, hash, 2) +} + +func link(url, hash string, typ int) string { + if url == "" || hash == "" { + return "" + } + switch url { + case "https://fuchsia.googlesource.com": + // We collect hashes from zircon repo. + return link(url+"/zircon", hash, typ) + } + if strings.HasPrefix(url, "https://github.com/") { + url = strings.TrimSuffix(url, ".git") + switch typ { + case 1: + return url + "/tree/" + hash + case 2: + return url + "/commits/" + hash + default: + return url + "/commit/" + hash + } + } + if strings.HasPrefix(url, "https://git.kernel.org/pub/scm/") || + strings.HasPrefix(url, "git://git.kernel.org/pub/scm/") { + url = strings.TrimPrefix(url, "git") + url = strings.TrimPrefix(url, "https") + switch typ { + case 1: + return "https" + url + "/tree/?id=" + hash + case 2: + return "https" + url + "/log/?id=" + hash + default: + return "https" + url + "/commit/?id=" + hash + } + } + if strings.HasPrefix(url, "https://") && strings.Contains(url, ".googlesource.com") { + switch typ { + case 1: + return url + "/+/" + hash + "/" + case 2: + return url + "/+log/" + hash + default: + return url + "/+/" + hash + "^!" + } + } + return "" +} diff --git a/pkg/vcs/vcs_test.go b/pkg/vcs/vcs_test.go index 9e90090a4..457430989 100644 --- a/pkg/vcs/vcs_test.go +++ b/pkg/vcs/vcs_test.go @@ -31,12 +31,13 @@ func TestCheckRepoAddress(t *testing.T) { "https://anonscm.debian.org/git/kernel/linux.git": true, "git://kernel.ubuntu.com/ubuntu/ubuntu-zesty.git": true, "http://host.xz:123/path/to/repo.git/": true, + "https://chromium.googlesource.com/chromiumos/third_party/kernel": true, + "https://fuchsia.googlesource.com": true, "": false, "foobar": false, "linux-next": false, "foo://kernel.ubuntu.com/ubuntu/ubuntu-zesty.git": false, "git://kernel/ubuntu.git": false, - "git://kernel.com/ubuntu": false, "gitgit://kernel.ubuntu.com/ubuntu/ubuntu-zesty.git": false, }) } @@ -83,3 +84,70 @@ func testPredicate(t *testing.T, fn func(string) bool, tests map[string]bool) { } } } + +func TestCommitLink(t *testing.T) { + type Test struct { + URL string + Hash string + CommitLink string + } + tests := []Test{ + { + "https://github.com/google/syzkaller", + "76dd003f1b102b791d8b342a1f92a6486ff56a1e", + "https://github.com/google/syzkaller/commit/76dd003f1b102b791d8b342a1f92a6486ff56a1e", + }, + { + "https://github.com/google/syzkaller.git", + "76dd003f1b", + "https://github.com/google/syzkaller/commit/76dd003f1b", + }, + { + "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git", + "8fe28cb58bcb", + "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8fe28cb58bcb", + }, + { + "git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git", + "8fe28cb58b", + "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8fe28cb58b", + }, + + { + "https://android.googlesource.com/kernel/common", + "d0c3914ffbe4c00f0a131bae83f811d5606699bc", + "https://android.googlesource.com/kernel/common/+/d0c3914ffbe4c00f0a131bae83f811d5606699bc^!", + }, + { + "https://gvisor.googlesource.com/gvisor", + "5301cbf8430e5436211bc142c0886d8c11cc71ab", + "https://gvisor.googlesource.com/gvisor/+/5301cbf8430e5436211bc142c0886d8c11cc71ab^!", + }, + { + "https://fuchsia.googlesource.com", + "13ee3dc5e4c46bf127977ad28645c47442ec517d", + "https://fuchsia.googlesource.com/zircon/+/13ee3dc5e4c46bf127977ad28645c47442ec517d^!", + }, + { + "git://git.cmpxchg.org/linux-mmots.git", + "8fe28cb58b", + "", + }, + { + "", + "8fe28cb58b", + "", + }, + { + "https://android.googlesource.com/kernel/common", + "", + "", + }, + } + for _, test := range tests { + link := CommitLink(test.URL, test.Hash) + if link != test.CommitLink { + t.Errorf("URL: %v\nhash: %v\nwant: %v\ngot: %v", test.URL, test.Hash, test.CommitLink, link) + } + } +} |
