github.com/abdfnx/gh-api@v0.0.0-20210414084727-f5432eec23b8/pkg/cmd/alias/set/set.go (about) 1 package set 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/MakeNowJust/heredoc" 8 "github.com/abdfnx/gh-api/internal/config" 9 "github.com/abdfnx/gh-api/pkg/cmdutil" 10 "github.com/abdfnx/gh-api/pkg/iostreams" 11 "github.com/google/shlex" 12 "github.com/spf13/cobra" 13 ) 14 15 type SetOptions struct { 16 Config func() (config.Config, error) 17 IO *iostreams.IOStreams 18 19 Name string 20 Expansion string 21 IsShell bool 22 RootCmd *cobra.Command 23 } 24 25 func NewCmdSet(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command { 26 opts := &SetOptions{ 27 IO: f.IOStreams, 28 Config: f.Config, 29 } 30 31 cmd := &cobra.Command{ 32 Use: "set <alias> <expansion>", 33 Short: "Create a shortcut for a gh command", 34 Long: heredoc.Doc(` 35 Declare a word as a command alias that will expand to the specified command(s). 36 37 The expansion may specify additional arguments and flags. If the expansion 38 includes positional placeholders such as '$1', '$2', etc., any extra arguments 39 that follow the invocation of an alias will be inserted appropriately. 40 41 If '--shell' is specified, the alias will be run through a shell interpreter (sh). This allows you 42 to compose commands with "|" or redirect with ">". Note that extra arguments following the alias 43 will not be automatically passed to the expanded expression. To have a shell alias receive 44 arguments, you must explicitly accept them using "$1", "$2", etc., or "$@" to accept all of them. 45 46 Platform note: on Windows, shell aliases are executed via "sh" as installed by Git For Windows. If 47 you have installed git on Windows in some other way, shell aliases may not work for you. 48 49 Quotes must always be used when defining a command as in the examples. 50 `), 51 Example: heredoc.Doc(` 52 $ gh alias set pv 'pr view' 53 $ gh pv -w 123 54 #=> gh pr view -w 123 55 56 $ gh alias set bugs 'issue list --label="bugs"' 57 $ gh bugs 58 59 $ gh alias set homework 'issue list --assignee @me' 60 $ gh homework 61 62 $ gh alias set epicsBy 'issue list --author="$1" --label="epic"' 63 $ gh epicsBy vilmibm 64 #=> gh issue list --author="vilmibm" --label="epic" 65 66 $ gh alias set --shell igrep 'gh issue list --label="$1" | grep $2' 67 $ gh igrep epic foo 68 #=> gh issue list --label="epic" | grep "foo" 69 `), 70 Args: cobra.ExactArgs(2), 71 RunE: func(cmd *cobra.Command, args []string) error { 72 opts.RootCmd = cmd.Root() 73 74 opts.Name = args[0] 75 opts.Expansion = args[1] 76 77 if runF != nil { 78 return runF(opts) 79 } 80 return setRun(opts) 81 }, 82 } 83 84 cmd.Flags().BoolVarP(&opts.IsShell, "shell", "s", false, "Declare an alias to be passed through a shell interpreter") 85 86 return cmd 87 } 88 89 func setRun(opts *SetOptions) error { 90 cs := opts.IO.ColorScheme() 91 cfg, err := opts.Config() 92 if err != nil { 93 return err 94 } 95 96 aliasCfg, err := cfg.Aliases() 97 if err != nil { 98 return err 99 } 100 101 isTerminal := opts.IO.IsStdoutTTY() 102 if isTerminal { 103 fmt.Fprintf(opts.IO.ErrOut, "- Adding alias for %s: %s\n", cs.Bold(opts.Name), cs.Bold(opts.Expansion)) 104 } 105 106 expansion := opts.Expansion 107 isShell := opts.IsShell 108 if isShell && !strings.HasPrefix(expansion, "!") { 109 expansion = "!" + expansion 110 } 111 isShell = strings.HasPrefix(expansion, "!") 112 113 if validCommand(opts.RootCmd, opts.Name) { 114 return fmt.Errorf("could not create alias: %q is already a gh command", opts.Name) 115 } 116 117 if !isShell && !validCommand(opts.RootCmd, expansion) { 118 return fmt.Errorf("could not create alias: %s does not correspond to a gh command", expansion) 119 } 120 121 successMsg := fmt.Sprintf("%s Added alias.", cs.SuccessIcon()) 122 if oldExpansion, ok := aliasCfg.Get(opts.Name); ok { 123 successMsg = fmt.Sprintf("%s Changed alias %s from %s to %s", 124 cs.SuccessIcon(), 125 cs.Bold(opts.Name), 126 cs.Bold(oldExpansion), 127 cs.Bold(expansion), 128 ) 129 } 130 131 err = aliasCfg.Add(opts.Name, expansion) 132 if err != nil { 133 return fmt.Errorf("could not create alias: %s", err) 134 } 135 136 if isTerminal { 137 fmt.Fprintln(opts.IO.ErrOut, successMsg) 138 } 139 140 return nil 141 } 142 143 func validCommand(rootCmd *cobra.Command, expansion string) bool { 144 split, err := shlex.Split(expansion) 145 if err != nil { 146 return false 147 } 148 149 cmd, _, err := rootCmd.Traverse(split) 150 return err == nil && cmd != rootCmd 151 }