github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/gnovm/cmd/gno/bug.go (about) 1 package main 2 3 import ( 4 "context" 5 "flag" 6 "net/url" 7 "os/exec" 8 "runtime" 9 "runtime/debug" 10 "strings" 11 "text/template" 12 "time" 13 14 "github.com/gnolang/gno/tm2/pkg/commands" 15 ) 16 17 // NOTE: keep in sync with .github/ISSUE_TEMPLATE/BUG-REPORT.md 18 const bugTmpl = `## [Subject of the issue] 19 20 ### Description 21 22 Describe your issue in as much detail as possible here 23 24 ### Your environment 25 26 * go version {{.GoVersion}} {{.Os}}/{{.Arch}} 27 * gno commit that causes this issue: {{.Commit}} 28 29 ### Steps to reproduce 30 31 * Tell us how to reproduce this issue 32 * Where the issue is, if you know 33 * Which commands triggered the issue, if any 34 35 ### Expected behaviour 36 37 Tell us what should happen 38 39 ### Actual behaviour 40 41 Tell us what happens instead 42 43 ### Logs 44 45 Please paste any logs here that demonstrate the issue, if they exist 46 47 ### Proposed solution 48 49 If you have an idea of how to fix this issue, please write it down here, so we can begin discussing it 50 51 ` 52 53 type bugCfg struct { 54 skipBrowser bool 55 } 56 57 func newBugCmd(io commands.IO) *commands.Command { 58 cfg := &bugCfg{} 59 return commands.NewCommand( 60 commands.Metadata{ 61 Name: "bug", 62 ShortUsage: "bug", 63 ShortHelp: "Start a bug report", 64 }, 65 cfg, 66 func(_ context.Context, args []string) error { 67 return execBug(cfg, args, io) 68 }, 69 ) 70 } 71 72 func (c *bugCfg) RegisterFlags(fs *flag.FlagSet) { 73 fs.BoolVar( 74 &c.skipBrowser, 75 "skip-browser", 76 false, 77 "do not open the browser", 78 ) 79 } 80 81 func execBug(cfg *bugCfg, args []string, io commands.IO) error { 82 if len(args) != 0 { 83 return flag.ErrHelp 84 } 85 86 bugReportEnv := struct { 87 Os, Arch, GoVersion, Commit string 88 }{ 89 runtime.GOOS, 90 runtime.GOARCH, 91 runtime.Version(), 92 getCommitHash(), 93 } 94 95 var buf strings.Builder 96 tmpl, err := template.New("bug.tmpl").Parse(bugTmpl) 97 if err != nil { 98 return err 99 } 100 tmpl.Execute(&buf, bugReportEnv) 101 102 body := buf.String() 103 url := "https://github.com/gnolang/gno/issues/new?body=" + url.QueryEscape(body) 104 105 if !cfg.skipBrowser && openBrowser(url) { 106 return nil 107 } 108 109 io.Println("Please file a new issue at github.com/gnolang/gno/issues/new using this template:") 110 io.Println() 111 io.Println(body) 112 113 return nil 114 } 115 116 // openBrowser opens a default web browser with the specified URL. 117 func openBrowser(url string) bool { 118 var cmdArgs []string 119 switch runtime.GOOS { 120 case "windows": 121 cmdArgs = []string{"cmd", "/c", "start", url} 122 case "darwin": 123 cmdArgs = []string{"/usr/bin/open", url} 124 default: // "linux" 125 cmdArgs = []string{"xdg-open", url} 126 } 127 128 cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) 129 if cmd.Start() == nil && appearsSuccessful(cmd, 3*time.Second) { 130 return true 131 } 132 133 return false 134 } 135 136 // getCommitHash returns the commit hash from build info, or an 137 // empty string if not found. 138 func getCommitHash() string { 139 if info, ok := debug.ReadBuildInfo(); ok { 140 for _, setting := range info.Settings { 141 if setting.Key == "vcs.revision" { 142 return setting.Value 143 } 144 } 145 } 146 return "" 147 } 148 149 // appearsSuccessful reports whether the command appears to have run successfully. 150 // If the command runs longer than the timeout, it's deemed successful. 151 // If the command runs within the timeout, it's deemed successful if it exited cleanly. 152 // Note: Taken from Go's `internal/browser“ 153 func appearsSuccessful(cmd *exec.Cmd, timeout time.Duration) bool { 154 errc := make(chan error, 1) 155 go func() { 156 errc <- cmd.Wait() 157 }() 158 159 select { 160 case <-time.After(timeout): 161 return true 162 case err := <-errc: 163 return err == nil 164 } 165 }