github.com/trevoraustin/hub@v2.2.0-preview1.0.20141105230840-96d8bfc654cc+incompatible/github/crash_report.go (about) 1 package github 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "os" 8 "reflect" 9 "runtime" 10 "strings" 11 12 "github.com/github/hub/git" 13 "github.com/github/hub/utils" 14 ) 15 16 const ( 17 hubReportCrashConfig = "hub.reportCrash" 18 hubProjectOwner = "github" 19 hubProjectName = "hub" 20 ) 21 22 func CaptureCrash() { 23 if rec := recover(); rec != nil { 24 if err, ok := rec.(error); ok { 25 reportCrash(err) 26 } else if err, ok := rec.(string); ok { 27 reportCrash(errors.New(err)) 28 } 29 } 30 } 31 32 func reportCrash(err error) { 33 if err == nil { 34 return 35 } 36 37 buf := make([]byte, 10000) 38 runtime.Stack(buf, false) 39 stack := formatStack(buf) 40 41 switch reportCrashConfig() { 42 case "always": 43 report(err, stack) 44 case "never": 45 printError(err, stack) 46 default: 47 printError(err, stack) 48 fmt.Print("Would you like to open an issue? ([Y]es/[N]o/[A]lways/N[e]ver): ") 49 var confirm string 50 fmt.Scan(&confirm) 51 52 always := utils.IsOption(confirm, "a", "always") 53 if always || utils.IsOption(confirm, "y", "yes") { 54 report(err, stack) 55 } 56 57 saveReportConfiguration(confirm, always) 58 } 59 os.Exit(1) 60 } 61 62 func report(reportedError error, stack string) { 63 title, body, err := reportTitleAndBody(reportedError, stack) 64 utils.Check(err) 65 66 project := NewProject(hubProjectOwner, hubProjectName, GitHubHost) 67 68 gh := NewClient(project.Host) 69 70 issue, err := gh.CreateIssue(project, title, body, []string{"Crash Report"}) 71 utils.Check(err) 72 73 fmt.Println(issue.HTMLURL) 74 } 75 76 func reportTitleAndBody(reportedError error, stack string) (title, body string, err error) { 77 message := "Crash report - %v\n\nError (%s): `%v`\n\nStack:\n\n```\n%s\n```\n\nRuntime:\n\n```\n%s\n```\n\n" 78 message += ` 79 # Creating crash report: 80 # 81 # This information will be posted as a new issue under jingweno/gh. 82 # We're NOT including any information about the command that you were executing, 83 # but knowing a little bit more about it would really help us to solve this problem. 84 # Feel free to modify the title and the description for this issue. 85 ` 86 87 errType := reflect.TypeOf(reportedError).String() 88 message = fmt.Sprintf(message, reportedError, errType, reportedError, stack, runtimeInfo()) 89 90 editor, err := NewEditor("CRASH_REPORT", "crash report", message) 91 if err != nil { 92 return "", "", err 93 } 94 95 defer editor.DeleteFile() 96 97 return editor.EditTitleAndBody() 98 } 99 100 func runtimeInfo() string { 101 return fmt.Sprintf("GOOS: %s\nGOARCH: %s", runtime.GOOS, runtime.GOARCH) 102 } 103 104 func formatStack(buf []byte) string { 105 buf = bytes.Trim(buf, "\x00") 106 107 stack := strings.Split(string(buf), "\n") 108 stack = append(stack[0:1], stack[5:]...) 109 110 return strings.Join(stack, "\n") 111 } 112 113 func printError(err error, stack string) { 114 fmt.Printf("%v\n\n", err) 115 fmt.Println(stack) 116 } 117 118 func saveReportConfiguration(confirm string, always bool) { 119 if always { 120 git.SetGlobalConfig(hubReportCrashConfig, "always") 121 } else if utils.IsOption(confirm, "e", "never") { 122 git.SetGlobalConfig(hubReportCrashConfig, "never") 123 } 124 } 125 126 func reportCrashConfig() (opt string) { 127 opt = os.Getenv("GH_REPORT_CRASH") 128 if opt == "" { 129 opt, _ = git.GlobalConfig(hubReportCrashConfig) 130 } 131 132 return 133 }