github.com/nkprince007/lab@v0.6.2-0.20171218071646-19d68b56f403/cmd/root.go (about)

     1  package cmd
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"os"
     8  	"os/exec"
     9  	"strconv"
    10  	"strings"
    11  	"syscall"
    12  	"text/template"
    13  	"unicode"
    14  
    15  	"github.com/pkg/errors"
    16  	"github.com/spf13/cobra"
    17  	gitconfig "github.com/tcnksm/go-gitconfig"
    18  	"github.com/zaquestion/lab/internal/git"
    19  	lab "github.com/zaquestion/lab/internal/gitlab"
    20  )
    21  
    22  // RootCmd represents the base command when called without any subcommands
    23  var RootCmd = &cobra.Command{
    24  	Use:   "lab",
    25  	Short: "A Git Wrapper for GitLab",
    26  	Long:  ``,
    27  	Run: func(cmd *cobra.Command, args []string) {
    28  		formatChar := "\n"
    29  		if git.IsHub {
    30  			formatChar = ""
    31  		}
    32  
    33  		git := git.New()
    34  		git.Stdout = nil
    35  		git.Stderr = nil
    36  		usage, _ := git.CombinedOutput()
    37  		fmt.Printf("%s%sThese GitLab commands are provided by lab:\n%s\n\n", string(usage), formatChar, labUsage(cmd))
    38  	},
    39  }
    40  
    41  func trimRightSpace(s string) string {
    42  	return strings.TrimRightFunc(s, unicode.IsSpace)
    43  }
    44  
    45  func rpad(s string, padding int) string {
    46  	template := fmt.Sprintf("%%-%ds", padding)
    47  	return fmt.Sprintf(template, s)
    48  }
    49  
    50  var templateFuncs = template.FuncMap{
    51  	"trimTrailingWhitespaces": trimRightSpace,
    52  	"rpad": rpad,
    53  }
    54  
    55  const labUsageTmpl = `{{range .Commands}}{{if (and (or .IsAvailableCommand (ne .Name "help")) (and (ne .Name "clone") (ne .Name "version")))}}
    56    {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}`
    57  
    58  func labUsage(c *cobra.Command) string {
    59  	t := template.New("top")
    60  	t.Funcs(templateFuncs)
    61  	template.Must(t.Parse(labUsageTmpl))
    62  
    63  	var buf bytes.Buffer
    64  	err := t.Execute(&buf, c)
    65  	if err != nil {
    66  		c.Println(err)
    67  	}
    68  	return buf.String()
    69  }
    70  
    71  // parseArgsRemote returns the remote and a number if parsed. Many commands
    72  // accept a remote to operate on and number such as a page id
    73  func parseArgsRemote(args []string) (string, int64, error) {
    74  	if len(args) == 2 {
    75  		n, err := strconv.ParseInt(args[1], 0, 64)
    76  		if err != nil {
    77  			return "", 0, err
    78  		}
    79  		ok, err := git.IsRemote(args[0])
    80  		if err != nil {
    81  			return "", 0, err
    82  		} else if !ok {
    83  			return "", 0, errors.Errorf("%s is not a valid remote", args[0])
    84  		}
    85  		return args[0], n, nil
    86  	}
    87  	if len(args) == 1 {
    88  		ok, err := git.IsRemote(args[0])
    89  		if err != nil {
    90  			return "", 0, err
    91  		}
    92  		if ok {
    93  			return args[0], 0, nil
    94  		}
    95  		n, err := strconv.ParseInt(args[0], 0, 64)
    96  		if err == nil {
    97  			return "", n, nil
    98  		}
    99  		return "", 0, errors.Errorf("%s is not a valid remote or number", args[0])
   100  	}
   101  	return "", 0, nil
   102  }
   103  
   104  var (
   105  	// Will be updated to upstream in init() if "upstream" remote exists
   106  	forkedFromRemote = "origin"
   107  	// Will be updated to lab.User() in init() if forkedFrom is "origin"
   108  	forkRemote = "origin"
   109  )
   110  
   111  // Execute adds all child commands to the root command and sets flags appropriately.
   112  // This is called by main.main(). It only needs to happen once to the rootCmd.
   113  func Execute() {
   114  	_, err := gitconfig.Local("remote.upstream.url")
   115  	if err == nil {
   116  		forkedFromRemote = "upstream"
   117  	}
   118  
   119  	if forkedFromRemote == "origin" {
   120  		// Check if the user fork exists
   121  		_, err = gitconfig.Local("remote." + lab.User() + ".url")
   122  		if err == nil {
   123  			forkRemote = lab.User()
   124  		}
   125  	}
   126  	if cmd, _, err := RootCmd.Find(os.Args[1:]); err != nil || cmd.Use == "clone" {
   127  		// Determine if any undefined flags were passed to "clone"
   128  		if cmd.Use == "clone" && len(os.Args) > 2 {
   129  			// ParseFlags will err in these cases
   130  			err = cmd.ParseFlags(os.Args[1:])
   131  			if err == nil {
   132  				if err := RootCmd.Execute(); err != nil {
   133  					// Execute has already logged the error
   134  					os.Exit(1)
   135  				}
   136  				return
   137  			}
   138  		}
   139  
   140  		// Passthrough to git for any unrecognised commands
   141  		git := git.New(os.Args[1:]...)
   142  		err = git.Run()
   143  		if exiterr, ok := err.(*exec.ExitError); ok {
   144  			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
   145  				os.Exit(status.ExitStatus())
   146  			}
   147  		}
   148  		if err != nil {
   149  			log.Fatal(err)
   150  		}
   151  		return
   152  	}
   153  	if err := RootCmd.Execute(); err != nil {
   154  		// Execute has already logged the error
   155  		os.Exit(1)
   156  	}
   157  }