Make github issue a bit more useful (#7)
* include the text from the report and other useful info * include a link to the report details * Pick github repo based on submitted app name
This commit is contained in:
parent
112158fd47
commit
3b5b19cc90
4 changed files with 81 additions and 6 deletions
|
@ -40,7 +40,9 @@ The body of the request should be a JSON object with the following fields:
|
||||||
|
|
||||||
* `user_agent`: Application user-agent. Included in the `details.log.gz` file.
|
* `user_agent`: Application user-agent. Included in the `details.log.gz` file.
|
||||||
|
|
||||||
* `app`: Identifier for the application (eg 'riot-web').
|
* `app`: Identifier for the application (eg 'riot-web'). Should correspond to a
|
||||||
|
mapping configured in the configuration file for github issue reporting to
|
||||||
|
work.
|
||||||
|
|
||||||
* `version`: Application version. Included in the `details.log.gz` file.
|
* `version`: Application version. Included in the `details.log.gz` file.
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,15 @@
|
||||||
listings_auth_user: alice
|
listings_auth_user: alice
|
||||||
listings_auth_pass: secret
|
listings_auth_pass: secret
|
||||||
|
|
||||||
|
# the external URL at which /api is accessible; it is used to add a link to the
|
||||||
|
# report to the GitHub issue. If unspecified, based on the listen address.
|
||||||
|
# api_prefix: https://riot.im/bugreports
|
||||||
|
|
||||||
# a GitHub personal access token (https://github.com/settings/tokens), which
|
# a GitHub personal access token (https://github.com/settings/tokens), which
|
||||||
# will be used to create a GitHub issue for each report. It requires
|
# will be used to create a GitHub issue for each report. It requires
|
||||||
# `public_repo` scope. If omitted, no issues will be created.
|
# `public_repo` scope. If omitted, no issues will be created.
|
||||||
github_token: secrettoken
|
github_token: secrettoken
|
||||||
|
|
||||||
|
# mappings from app name (as submitted in the API) to github repo for issue reporting.
|
||||||
|
github_project_mappings:
|
||||||
|
my-app: octocat/HelloWorld
|
||||||
|
|
|
@ -25,8 +25,11 @@ import (
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
@ -39,8 +42,13 @@ type config struct {
|
||||||
BugsUser string `yaml:"listings_auth_user"`
|
BugsUser string `yaml:"listings_auth_user"`
|
||||||
BugsPass string `yaml:"listings_auth_pass"`
|
BugsPass string `yaml:"listings_auth_pass"`
|
||||||
|
|
||||||
|
// External URI to /api
|
||||||
|
APIPrefix string `yaml:"api_prefix"`
|
||||||
|
|
||||||
// A GitHub personal access token, to create a GitHub issue for each report.
|
// A GitHub personal access token, to create a GitHub issue for each report.
|
||||||
GithubToken string `yaml:"github_token"`
|
GithubToken string `yaml:"github_token"`
|
||||||
|
|
||||||
|
GithubProjectMappings map[string]string `yaml:"github_project_mappings"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func basicAuth(handler http.Handler, username, password, realm string) http.Handler {
|
func basicAuth(handler http.Handler, username, password, realm string) http.Handler {
|
||||||
|
@ -77,10 +85,24 @@ func main() {
|
||||||
&oauth2.Token{AccessToken: cfg.GithubToken},
|
&oauth2.Token{AccessToken: cfg.GithubToken},
|
||||||
)
|
)
|
||||||
tc := oauth2.NewClient(ctx, ts)
|
tc := oauth2.NewClient(ctx, ts)
|
||||||
|
tc.Timeout = time.Duration(5) * time.Minute
|
||||||
ghClient = github.NewClient(tc)
|
ghClient = github.NewClient(tc)
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Handle("/api/submit", &submitServer{ghClient})
|
apiPrefix := cfg.APIPrefix
|
||||||
|
if apiPrefix == "" {
|
||||||
|
_, port, err := net.SplitHostPort(*bindAddr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
apiPrefix = fmt.Sprintf("http://localhost:%s/api", port)
|
||||||
|
} else {
|
||||||
|
// remove trailing /
|
||||||
|
apiPrefix = strings.TrimRight(apiPrefix, "/")
|
||||||
|
}
|
||||||
|
log.Printf("Using %s/listing as public URI", apiPrefix)
|
||||||
|
|
||||||
|
http.Handle("/api/submit", &submitServer{ghClient, apiPrefix, cfg.GithubProjectMappings})
|
||||||
|
|
||||||
// Make sure bugs directory exists
|
// Make sure bugs directory exists
|
||||||
_ = os.Mkdir("bugs", os.ModePerm)
|
_ = os.Mkdir("bugs", os.ModePerm)
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,6 +39,12 @@ type submitServer struct {
|
||||||
// github client for reporting bugs. may be nil, in which case,
|
// github client for reporting bugs. may be nil, in which case,
|
||||||
// reporting is disabled.
|
// reporting is disabled.
|
||||||
ghClient *github.Client
|
ghClient *github.Client
|
||||||
|
|
||||||
|
// External URI to /api
|
||||||
|
apiPrefix string
|
||||||
|
|
||||||
|
// mappings from application to github owner/project
|
||||||
|
githubProjectMappings map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type payload struct {
|
type payload struct {
|
||||||
|
@ -95,12 +102,17 @@ func (s *submitServer) saveReport(ctx context.Context, p payload) error {
|
||||||
// "bugreport-20170115-112233-N.log.gz" => oldest log
|
// "bugreport-20170115-112233-N.log.gz" => oldest log
|
||||||
t := time.Now().UTC()
|
t := time.Now().UTC()
|
||||||
prefix := t.Format("2006-01-02/150405")
|
prefix := t.Format("2006-01-02/150405")
|
||||||
|
listingURL := s.apiPrefix + "/listing/" + prefix
|
||||||
|
|
||||||
|
log.Println("Handling report submission; listing URI will be %s", listingURL)
|
||||||
|
|
||||||
|
userText := strings.TrimSpace(p.Text)
|
||||||
|
|
||||||
var summaryBuf bytes.Buffer
|
var summaryBuf bytes.Buffer
|
||||||
fmt.Fprintf(
|
fmt.Fprintf(
|
||||||
&summaryBuf,
|
&summaryBuf,
|
||||||
"%s\n\nNumber of logs: %d\nApplication: %s\nVersion: %s\nUser-Agent: %s\n",
|
"%s\n\nNumber of logs: %d\nApplication: %s\nVersion: %s\nUser-Agent: %s\n",
|
||||||
p.Text, len(p.Logs), p.AppName, p.Version, p.UserAgent,
|
userText, len(p.Logs), p.AppName, p.Version, p.UserAgent,
|
||||||
)
|
)
|
||||||
for k, v := range p.Data {
|
for k, v := range p.Data {
|
||||||
fmt.Fprintf(&summaryBuf, "%s: %s\n", k, v)
|
fmt.Fprintf(&summaryBuf, "%s: %s\n", k, v)
|
||||||
|
@ -117,15 +129,46 @@ func (s *submitServer) saveReport(ctx context.Context, p payload) error {
|
||||||
|
|
||||||
if s.ghClient == nil {
|
if s.ghClient == nil {
|
||||||
// we're done here
|
// we're done here
|
||||||
|
log.Println("GH issue submission disabled")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// submit a github issue
|
// submit a github issue
|
||||||
owner := "richvdh"
|
|
||||||
repo := "test"
|
ghProj := s.githubProjectMappings[p.AppName]
|
||||||
title := "Automated bug report"
|
if ghProj == "" {
|
||||||
|
log.Println("Not creating GH issue for unknown app", p.AppName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
splits := strings.SplitN(ghProj, "/", 2)
|
||||||
|
if len(splits) < 2 {
|
||||||
|
log.Println("Can't create GH issue for invalid repo", ghProj)
|
||||||
|
}
|
||||||
|
owner, repo := splits[0], splits[1]
|
||||||
|
|
||||||
|
var title string
|
||||||
|
if userText == "" {
|
||||||
|
title = "Untitled report"
|
||||||
|
} else {
|
||||||
|
// set the title to the first line of the user's report
|
||||||
|
if i := strings.IndexAny(userText, "\r\n"); i < 0 {
|
||||||
|
title = userText
|
||||||
|
} else {
|
||||||
|
title = userText[0:i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body := fmt.Sprintf(
|
||||||
|
"User message:\n```\n%s\n```\nVersion: %s\n[Details](%s) / [Logs](%s)",
|
||||||
|
userText,
|
||||||
|
p.Version,
|
||||||
|
listingURL+"/details.log.gz",
|
||||||
|
listingURL,
|
||||||
|
)
|
||||||
|
|
||||||
issueReq := github.IssueRequest{
|
issueReq := github.IssueRequest{
|
||||||
Title: &title,
|
Title: &title,
|
||||||
|
Body: &body,
|
||||||
}
|
}
|
||||||
|
|
||||||
issue, _, err := s.ghClient.Issues.Create(ctx, owner, repo, &issueReq)
|
issue, _, err := s.ghClient.Issues.Create(ctx, owner, repo, &issueReq)
|
||||||
|
|
Reference in a new issue