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