diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2026-01-29 09:21:15 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2026-01-30 11:03:40 +0000 |
| commit | 5f1dd856122a81d1cf6bcad71da663f42484f321 (patch) | |
| tree | c35188a6b0590bf0d0c1d229fa83cc8c96205069 /pkg | |
| parent | b31760143a55012873c68e5ff5b51cd9c376729f (diff) | |
pkg/gerrit: add package
Package gerrit provides gerrit client for:
https://linux.googlesource.com/Documentation/#gerrit-code-reviews-for-the-linux-kernel
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/gerrit/gerrit.go | 79 | ||||
| -rw-r--r-- | pkg/gerrit/repos.go | 61 | ||||
| -rw-r--r-- | pkg/gerrit/repos_test.go | 33 |
3 files changed, 173 insertions, 0 deletions
diff --git a/pkg/gerrit/gerrit.go b/pkg/gerrit/gerrit.go new file mode 100644 index 000000000..f9e6d2c5f --- /dev/null +++ b/pkg/gerrit/gerrit.go @@ -0,0 +1,79 @@ +// Copyright 2026 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +// Package gerrit provides gerrit client for: +// https://linux.googlesource.com/Documentation/#gerrit-code-reviews-for-the-linux-kernel +// For documentation on the API see: +// https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html +package gerrit + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" +) + +const host = "https://linux-review.googlesource.com" + +func CreateChange(ctx context.Context, repo, branch, baseCommit, description, diff string) ( + changeID int, link string, err error) { + project, err := projectForRepo(repo) + if err != nil { + return 0, "", err + } + req := map[string]any{ + "project": project, + "branch": branch, + "subject": description, + "base_commit": baseCommit, + "patch": map[string]any{ + "patch": diff, + }, + } + resp := new(struct { + Number int `json:"_number"` + }) + err = request(ctx, "changes/", req, resp) + link = fmt.Sprintf("%v/c/%v/+/%v", host, project, resp.Number) + return resp.Number, link, err +} + +func request(ctx context.Context, api string, req map[string]any, resp any) error { + ts, err := google.DefaultTokenSource(ctx, "https://www.googleapis.com/auth/gerritcodereview") + if err != nil { + return fmt.Errorf("gerrit: failed to get token source: %w", err) + } + reqData, err := json.Marshal(req) + if err != nil { + return fmt.Errorf("gerrit: failed to marshal CreateChange: %w", err) + } + endpoint := fmt.Sprintf("%v/a/%v", host, api) + httpReq, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewBuffer(reqData)) + if err != nil { + return fmt.Errorf("gerrit: failed to create request: %w", err) + } + httpReq.Header.Set("Content-Type", "application/json; charset=UTF-8") + httpResp, err := oauth2.NewClient(ctx, ts).Do(httpReq) + if err != nil { + return fmt.Errorf("gerrit: failed to call %v: %w", api, err) + } + defer httpResp.Body.Close() + body, err := io.ReadAll(httpResp.Body) + if err != nil || httpResp.StatusCode < 200 || httpResp.StatusCode > 299 { + return fmt.Errorf("gerrit: failed to call %v: %v %v err:%w: %s", + api, httpResp.StatusCode, http.StatusText(httpResp.StatusCode), err, body) + } + // Responses may start with ")]}'" for XSSI protection; trim it. + const xssiPrefix = ")]}'\n" + body = bytes.TrimPrefix(body, []byte(xssiPrefix)) + if err := json.Unmarshal(body, resp); err != nil { + return fmt.Errorf("gerrit: failed to unmarshal %v response: %w\n%s", api, err, body) + } + return nil +} diff --git a/pkg/gerrit/repos.go b/pkg/gerrit/repos.go new file mode 100644 index 000000000..b2c793111 --- /dev/null +++ b/pkg/gerrit/repos.go @@ -0,0 +1,61 @@ +// Copyright 2026 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package gerrit + +import ( + "fmt" +) + +func projectForRepo(repo string) (string, error) { + project := projects[repo] + if project == "" { + return "", fmt.Errorf("gerrit: repo %q is not supported", repo) + } + return project, nil +} + +var projects = func() map[string]string { + // There are few others non-kernel.org-based, but we don't support them here now. + kernelOrgRepos := []string{ + "bluetooth/bluetooth-next", + "bpf/bpf", + "bpf/bpf-next", + "brauner/linux", + "davem/net", + "davem/net-next", + "dhowells/keyutils", + "dhowells/linux-fs", + "dtor/input", + "gregkh/char-misc", + "gregkh/driver-core", + "gregkh/staging", + "gregkh/tty", + "gregkh/usb", + "herbert/crypto-2.6", + "hid/hid", + "jmorris/linux-security", + "klassert/ipsec", + "klassert/ipsec-next", + "mellanox/linux", + "paulmck/linux-rcu", + "tip/tip", + "tiwai/sound", + "tj/cgroup", + "tj/wq", + "torvalds/linux", + "viro/vfs", + "will/linux", + "zohar/linux-integrity", + } + res := map[string]string{} + for _, repo := range kernelOrgRepos { + project := "linux/kernel/git/" + repo + const kernelOrgPrefix = "git.kernel.org/pub/scm/linux/kernel/git" + const googleSrcPrefix = "kernel.googlesource.com/pub/scm/linux/kernel/git" + res[fmt.Sprintf("git://%v/%v.git", kernelOrgPrefix, repo)] = project + res[fmt.Sprintf("https://%v/%v.git", kernelOrgPrefix, repo)] = project + res[fmt.Sprintf("https://%v/%v.git", googleSrcPrefix, repo)] = project + } + return res +}() diff --git a/pkg/gerrit/repos_test.go b/pkg/gerrit/repos_test.go new file mode 100644 index 000000000..019396eae --- /dev/null +++ b/pkg/gerrit/repos_test.go @@ -0,0 +1,33 @@ +// Copyright 2026 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package gerrit + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestProjectForRepo(t *testing.T) { + { + got, err := projectForRepo("git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git") + require.NoError(t, err) + require.Equal(t, "linux/kernel/git/torvalds/linux", got) + } + { + got, err := projectForRepo("https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git") + require.NoError(t, err) + require.Equal(t, "linux/kernel/git/bpf/bpf-next", got) + } + { + got, err := projectForRepo("https://kernel.googlesource.com/pub/scm/linux/kernel/git/davem/net.git") + require.NoError(t, err) + require.Equal(t, "linux/kernel/git/davem/net", got) + } + { + // Valid repo, but we don't mirror it. + _, err := projectForRepo("git://git.kernel.org/pub/scm/linux/kernel/git/ast/bpf.git") + require.Error(t, err) + } +} |
