Closed meatballhat closed 1 week ago
(Moving from https://github.com/urfave/cli/issues/833#issuecomment-1312834335 to here)
flags: must start with
-
This is reasonable imo, I haven't seen any use for not starting (non-argument) flags without -
. It also majorly simplifies parsing.
flags: must start with
-
This is reasonable imo, I haven't seen any use for not starting (non-argument) flags without
-
. It also majorly simplifies parsing.
@jtagcat it's a unix-y-centric way of working, imho. For example, in Windows there's strong precedent for /
as prefix and :
as assignment like /{flag}:{value}
, although this also conflicts with the idea of "compound short flags" 🤷🏼 I'd like to provide a flexible way to allow for different argument styles if possible, e.g. how argparse does it.
Ok, fair point.
I'd say multiple prefixes at once gets unreasonably complex. Yet everything is possible by swapping out (not adding to) the prefix and/or the non-space delimiter (=
) and/or boolean for enabling compound short flags. Multiple parse calls with different delimiters gets you any result you could want.
For arguments without a key (eg. @file.txt
), the caller (or a library wrapping harg) can just do a for range against (parsed) args with strings.HasPrefix()
tailored to the use-case.
Performance of multiple parses isn't an issue. First, arguments are only parsed once (on app startup). With modern processors, multiple calls may even be more performant vs a messy monolithic do-all parser.
But I'd not touch the topic right now at all. A bad standard is better than 5 standards. In my opinion, the spec I have in FORMAT.md is fairly good, well-known, and used. Even (new) Microsoft CLI utilities use GNU-like arguments.
Everone is free to fork (and wrap the library or whole cli binary) for their own niche. I want to make the codebase maintainable (and workable) for as many as possible, including myself. One niche use-case just isn't worth it.
I want to keep it as simple as possible. There isn't anything we are removing compared to stdlib flags. Right now, getting v3 released is the priority.
@jtagcat I appreciate the points you're making.
I'd love to see what an integration of harg for the purpose of addressing this issue looks like, if you're up for doing that work.
I'm personally interested in exploring this approach, if only to better understand how the urfave/cli layer can change.
@meatballhat @jtagcat Both of you make good points. I think as software devs we'd like to make software perfect - covers all use cases, very performant, infintely extensible and so on. While a complete new parser is good, we need to see how much we can stretch stdlib flag to the max to cover most of the use cases. We cannot satisfy every user's use cases so its better to list what features beyond what urfave/cli/v2 offers do we want to achieve.
@dearchap Are you suggesting that this issue is not worth pursuing (yet)? 😅 imho the workarounds in place to support nested commands and compound short flags are indications that we've already gone past the point of stretching stdlib flag to the max. Or do I misunderstand what you mean? ❤️
@meatballhat What are the most requested features of this library ?
if we have those two knocked out, rest of the features are icing on the cake no ?
Update: https://github.com/jtagcat/harg is tested and ready to integrate.
henv
(environment variables) seems surprisingly straight-forward to implement. Would likely have to restructure the project files, though.
I think I'd like to keep help text, defaults, and bash completion outside the package. I'll dig around this cli
package before, to see where it makes the most sense to implement at.
@dearchap @meatballhat requesting review if you have the time.
@jtagcat imho https://github.com/jtagcat/harg is lovely as heck and I would very much like to see what an integration with urfave/cli/v3 looks like 😁 Is this work you're planning to do soon?
@meatballhat: Soon, but not soon. I have half a workday today, then maybe next Sunday, then longer periods starting around 18. dec.
Honestly, it plays such a major role in the cli package, a complete rewrite (creatively egoistically named hcli
) for v3 might be doable.
What components are there?
Flag
, replace Required bool
with Action
, renamed to Condition
. It is trivial to write common implementations with generic functions. The library can provide commonly used ones, such as NotDefault
(must be set by user, --foo=""
is valid) and NotEmpty
. In most cases, omitted or looking like cli.StringFlag{Name: "foo", Condition: cli.NotEmpty}
.
Ideas for changes I've had so far. I've asked around for feedback, and clarifying what Required
did come up.$ app
→ ls $(pwd)
$ app <flag>
→ cd <flag>
[^notv3]It should also include everything in the v3 milestone. Anything I missed? The only parts I expect to get stuck on are shell completions, and yaml. They are not critical for a v3 beta, though.
[^notv3]: Not needed for a functional beta release. No changes are necessary for adding it without breaking changes.
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"25", GitVersion:"v1.25.4", GitCommit:"872a965c6c6526caa949f0c6ac028ef7aff3fb78", GitTreeState:"clean", BuildDate:"2022-11-09T13:36:36Z", GoVersion:"go1.19.3", Compiler:"gc", Platform:"linux/amd64"}
```
Implementation:
```
var BuildInfoGitCommit string
```
```
export GIT_COMMIT=$(git rev-list -1 HEAD) && \
go build -ldflags "-X cli.BuildInfoGitCommit=$GIT_COMMIT"
```
@jtagcat do I understand correctly that you are recommending against integrating jtagcat/harg
directly with this library, but instead to make a jtagcat/hcli
project that uses it, and then use jtagcat/hcli
in this library?
@jtagcat could you provide some examples of how users would use the harg library ? Maybe a definition for a int flag and a float slice . Thanks
@jtagcat do I understand correctly that you are recommending against integrating
jtagcat/harg
directly with this library, but instead to make ajtagcat/hcli
project that uses it, and then usejtagcat/hcli
in this library?
I meant urfave/cli@v3
= hcli
, maybe. I'm not recommending, just had some thoughts.
@jtagcat could you provide some examples of how users would use the harg library? Maybe a definition for a int flag and a float slice . Thanks
One way would be to do as it is now: Define flags (and aliases, etc), and then retrieve by string. Just the underlining library differs. (I would expect a flag to be available for all subcommands unless explicitly marked as local.)
I don't know if it is ergonomically possible to type the lookup string, as it is in harg tests:
key1 := "foo"
defs := harg.Definitions{key1: {Type: harg.String}}
defs.Parse()
s, _ := defs[key1].String()
I probably need some time to think about it.
If you meant direct use of harg
, see https://github.com/jtagcat/harg/blob/main/parse_test.go#L16 (or example at https://pkg.go.dev/github.com/jtagcat/harg#Definitions.Parse)
What I came up with atm:
One thing maybe needing explaining is Source
. It defines the priority (opt overrides env, or other way around).
Value-typing is still available:
var (
flagFoo = "foo"
)
_ = hcli.Command{
Flags: []hcli.Flag{
hcli.StringFlag{Options: []string{flagFoo, "f"}},
}
Action: ... {
ctx.SlString(flagFoo)
}
...
}
This provides completion and validation in the IDE, and ctrl-clicking (jump to definition, or show all uses).
Progress at https://github.com/jtagcat/harg/tree/hcli. It's coming together beautifully imo, needs a workweek more (x2 for unused feature creep).
If there is interest, I still would like to make it a future version of urfave/cli.
@jtagcat We have support for persistent flags in v3 right now. So if you mark a flag as persistent it will be available to all subsequent sub commands. if sub command defines same flag it will take precedence over persistent one.
@jtagcat Right now we have 2 competing arg parsers, yours and @meatballhat 's argh. At some point we have to make a decision which path is preferred.
Two requested parses:
In my current project I've built an inefficient args parser which moves flags and positional arguments to their correct positions before sending to app.Run(...).
Example: Given an App
&cli.App{
Commands: []*cli.Command{
{
Name: "function",
ArgsUsage: "<name>",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "command",
},
},
},
},
}
Arguments : prog function functionName --command cmd
Parsed : prog function --command cmd functionName
Arguments : prog --command cmd function
Parsed : prog function --command cmd
Whenever we release current iteration it will be here: https://github.com/taubyte/tau-cli/blob/main/cli/args/move_postfix.go
Above is now released, you can see my tests here: https://github.com/taubyte/tau-cli/blob/main/cli/args/args_test.go
@skelouse sorry for the long delay! I think your requests are accounted for, but I'll definitely want more feedback once this is closer. I've been occupied with other life adventures since ~January-ish but I've got some vacation time coming up 🤞🏼
I have lost focus on this more than once, and IIUC others in threads here have also moved on, so I'd like to propose that this is not a blocker for v3 release. @urfave/cli WDYT?
I agree. We probably handle most of the use cases for a cli utility. Refitting a new parser is a nice engineering project but adds only incremental value
I am closing this as we are able to extend golang flag library to the max extent possible. We have support for pretty much every requirement that was required of a new parser
tl;dr - By switching away from stdlib
flag
, we will be able to support a wider range of capabilities with significantly less workaround code.Much of the code around here is arguably working around stdlib
flag
, for example:FlagSet.Var
)By building on top of stdlib
flag
, we are limited to two categories of string arguments:-
-
flag
🤷🏼There are many other command line argument parsers available in the Go third-party package ecosystem such as:
flag
argparse