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  }