diff --git a/.github/ISSUE_TEMPLATE/BUG-REPORT.md b/.github/ISSUE_TEMPLATE/BUG-REPORT.md index bc397183153..6d0f2e573c3 100644 --- a/.github/ISSUE_TEMPLATE/BUG-REPORT.md +++ b/.github/ISSUE_TEMPLATE/BUG-REPORT.md @@ -1,6 +1,7 @@ --- name: Bug Report Template about: Create a bug report +# NOTE: keep in sync with gnovm/cmd/gno/bug.go --- ## [Subject of the issue] diff --git a/docs/reference/go-gno-compatibility.md b/docs/reference/go-gno-compatibility.md index 0bac44518ce..b67d3c2dc15 100644 --- a/docs/reference/go-gno-compatibility.md +++ b/docs/reference/go-gno-compatibility.md @@ -287,7 +287,7 @@ Legend: | go command | gno command | comment | |-------------------|------------------|-----------------------------------------------------------------------| -| go bug | | see https://github.com/gnolang/gno/issues/733 | +| go bug | gno bug | same behavior | | go build | gno build | same intention, limited compatibility | | go clean | gno clean | same intention, limited compatibility | | go doc | gno doc | limited compatibility; see https://github.com/gnolang/gno/issues/522 | diff --git a/gnovm/cmd/gno/bug.go b/gnovm/cmd/gno/bug.go new file mode 100644 index 00000000000..98e6fb07319 --- /dev/null +++ b/gnovm/cmd/gno/bug.go @@ -0,0 +1,165 @@ +package main + +import ( + "context" + "flag" + "net/url" + "os/exec" + "runtime" + "runtime/debug" + "strings" + "text/template" + "time" + + "github.com/gnolang/gno/tm2/pkg/commands" +) + +// NOTE: keep in sync with .github/ISSUE_TEMPLATE/BUG-REPORT.md +const bugTmpl = `## [Subject of the issue] + +### Description + +Describe your issue in as much detail as possible here + +### Your environment + +* go version {{.GoVersion}} {{.Os}}/{{.Arch}} +* gno commit that causes this issue: {{.Commit}} + +### Steps to reproduce + +* Tell us how to reproduce this issue +* Where the issue is, if you know +* Which commands triggered the issue, if any + +### Expected behaviour + +Tell us what should happen + +### Actual behaviour + +Tell us what happens instead + +### Logs + +Please paste any logs here that demonstrate the issue, if they exist + +### Proposed solution + +If you have an idea of how to fix this issue, please write it down here, so we can begin discussing it + +` + +type bugCfg struct { + skipBrowser bool +} + +func newBugCmd(io commands.IO) *commands.Command { + cfg := &bugCfg{} + return commands.NewCommand( + commands.Metadata{ + Name: "bug", + ShortUsage: "bug", + ShortHelp: "Start a bug report", + }, + cfg, + func(_ context.Context, args []string) error { + return execBug(cfg, args, io) + }, + ) +} + +func (c *bugCfg) RegisterFlags(fs *flag.FlagSet) { + fs.BoolVar( + &c.skipBrowser, + "skip-browser", + false, + "do not open the browser", + ) +} + +func execBug(cfg *bugCfg, args []string, io commands.IO) error { + if len(args) != 0 { + return flag.ErrHelp + } + + bugReportEnv := struct { + Os, Arch, GoVersion, Commit string + }{ + runtime.GOOS, + runtime.GOARCH, + runtime.Version(), + getCommitHash(), + } + + var buf strings.Builder + tmpl, err := template.New("bug.tmpl").Parse(bugTmpl) + if err != nil { + return err + } + tmpl.Execute(&buf, bugReportEnv) + + body := buf.String() + url := "https://github.com/gnolang/gno/issues/new?body=" + url.QueryEscape(body) + + if !cfg.skipBrowser && openBrowser(url) { + return nil + } + + io.Println("Please file a new issue at github.com/gnolang/gno/issues/new using this template:") + io.Println() + io.Println(body) + + return nil +} + +// openBrowser opens a default web browser with the specified URL. +func openBrowser(url string) bool { + var cmdArgs []string + switch runtime.GOOS { + case "windows": + cmdArgs = []string{"cmd", "/c", "start", url} + case "darwin": + cmdArgs = []string{"/usr/bin/open", url} + default: // "linux" + cmdArgs = []string{"xdg-open", url} + } + + cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) + if cmd.Start() == nil && appearsSuccessful(cmd, 3*time.Second) { + return true + } + + return false +} + +// getCommitHash returns the commit hash from build info, or an +// empty string if not found. +func getCommitHash() string { + if info, ok := debug.ReadBuildInfo(); ok { + for _, setting := range info.Settings { + if setting.Key == "vcs.revision" { + return setting.Value + } + } + } + return "" +} + +// appearsSuccessful reports whether the command appears to have run successfully. +// If the command runs longer than the timeout, it's deemed successful. +// If the command runs within the timeout, it's deemed successful if it exited cleanly. +// Note: Taken from Go's `internal/browser“ +func appearsSuccessful(cmd *exec.Cmd, timeout time.Duration) bool { + errc := make(chan error, 1) + go func() { + errc <- cmd.Wait() + }() + + select { + case <-time.After(timeout): + return true + case err := <-errc: + return err == nil + } +} diff --git a/gnovm/cmd/gno/bug_test.go b/gnovm/cmd/gno/bug_test.go new file mode 100644 index 00000000000..81231c3d580 --- /dev/null +++ b/gnovm/cmd/gno/bug_test.go @@ -0,0 +1,21 @@ +package main + +import "testing" + +func TestBugApp(t *testing.T) { + tc := []testMainCase{ + { + args: []string{"bug -h"}, + errShouldBe: "flag: help requested", + }, + { + args: []string{"bug unknown"}, + errShouldBe: "flag: help requested", + }, + { + args: []string{"bug", "-skip-browser"}, + stdoutShouldContain: "go version go1.", + }, + } + testMainCaseRun(t, tc) +} diff --git a/gnovm/cmd/gno/main.go b/gnovm/cmd/gno/main.go index 5271bcb1e9f..8b77cfd2a10 100644 --- a/gnovm/cmd/gno/main.go +++ b/gnovm/cmd/gno/main.go @@ -33,6 +33,7 @@ func newGnocliCmd(io commands.IO) *commands.Command { newReplCmd(), newDocCmd(io), newEnvCmd(io), + newBugCmd(io), // fmt -- gofmt // graph // vendor -- download deps from the chain in vendor/ @@ -41,8 +42,7 @@ func newGnocliCmd(io commands.IO) *commands.Command { // publish/release // generate // "vm" -- starts an in-memory chain that can be interacted with? - // bug -- start a bug report - // version -- show gno, golang versions + // version -- show cmd/gno, golang versions ) return cmd