blob: 1b8703dfde730a29e1a81e89ed17674252f3bd45 [file] [log] [blame]
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package worker
import (
"context"
"fmt"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
"golang.org/x/vuln/srv/internal"
"golang.org/x/vuln/srv/internal/derrors"
)
type Issue struct {
Title string
Body string
Labels []string
}
type IssueClient interface {
// Destination describes where issues will be created.
Destination() string
// Reference returns a string that refers to the issue with number.
Reference(number int) string
// IssueExists reports whether an issue with the given ID exists.
IssueExists(ctx context.Context, number int) (bool, error)
// CreateIssue creates a new issue.
CreateIssue(ctx context.Context, iss *Issue) (number int, err error)
}
// ParseGithubRepo parses a string of the form owner/repo.
func ParseGithubRepo(s string) (owner, repoName string, err error) {
var found bool
owner, repoName, found = internal.Cut(s, "/")
if !found {
return "", "", fmt.Errorf("%q is not in the form owner/repo", s)
}
return owner, repoName, nil
}
type githubIssueClient struct {
client *github.Client
owner string
repo string
}
// NewGithubIssueClient creates an IssueClient that will create issues in
// the a GitHub repo.
// A GitHub access token is required to create issues.
func NewGithubIssueClient(owner, repo, accessToken string) *githubIssueClient {
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: accessToken})
tc := oauth2.NewClient(context.Background(), ts)
return &githubIssueClient{
client: github.NewClient(tc),
owner: owner,
repo: repo,
}
}
// Destination implements IssueClient.Destination.
func (c *githubIssueClient) Destination() string {
return fmt.Sprintf("https://github.com/%s/%s", c.owner, c.repo)
}
// Reference implements IssueClient.Reference.
func (c *githubIssueClient) Reference(num int) string {
return fmt.Sprintf("%s/issues/%d", c.Destination(), num)
}
// IssueExists implements IssueClient.IssueExists.
func (c *githubIssueClient) IssueExists(ctx context.Context, number int) (_ bool, err error) {
defer derrors.Wrap(&err, "IssueExists(%d)", number)
iss, _, err := c.client.Issues.Get(ctx, c.owner, c.repo, number)
if err != nil {
return false, err
}
if iss != nil {
fmt.Printf("ID = %d, Number = %d\n", iss.GetID(), iss.GetNumber())
return true, nil
}
return false, nil
}
// CreateIssue implements IssueClient.CreateIssue.
func (c *githubIssueClient) CreateIssue(ctx context.Context, iss *Issue) (number int, err error) {
defer derrors.Wrap(&err, "CreateIssue(%s)", iss.Title)
req := &github.IssueRequest{
Title: &iss.Title,
Body: &iss.Body,
Labels: &iss.Labels,
}
giss, _, err := c.client.Issues.Create(ctx, c.owner, c.repo, req)
if err != nil {
return 0, err
}
return giss.GetNumber(), nil
}