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  }