github.com/q2/git-lfs@v0.5.1-0.20150410234700-03a0d4cec40e/commands/commands.go (about) 1 package commands 2 3 import ( 4 "bytes" 5 "fmt" 6 "github.com/github/git-lfs/lfs" 7 "github.com/spf13/cobra" 8 "io" 9 "log" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "strings" 14 "time" 15 ) 16 17 var ( 18 Debugging = false 19 ErrorBuffer = &bytes.Buffer{} 20 ErrorWriter = io.MultiWriter(os.Stderr, ErrorBuffer) 21 OutputWriter = io.MultiWriter(os.Stdout, ErrorBuffer) 22 RootCmd = &cobra.Command{ 23 Use: "git-lfs", 24 Short: "Git LFS provides large file storage to Git.", 25 Run: func(cmd *cobra.Command, args []string) { 26 versionCommand(cmd, args) 27 cmd.Usage() 28 }, 29 } 30 ) 31 32 // Error prints a formatted message to Stderr. It also gets printed to the 33 // panic log if one is created for this command. 34 func Error(format string, args ...interface{}) { 35 line := fmt.Sprintf(format, args...) 36 fmt.Fprintln(ErrorWriter, line) 37 } 38 39 // Print prints a formatted message to Stdout. It also gets printed to the 40 // panic log if one is created for this command. 41 func Print(format string, args ...interface{}) { 42 line := fmt.Sprintf(format, args...) 43 fmt.Fprintln(OutputWriter, line) 44 } 45 46 // Exit prints a formatted message and exits. 47 func Exit(format string, args ...interface{}) { 48 Error(format, args...) 49 os.Exit(2) 50 } 51 52 // Debug prints a formatted message if debugging is enabled. The formatted 53 // message also shows up in the panic log, if created. 54 func Debug(format string, args ...interface{}) { 55 if !Debugging { 56 return 57 } 58 log.Printf(format, args...) 59 } 60 61 // LoggedError prints a formatted message to Stderr and writes a stack trace for 62 // the error to a log file without exiting. 63 func LoggedError(err error, format string, args ...interface{}) { 64 Error(format, args...) 65 file := handlePanic(err) 66 67 if len(file) > 0 { 68 fmt.Fprintf(os.Stderr, "\nErrors logged to %s.\nUse `git lfs logs last` to view the log.\n", file) 69 } 70 } 71 72 // Panic prints a formatted message, and writes a stack trace for the error to 73 // a log file before exiting. 74 func Panic(err error, format string, args ...interface{}) { 75 LoggedError(err, format, args...) 76 os.Exit(2) 77 } 78 79 func Run() { 80 RootCmd.Execute() 81 } 82 83 func PipeMediaCommand(name string, args ...string) error { 84 return PipeCommand("bin/"+name, args...) 85 } 86 87 func PipeCommand(name string, args ...string) error { 88 cmd := exec.Command(name, args...) 89 cmd.Stdin = os.Stdin 90 cmd.Stderr = os.Stderr 91 cmd.Stdout = os.Stdout 92 return cmd.Run() 93 } 94 95 func handlePanic(err error) string { 96 if err == nil { 97 return "" 98 } 99 100 return logPanic(err, false) 101 } 102 103 func logEnv(w io.Writer) { 104 for _, env := range lfs.Environ() { 105 fmt.Fprintln(w, env) 106 } 107 } 108 109 func logPanic(loggedError error, recursive bool) string { 110 var fmtWriter io.Writer = os.Stderr 111 112 if err := os.MkdirAll(lfs.LocalLogDir, 0755); err != nil { 113 fmt.Fprintf(fmtWriter, "Unable to log panic to %s: %s\n\n", lfs.LocalLogDir, err.Error()) 114 return "" 115 } 116 117 now := time.Now() 118 name := now.Format("20060102T150405.999999999") 119 full := filepath.Join(lfs.LocalLogDir, name+".log") 120 121 file, err := os.Create(full) 122 if err == nil { 123 fmtWriter = file 124 defer file.Close() 125 } 126 127 fmt.Fprintf(fmtWriter, "> %s", filepath.Base(os.Args[0])) 128 if len(os.Args) > 0 { 129 fmt.Fprintf(fmtWriter, " %s", strings.Join(os.Args[1:], " ")) 130 } 131 fmt.Fprint(fmtWriter, "\n") 132 133 logEnv(fmtWriter) 134 fmt.Fprint(fmtWriter, "\n") 135 136 fmtWriter.Write(ErrorBuffer.Bytes()) 137 fmt.Fprint(fmtWriter, "\n") 138 139 fmt.Fprintln(fmtWriter, loggedError.Error()) 140 141 if wErr, ok := loggedError.(ErrorWithStack); ok { 142 fmt.Fprintln(fmtWriter, wErr.InnerError()) 143 for key, value := range wErr.Context() { 144 fmt.Fprintf(fmtWriter, "%s=%s\n", key, value) 145 } 146 fmtWriter.Write(wErr.Stack()) 147 } else { 148 fmtWriter.Write(lfs.Stack()) 149 } 150 151 if err != nil && !recursive { 152 fmt.Fprintf(fmtWriter, "Unable to log panic to %s\n\n", full) 153 logPanic(err, true) 154 } 155 156 return full 157 } 158 159 type ErrorWithStack interface { 160 Context() map[string]string 161 InnerError() string 162 Stack() []byte 163 } 164 165 func init() { 166 log.SetOutput(ErrorWriter) 167 }