1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
package set
import (
"fmt"
"io"
"strings"
"github.com/MakeNowJust/heredoc"
"github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/pkg/cmd/alias/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
"github.com/spf13/cobra"
)
type SetOptions struct {
Config func() (config.Config, error)
IO *iostreams.IOStreams
Name string
Expansion string
IsShell bool
OverwriteExisting bool
validAliasName func(string) bool
validAliasExpansion func(string) bool
}
func NewCmdSet(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command {
opts := &SetOptions{
IO: f.IOStreams,
Config: f.Config,
}
cmd := &cobra.Command{
Use: "set <alias> <expansion>",
Short: "Create a shortcut for a gh command",
Long: heredoc.Docf(`
Define a word that will expand to a full gh command when invoked.
The expansion may specify additional arguments and flags. If the expansion includes
positional placeholders such as %[1]s$1%[1]s, extra arguments that follow the alias will be
inserted appropriately. Otherwise, extra arguments will be appended to the expanded
command.
Use %[1]s-%[1]s as expansion argument to read the expansion string from standard input. This
is useful to avoid quoting issues when defining expansions.
If the expansion starts with %[1]s!%[1]s or if %[1]s--shell%[1]s was given, the expansion is a shell
expression that will be evaluated through the %[1]ssh%[1]s interpreter when the alias is
invoked. This allows for chaining multiple commands via piping and redirection.
`, "`"),
Example: heredoc.Doc(`
# note: Command Prompt on Windows requires using double quotes for arguments
$ gh alias set pv 'pr view'
$ gh pv -w 123 #=> gh pr view -w 123
$ gh alias set bugs 'issue list --label=bugs'
$ gh bugs
$ gh alias set homework 'issue list --assignee @me'
$ gh homework
$ gh alias set 'issue mine' 'issue list --mention @me'
$ gh issue mine
$ gh alias set epicsBy 'issue list --author="$1" --label="epic"'
$ gh epicsBy vilmibm #=> gh issue list --author="vilmibm" --label="epic"
$ gh alias set --shell igrep 'gh issue list --label="$1" | grep "$2"'
$ gh igrep epic foo #=> gh issue list --label="epic" | grep "foo"
`),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
opts.Name = args[0]
opts.Expansion = args[1]
opts.validAliasName = shared.ValidAliasNameFunc(cmd)
opts.validAliasExpansion = shared.ValidAliasExpansionFunc(cmd)
if runF != nil {
return runF(opts)
}
return setRun(opts)
},
}
cmd.Flags().BoolVarP(&opts.IsShell, "shell", "s", false, "Declare an alias to be passed through a shell interpreter")
cmd.Flags().BoolVar(&opts.OverwriteExisting, "clobber", false, "Overwrite existing aliases of the same name")
return cmd
}
func setRun(opts *SetOptions) error {
cs := opts.IO.ColorScheme()
cfg, err := opts.Config()
if err != nil {
return err
}
aliasCfg := cfg.Aliases()
expansion, err := getExpansion(opts)
if err != nil {
return fmt.Errorf("did not understand expansion: %w", err)
}
if opts.IsShell && !strings.HasPrefix(expansion, "!") {
expansion = "!" + expansion
}
isTerminal := opts.IO.IsStdoutTTY()
if isTerminal {
fmt.Fprintf(opts.IO.ErrOut, "- Creating alias for %s: %s\n", cs.Bold(opts.Name), cs.Bold(expansion))
}
var existingAlias bool
if _, err := aliasCfg.Get(opts.Name); err == nil {
existingAlias = true
}
if !opts.validAliasName(opts.Name) {
if !existingAlias {
return fmt.Errorf("%s Could not create alias %s: already a gh command or extension",
cs.FailureIcon(),
cs.Bold(opts.Name))
}
if existingAlias && !opts.OverwriteExisting {
return fmt.Errorf("%s Could not create alias %s: name already taken, use the --clobber flag to overwrite it",
cs.FailureIcon(),
cs.Bold(opts.Name),
)
}
}
if !opts.validAliasExpansion(expansion) {
return fmt.Errorf("%s Could not create alias %s: expansion does not correspond to a gh command, extension, or alias",
cs.FailureIcon(),
cs.Bold(opts.Name))
}
aliasCfg.Add(opts.Name, expansion)
err = cfg.Write()
if err != nil {
return err
}
successMsg := fmt.Sprintf("%s Added alias %s", cs.SuccessIcon(), cs.Bold(opts.Name))
if existingAlias && opts.OverwriteExisting {
successMsg = fmt.Sprintf("%s Changed alias %s",
cs.WarningIcon(),
cs.Bold(opts.Name))
}
if isTerminal {
fmt.Fprintln(opts.IO.ErrOut, successMsg)
}
return nil
}
func getExpansion(opts *SetOptions) (string, error) {
if opts.Expansion == "-" {
stdin, err := io.ReadAll(opts.IO.In)
if err != nil {
return "", fmt.Errorf("failed to read from STDIN: %w", err)
}
return string(stdin), nil
}
return opts.Expansion, nil
}
|