Skip to content

gh alias set#970

Merged
vilmibm merged 26 commits intotrunkfrom
alias-set
Jun 3, 2020
Merged

gh alias set#970
vilmibm merged 26 commits intotrunkfrom
alias-set

Conversation

@vilmibm
Copy link
Contributor

@vilmibm vilmibm commented May 19, 2020

This PR adds gh alias set.

what this PR does

  • allows creating aliases, including ones with placeholder arguments (e.g. $1)
  • validates that you can't set an alias for a non-existent command
  • adds a new top level aliases section to our config which is created on first use of alias set

what this PR doesn't do

considerations

I did two things I'm nervous about in this PR.

  1. modified main.go to initialize a Context, check the config, and rewrite argv if an alias is found.
  2. disabled flag parsing for alias set. I encountered what I think is a bug where cobra would treat flags inside of single quoted strings as something it should parse (eg alias set il 'issue list --label="bug"'). Disabling flag processing for this command fixed the problem but if we ever want alias set to accept flags we'll need to revisit this.

I'm open to feedback on those things.

demo

(right click view image to make more legible)

aliasdemo

todo todid

  • add command
  • process args properly
  • expose aliases through config
  • error if alias already exist
  • initialize aliases on disk on first set
  • actual alias dispatching
  • error if expand to illegal command
  • documentation
  • finish tests
    • arg processing
    • already exists
    • illegal command
    • proper dispatching
    • lazy initialization on first set
  • clean up hacks
    • context handling (maybe it's fine?? initContext seems sufficiently idempotent)
    • disabling of flag processing on alias set (though it might be fine to ship initially)
    • more resilient expansion code

closes #938

@vilmibm vilmibm marked this pull request as ready for review May 22, 2020 21:15
@vilmibm vilmibm changed the title [WIP] gh alias set gh alias set May 22, 2020
@vilmibm vilmibm mentioned this pull request May 22, 2020
Copy link
Contributor

@mislav mislav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the general approach taken here, but there are many edge cases that I feel should be addressed before shipping.

An idea: should we print information about alias expansion when DEBUG is active? This seems to me like a good use of debug mode; e.g.

$ DEBUG=1 gh pl foo
[pl foo] -> [pr list foo]
(...rest of output)

Additionally, I think that we should prevent recording and expanding an alias of the same name as a built-in CLI command. This is primarily for two reasons:

  1. Ease of debugging user problem reports: someone could report to us an error they are getting with gh pr create and we could spend time going back and forth with that user until we find out that the user has pr configured among their aliases that expands to something unexpected.

  2. Allowing our future commands to override people's aliases: it's possible that it would get popular that CLI users would share an alias named open within our community, but if ship a top-level command named open in a future release, we probably want our command to take precedence over the alias. Otherwise, our release notes are advertising gh open behavior that some users would not be getting because they have an active alias (and they might have forgotten about it).

command/alias.go Outdated
Comment on lines +25 to +28
// TODO HACK: even when inside of a single-quoted string, cobra was noticing and parsing any flags
// used in an alias expansion string argument. Since this command needs no flags, I disabled their
// parsing. If we ever want to add flags to alias set we'll have to figure this out. I haven't
// checked if this is fixed in a new cobra.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cobra will only ever try to parse arguments as flags if an argument begins with a -- or -. With default Cobra config, this will work:

gh alias set pc 'pr create --assignee mona'

i.e. Cobra will not try to parse flags within the pr create --assignee mona. However, the quote-free version will fail:

gh alias set pc pr create --assignee mona

This is because all "words" are now passed as distinct arguments to alias set and one of them looks like a flag. To enable this use, I think that DisableFlagParsing is a good approach, but you are right that we will have to revisit this should we ever want to enable flags specifically for alias set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gh alias set pc 'pr create --assignee mona' is exactly what didn't work, hence my saying "even when inside of a single-quoted string" and saying it seemed like a bug.

I understand how argument passing works :)

Copy link
Contributor

@mislav mislav May 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gh alias set pc 'pr create --assignee mona' is exactly what didn't work

I only brought up my points because I have first tried to reproduce this with DisableFlagParsing: false, but was not able to.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is my experience with DisableFlagParsing unset:

image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had trouble reproducing this too. It might be that you’re trying to use the --author flag on pr list but it surprisingly doesn’t exist. I tried out these alias for issues and they worked:

ghd alias set foo ‘issue list —author=“probablycorey”’
ghd alias set foo2 ‘issue list —author=probablycorey’

Copy link
Contributor Author

@vilmibm vilmibm May 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the error is from alias set; not pr list. i get the same result with issue list:

image

I thought it was possibly something weird about my shell but I'm getting the same behavior in bash and zsh.

I noticed that your paste, corey, isn't using actual single quotes but rather the ‘ character. I tried that but it still fails for me; I wonder, however, if that character is treated different on macos?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vilmibm I'm stumped. I cannot reproduce this when I remove the DisableFlagParsing workaround:

$ gh alias set ix 'issue list --author=vilmibm'
- Adding alias for ix: issue list --author=vilmibm
✓ Added alias.

$ gh alias set ix 'issue list --author="vilmibm"'
- Adding alias for ix: issue list --author="vilmibm"
✓ Changed alias ix from issue list --author=vilmibm to issue list --author="vilmibm"

$ gh ix

Showing 11 of 11 issues in cli/cli that match your search

#990  gh alias delete                     (aliases, enhancement)  about 6 days ago
...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷 i have no further ideas. it is concretely broken for me. i think we should leave flag parsing disabled until we have a reason to worry about it; i'll amend my comment to mention the weird by-platform behavior.

Copy link
Contributor

@mislav mislav Jun 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vilmibm I can see that you are running ghd instead of gh. Could you check in your ghd script that you are forwading the arguments with "$@" and not via $@ or $*?


func (c *fileConfig) parseAliasConfig(aliasesEntry *yaml.Node) (*AliasConfig, error) {
if aliasesEntry.Kind != yaml.MappingNode {
return nil, errors.New("expected aliases to be a map")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we also interpret a YAML null essentially as an empty mapping?

When experimenting with aliases, I've manually deleted alias entries in my config file, resulting in only this:

aliases:

the current alias-parsing code now aborts because aliases is not a map, but I think we should be permissive about this and interpret null as an empty map.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops yeah, good catch. that shouldn't explode a config parsing.

@mislav mislav changed the base branch from master to trunk May 27, 2020 11:41
Copy link
Contributor

@probablycorey probablycorey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! I can't wait to use it for real!

An idea: should we print information about alias expansion when DEBUG is active? This seems to me like a good use of debug mode; e.g.

I really like this idea. I ran into a situation where I couldn’t figure out why an alias wouldn’t work and this would have helped!

One thing that surprised me: If I try to change an alias I got “alias foo already exists” I was expecting it to say something like “alias foo was ghd xyz but is now god abc

command/alias.go Outdated
Comment on lines +25 to +28
// TODO HACK: even when inside of a single-quoted string, cobra was noticing and parsing any flags
// used in an alias expansion string argument. Since this command needs no flags, I disabled their
// parsing. If we ever want to add flags to alias set we'll have to figure this out. I haven't
// checked if this is fixed in a new cobra.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had trouble reproducing this too. It might be that you’re trying to use the --author flag on pr list but it surprisingly doesn’t exist. I tried out these alias for issues and they worked:

ghd alias set foo ‘issue list —author=“probablycorey”’
ghd alias set foo2 ‘issue list —author=probablycorey’

command/alias.go Outdated
}

if !validCommand(expansion) {
return fmt.Errorf("could not create alias: %s does not correspond to a gh command", utils.Bold(expansion))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the "error on valid command" idea. I think @vilmibm's idea of warning on import would be an acceptable fix for the potential import problem.

command/alias.go Outdated
Comment on lines +83 to +96
func validCommand(expansion string) bool {
split, _ := shlex.Split(expansion)
cmd, _, err := RootCmd.Traverse(split)
return err == nil && cmd != RootCmd
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is easier than I thought it would be!

@vilmibm
Copy link
Contributor Author

vilmibm commented May 28, 2020

I've caught up on all the feedback. The only thing that is giving me pause is I'm not pleased with my handling of the empty aliases key. I'll revisit that on Monday but it's possible that just leaving my kind of gross hack in place is enough for now.

Update: I moved that code into config_type and feel better about it.

@vilmibm vilmibm requested review from mislav and probablycorey May 28, 2020 04:35
Copy link
Contributor

@mislav mislav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting really close! I already love the functionality in this branch; works great for me ✨

My last remaining requests are related to tightening safety around how we pass arguments around so that single arguments that contain spaces do not accidentally get intepreted as multiple arguments due to shellsplitting.

command/alias.go Outdated
Comment on lines +25 to +28
// TODO HACK: even when inside of a single-quoted string, cobra was noticing and parsing any flags
// used in an alias expansion string argument. Since this command needs no flags, I disabled their
// parsing. If we ever want to add flags to alias set we'll have to figure this out. I haven't
// checked if this is fixed in a new cobra.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vilmibm I'm stumped. I cannot reproduce this when I remove the DisableFlagParsing workaround:

$ gh alias set ix 'issue list --author=vilmibm'
- Adding alias for ix: issue list --author=vilmibm
✓ Added alias.

$ gh alias set ix 'issue list --author="vilmibm"'
- Adding alias for ix: issue list --author="vilmibm"
✓ Changed alias ix from issue list --author=vilmibm to issue list --author="vilmibm"

$ gh ix

Showing 11 of 11 issues in cli/cli that match your search

#990  gh alias delete                     (aliases, enhancement)  about 6 days ago
...

@vilmibm vilmibm requested a review from mislav June 2, 2020 21:07
@vilmibm
Copy link
Contributor Author

vilmibm commented Jun 2, 2020

@mislav caught up once more.

Copy link
Contributor

@mislav mislav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One tiny edge case; otherwise everything looks great! Thank you for the hard work

command/alias.go Outdated
return args[0]
}

return strings.Join(args, " ")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm finding that this is still an issue. Repro:

$ gh config set editor 'path with spaces'  # works

$ gh alias set c config set editor 'path with spaces'
$ gh c
#=> error: "accepts 2 arg(s), received 4"

this is because 'path with spaces' stopped being treated as a single argument when serialized into config:

aliases:
    c: config set editor path with spaces

@vilmibm
Copy link
Contributor Author

vilmibm commented Jun 3, 2020

nevermind, i'm tired and this is fully PEBCAC. My ghd wrapper was interfering 🤦

@mislav Something very strange is happening.

When I run gh, I lose quoted arguments. The quotes are gone from the moment I print each thing in os.Args.

when I print each thing in os.Args from another Go program, quoted arguments are preserved as expected.

It seems that something is pre-processing os.Args and what you're seeing is breaking long before the alias code gets to it.

I will continue investigating.

@DanyC97
Copy link

DanyC97 commented Jun 3, 2020

@vilmibm are the aliases stored in a file somewhere? would be awesome if somehow gh can read from gitconfig so folks can build their own dotfiles and at the same time keep them in same place

@vilmibm
Copy link
Contributor Author

vilmibm commented Jun 3, 2020

@DanyC97 aliases are stored in the config.yml file; we have no plans to read them from gitconfig but we're actively removing the auth stuff from the gh config file so that people can start committing their gh config alongside other dotfiles.

@vilmibm vilmibm merged commit 7fc8677 into trunk Jun 3, 2020
@mislav mislav deleted the alias-set branch June 25, 2020 14:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gh alias set

4 participants