cli
cli copied to clipboard
Release 3.x meta/discussion
This is a meta-issue meant to track the work in other issues targeting the Release 3.x milestone.
benevolent takeover notes
Hiii I'm working on getting many maintenance tasks addressed including taking over this issue from @coilysiren ππΌ who is busy with other life adventures β€οΈ ~ @meatballhat
~I know 2.0 isn't out yet π (see https://github.com/urfave/cli/issues/826) but~ I wanted to create a tracking issue for the 3.x series.
Here's some breaking/difficult changes we want to consider for V3:
- [x] #1498
- [x] #1489
- [x] Replace code generation with generics where possible/maintainable (#1512 )
- [x] ~#1058~
- [x] #1055
- [x] #1695
- [x] ~Improve testability of
*cli.Context(via https://github.com/urfave/cli/issues/833#issuecomment-574171962)~ (conflicts with #1587) - [x] remove
EnableBashCompletionin favor ofCompletionShells - [x] ~consider a different release method (eg. not just merging to
master) via https://github.com/urfave/cli/issues/952#issuecomment-561062742~ (this was changed in v2) - [x] ~make flags "global" by default, to some degree~ (this was added into v2)
- [x] #753
- [x] #1071
- [x] ~Cleanup error handling. Make multiError implement ExitCoder~
- [x] #1663
- [x] #1890 [^1]
- [x] #1775
- [ ] #1774
- [ ] #1662
- [ ] #1531
- [ ] #1885
- [ ] #1891
Please suggest more! I'll add them to this OP
Update ~January 2023: Progress! And yet, yeah, wish lists balanced with releasing usable software is hard to do. If you're the kind of human who finds themselves reading this, there's a v3.0.0-alpha2 tag now, which should play nicely with go get github.com/urfave/cli/v3@latest. Feedback wanted! β€οΈ ~ @meatballhat
[^1]: The documentation-related code that depends on github.com/russross/blackfriday/v2 and github.com/cpuguy83/go-md2man/v2 has been shown to add significant bulk to compiled outputs
Regarding the API we could split it up into an internal and external one. Everything which should not be imported externally would go into the internal folder.
I would love to see fish shell completion support. π
Regarding the API we could split it up into an internal and external one. Everything which should not be imported externally would go into the internal folder.
I'm a big fan of tossing tons of code into internal, its a really cool pattern
I would love to see fish shell completion support.
For anyone else reading, here's a ticket for that => https://github.com/urfave/cli/issues/351
Oh! I see now that we call the flag EnableBashCompletion instead of CompletionShells. I'll add this to the OP π
Since v2 was never officially released, it might be okay to make minimal breaking changes to the v2 branch before merging and releasing it?
I'm happy either way, but if that's more convenient then it should be okay since people know that v2 has not been released yet and so it's okay to make some breaking changes before its officially stable
^ I'm cleaning up the issues about altsrc ^_^
I'm gonna drop off of guidance for this one - I've got a few other things I can focus on
Commenting here, as it seems like you guys want everything related to altsrc in this issue. Thanks for all the work you've put in on a great package!
I've got two questions about usage of altsrc to read flags from a config file:
-
It seems like
altsrc.InitInputSourceWithContextreturns an error if there are some flags in the config file which aren't defined, even though said flags aren't marked asRequiredwhen defining them. Is this the intended behavior, or am I doing something wrong here? -
If marking a flags as
Required, I get an error when executing my command when defining the flag in my config file, but not at the CLI invocation point. This seems like a bug to me, or am I confused about something here?
Commenting here, as it seems like you guys want everything related to
altsrcin this issue.
Yes please!
To your questions - I'm not sure yet what the answers are, but I will totally investigate o7!
Something I want to consider for v3: making all flags "generic", so you only have a "Flag" type and we use type assertions internally to turn it into StringFlag
It's looking like I'm not gonna have time to do a bunch of work on the V3 PR anytime in the near / mid future, so I'd like to hand it off to anyone else => https://github.com/urfave/cli/pull/936
Anyone is - who is so inclined - is also free to help guide this V3 tracking issue β¨
Improve testability of *cli.Context. A couple of potential options:
- Create a
cli.NewTestContextor similar that could give us the ability to configure private fields likeflagSetthat are currently impossible to set externally. - Make
cli.Contextan interface, socli.ActionFuncand friends could accept any type that implements those methods, rather than only a*cli.Context.
See discussion: https://github.com/urfave/cli/pull/1039#issuecomment-574089223
Improve testability of
*cli.Context. A couple of potential options:
- Create a
cli.NewTestContextor similar that could give us the ability to configure private fields likeflagSetthat are currently impossible to set externally.- Make
cli.Contextan interface, socli.ActionFuncand friends could accept any type that implements those methods, rather than only a*cli.Context.See discussion: #1039 (comment)
This is an amazing idea
taking a pass at reducing our public api surface, so there's less ways we can accidentally break people's code
Closed a while back due to staleness, but removing unused fields such as Compiled are a good candidate for reducing the public API surface: https://github.com/urfave/cli/issues/753
Interesting Feature: Make the library modular.
As mentioned in issue #1055 our library has grown quite large considering a plethora of features we are offering. While this is good, not all users are using every feature. This means that people are downloading a bunch of dependencies and not ending up using them.
Having a modular library would allow us make the core smaller and have the other features as addons or plugins.
When we were planning to release v2, there was some talk about how to go forward with the release. There were several chains of thought around the matter. I think we are at a point where we would soon release v3. I want everyone's help in deciding how are we going to go forward with the release.
Do we cut out a separate v2 branch like the v1 and continue bug fixes and other development etc for v2 over there and master becomes v3?
Does anyone have any other idea?
I also think that we should start with a development branch for v3 where we can merge the changes and keep it up-to-date with master until we decide to make the final release. I will check out this branch from master and point my PRs to that branch
FWIW, Compiled can be useful when confirming a debug/canary version of software: typically if the version isn't YET bumped, the only macroscopic difference is compile date.
I see more use though of the text-based ISO8601/RFC3339 of the commit date in our typical version output so that repeated builds of the same commit reproduce the same date.
var (
BUILD_DATE string
)
...
if buildTime, err := time.Parse("2006-01-02T15:04:05-07:00", BUILD_DATE); err == nil {
app.Compiled = buildTime
}
Mind you, requires a build-time flag (based on git show --no-patch --no-notes --pretty='%cI' HEAD) to populate that, which is why I silently consume the error on parse to not set the date.
Might not appear useful, but next time you're wondering whether CI/CD pushed the right thing to canary, you might remember this comment.
Drop explicit support for the MultiError interface in HandleExitCoder, and instead make the private multiError type implement ExitCoder.
Right now MultiError as an interface seems to be supported specifically for for the internal multiError type, but requires logic in the default handler, HandleExitCoder, which doesn't really seem ideal. Instead, if the private type happened to implement ExitCoder itself, the generic default handler can be simplified to only care about ExitCoders.
The main downside to this approach is that a MultiError couldn't easily conditionally implement an interface. Imagine a method like this:
func (m multiErr) ExitCode() int {
var exitCoder ExitCoder
for _, err := m.Errors() {
if errors.As(err, &exitCoder) {
return exitCoder.ExitCode()
}
}
// No error in the multi-error was an exit coder, but the multi-error still is!
// We have to return something here...
return -1
}
To support that kind of case, we'd probably want to change the behavior of HandleExitCoder to specifically check for exit codes >=0 (or >0?), so a multi-error or other type of error could conditionally opt in-or-out of being an ExitCoder.
One other caveat is that because of the current implementation, any error implementing MultiError is actually treated as an ExitCoder and causes the application to exit with a status of 1, even if there is no ExitCoder in the MultiError. We probably don't want that to be the case.
Changing that behavior is technically a breaking change, so we wait until v3.
Related: #1089
Is there any appetite for re-organizing code ? I have begun some basic cleanup is #1264 but my main goal is to cleanup and app.go and command.go files. There is a lot of duplicate code in these two files which makes it hard to maintain. Any new feature needs to be added in two places. Its a nightmare for maintainers. What I would suggest is to have all the processing logic in command.go. App would create an internal rootCmd(of *Command) and run with a default parent context. So all the logic is within Command and there is no need to switch from app to command and then back to app. All this subcommand logic in App goes away. Everything becomes neatly compacted into one function in Command. All the helpTemplates collapse into one and will be rendered with a Command object. Main app commands/flags would be rendered with rootCmd and command specific stuff with the corresponding Command object. It becomes all consistent and we wouldnt even have to change the user level API. All this happens behind the scenes without user having to change any code. I have a partial implementation of this which I'm testing out but dont want to proceed if it ends up getting rejected.
Thank you for your interest and contributions, I really appreciate the effort you've put in so far.
Is there any appetite for re-organizing code ?
Absolutely. If there are ways to improve the code base that aren't breaking changes, we can slot those into v2, even if they're major refactors. I agree that there are lots of things we have that are less than ideal, and improvements are always welcome.
I have a partial implementation of this which I'm testing out but dont want to proceed if it ends up getting rejected.
Unfortunately I think the time that's actively been put into maintenance is less than I'd like it to be, which I say as a maintainer that wishes I spent more time on responding to issues/PRs. There's a very real possibility that if you put something wonderful together, it gets overlooked rather than rejected.
The best candid advice I can give isβif you aren't prepared to wait for a response, unfortunately now might not be the best time to contribute. I'll do my best to pitch in where I can, but for v2 changes you'll need at least two maintainers to approve. If you do want to contribute, a series of smaller changes are a lot easier to get merged in than trying to push a lot of things together in the same PR, we will definitely ask for unit tests for anything that's new or changing that doesn't already have tests, and absolutely feel free to ping multiple times if you don't get a response.
I realize that's probably not the best answer to hear, but it's the one I can give right now. Thank you again for your time here.
@rliebz These are my thoughts exactly. I've been going through the PRs and issues and noticing the long lead time in responses/approvals. I dont mind waiting a month or so for a comment and 2-3 months for an approval. That absolutely fine with me. One of my PRs in kustomize has been "stuck" since August of last year. So I totally understand where you're coming from. Maintainers have so many other priorities that they cannot afford to spend time daily on one project.
If you look at my PRs for urfave/cli I've tried to break it down into very very small pieces, with unit tests included, so that it gives the reviewers confidence that nothing has broken and only some existing bug has been fixed. Without tests there is no way a maintainer can figure out just from the code(for non-trivial changes) whether the bug is fixed or not. I'm a big fan of unit tests. In fact I write the unit tests first to make sure the problem exists and then make changes to the code and then rerun all tests to make sure nothing is broken.
My refactoring of app/command is a very slow process. Sometimes I make a change and it works and sit on it for a while. After a couple of days a different idea pops up and I try to incorporate that. So I'm in no hurry to finish it asap even though that would be great :) My goal is to make sure that there are zero user facing changes for this. Its a big task but I think its worth it in the long run to make the code more modular and more maintainable.
Thank you for your comments and time. Appreciate it.
Hey @lynncyrin (or maybe @rliebz?)! Could you kindly consider adding something so we can choose between how args and flags are parsed? There's a discussion over at #1113.
Not sure this is the right place to ask for that. I do apologise in case it isn't.
The status of a 3.X release is pretty unlikely right now, since the maintenance overhead is probably higher than we have folks who can support it (although we haven't dropped support for v1 yet!).
That said we are still accepting backward-compatible contributions for v2, so definitely feel free to open or comment on individual issues. This issue in particularis probably best suited for discussing potential changes we might make as breaking changes if the situation changes in the future.
For anyone happening across this end of the discussion, I'm working on building up my understanding of this whole situation and I'm hoping to have an updated roadmap for 3.X in the OP in the next few days π€πΌ
@meatballhat im also open to have a zoom or google meets meeting for us to get together and plan the future of this library.
π lol yes busy with other life adventures
FWIW this is an abandoned refactor: https://github.com/rancher/spur/
@meatballhat im also open to have a zoom or google meets meeting for us to get together and plan the future of this library.
Hello, @asahasrabuddhe! Sorry for not seeing this reply earlier π© If you're able, can we coordinate meeting via the team discussions? https://github.com/orgs/urfave/teams/cli
Updated requirements for 3.X release
- [ ] New flag parser, written from ground up to overcome golang flag limitations
- [ ] Remove public fields in App/Command structs and replace with an Options interface for configuration
- [ ] Single Flag interface which encapsulates all the functions in the other *Flag interfaces, Required(), Category() etc etc
- [ ] Target go 1.18+ as a minimum
- [ ] Remove generated flag code and replace with generics,
- [ ] Any standard type flags with also have *Slice types by utilizing generics.
- [ ] Minimal number of files, not including test files, app.go, command.go, context.go, help.go and types.go
- [ ] Collapse redundant execute code in app.go/command.go . Consider using an App.RootCommand to handle App.run