Comments (20)
- I will read this more thoroughly tomorrow.
- Nested commands existed since the very early releases.
- Breaking backwards compatibility is an inseparable part of software development, that is what major versions are for :)
from bashly.
Yes, aware of major versions! (I'm a maintainer/project lead of a large OSS project myself!)
In this particular case, consumers are using the script I've created to perform automation tasks, so I'd love to have a forwards-compatible minor release before I bump to a new major and break BC. (This is something I've done in my OSS projects, and it's tended to make the transition to a new major far easier, which has led to fewer support headaches for myself and the other maintainers.)
Thanks for reviewing; curious to see if it's something you're willing or able to tackle! Glad to help test out if you decide to attempt it! (I'd try, but have almost zero Ruby experience!)
from bashly.
Ok.
First of all, my major version comment was a joke :) - I am aware of both your credentials and the need to be backwards compatible wherever possible.
Also - thanks for the detailed ticket, however - I would like to suggest a simpler solution that will hopefully work for you.
The concept is this:
- We define both old and new methods, optionally using either YAML aliases or
import
to define it only once. - The old method will be marked
private
so it can be executed, but not displayed in the--help
text. - The old method will have both a different
filename
and a different function name (using thefunction
option - which does not yet exist). - The file of the old function will
- Print a deprecation message to stderr
- Call the internal function that runs the new one.
Now, to explain the above with files:
# src/bashly.yml
name: cli
commands:
- name: foo
help: New foo
commands:
- &foo-list
name: list
help: New foo list
flags:
- long: --alpha
help: Alpha flag
# Deprecated
- <<: *foo-list
name: foo-list
private: true
filename: deprecated/foo_list_command.sh
function: deprecated_foo_list # this feature does not exist yet
# src/deprecated/foo_list_command.sh
echo "DEPRECATED: Use 'foo list' instead" >&2
cli_foo_list_command "$@"
and output:
$ ./cli foo-list
DEPRECATED: Use 'foo list' instead
This is the new foo list script
args: none
$ ./cli foo list
This is the new foo list script
args: none
$ ./cli
Usage:
cli COMMAND
cli [COMMAND] --help | -h
cli --version | -v
Commands:
foo New foo
from bashly.
Actually - the above suggestion will also allow you to create a separate help with this YAML:
name: cli
commands:
- name: foo
help: New foo
commands:
- &foo-list
name: list
help: New foo list
flags:
- long: --alpha
help: Alpha flag
# Deprecated
- <<: *foo-list
name: foo-list
help: DEPRECATED - use 'foo list' instead
private: true
filename: deprecated/foo_list_command.sh
function: deprecated_foo_list # this feature does not exist yet
and I am also toying with the idea of wrapping the entire concept in one new directive:
- name: foo-list
deprecated: foo list
If you want, I can push a version that supports this function
directive.
Are you using the docker or Ruby version of bashly?
from bashly.
Love these approaches!
The two potential issues I see with using the function
directive are:
- if you were to change the command name, the function name would need to change
- it requires that the person using bashly understand the normalization rules
The first is not a huge problem, and, honestly, both could be cases that documentation would solve.
I use the Docker version, but can also do the Ruby version for testing purposes.
from bashly.
if you were to change the command name, the function name would need to change
Not necessarily, and even if so, this is the niche case of the niche case. Normally, there should be no reason for anyone to use the function
directive. It is just intended to allow edge cases like yours. I definitely consider this "the edge of the bashly universe".
it requires that the person using bashly understand the normalization rules
I am not following. This function name is internal. It will be suffixed by _command
and _usage
and other suffixes as needed.
from bashly.
I am not following. This function name is internal. It will be suffixed by
_command
and_usage
and other suffixes as needed.
I mean it requires that somebody writing a command in bashly understands how something like:
name: cli
commands:
- name: config
commands:
- name: set-default
translates to the functions cli_config_set_default_command
and cli_config_set_default_usage
; i.e., that spaces and punctuation in command and subcommand names get translated to underscores for purposes of generating command and usage functions. That's all I was getting at.
from bashly.
I have just pushed a new branch, if you want to test this approach.
See the instructions on the edge page.
I have not published an edge
release, so you will need to adjust any of the instructions there to pull from the add/command-function
branch.
If using Ruby, make sure you have at least Ruby 2.7.
...and the beauty of it, is that the change cannot be any smaller
from bashly.
Works brilliantly!
A few notes for the docs:
- If you use this functionality, you MUST recreate any filters, arguments, and flags if you want bashly to validate and aggregate them in the same manner as the original command.
- You CAN provide alternate help text, footers, etc. to differentiate the command.
These limitations work great for my purposes, and allow me to accomplish what I need to in terms of preparing a forwards compatible release.
Thanks for the quick turnaround!
from bashly.
Oh, this is slick!
I discovered that I can have a script with front matter that omits the name
attribute. Then in my bashly.yml
:
name: cli
commands:
- name: config
commands:
- name: set-default
import: src/config_set_default_command.sh
# Legacy commands
- name: config-set-default
import: src/config_set_default_command.sh
function: config_set_default_command
footer: $(red "DEPRECATED Use config set-default")
The beauty of this is that I get to re-use the argument, flag, and filter configuration, and help text, but still draw attention to the deprecation. When I'm ready to cut the next major, I just remove all the items after # Legacy commands
.
from bashly.
Ran into my first issue.
name: cli
commands:
- name: utility
commands:
- name: exec
import: src/utility_exec_command.sh
# Legacy commands
- name: exec
import: src/utility_exec_command.sh
function: utility_exec_command
and where I have a src/utility_exec_command.sh
with YAML frontmatter containing the help, arguments, and flags, but no name
element.
When I run bashly generate
with this, it generates a src/exec_command.sh
stub, even though I've indicated it should use a function.
from bashly.
Excellent.
Since the function
directive is not necessarily designed for a deprecation situation, I will probably keep the docs simple, but add an example that shows a "real world" scenario, like deprecation.
I would love your opinion on the docs and example, once I complete them.
from bashly.
Can you paste a minimal version of what you have in src/config_set_default_command.sh
?
it will help my example building.
from bashly.
When I run bashly generate with this, it generates a src/exec_command.sh stub, even though I've indicated it should use a function.
Specifying function
does not change anything other the name of the internal function.
Like any other command, it must have a file, and you can use filename
as I showed in my original example.
And do not use _command
suffix in your function: utility_exec_command
- it will be added internally.
I suggest you test with my initial minimal example, and review the generated script to understand what it does.
from bashly.
You might be able to get away with this:
# src/bashly.yml
name: cli
commands:
- name: foo
help: New foo
commands:
- &foo-list
name: list
help: New foo list
flags:
- long: --alpha
help: Alpha flag
# Deprecated
- <<: *foo-list
name: foo-list
help: DEPRECATED
private: true
filename: foo_list_command.sh
function: deprecated_foo_list
Notes:
- Using the same filename as the non deprecated variant
- Using a different function name
Although I still recommend my original concept - of allowing it to have its own file, and using this file to a) print deprecation and b) call the internal new function
from bashly.
Wonderful - adding filename
indeed worked. Your example also taught me about the private
flag, which is perfect here as it means if they know the command and use it, it will work, but it will stop showing up in the lists.
I'd also not understood the &
and *
signifiers, so after trying your example, I looked those up in the YAML specification, and those help me simplify even more. Thanks for the great examples!
So yes,the combination of the new function
directive, along with anchors and the private
directive gives me exactly what I'm looking for! When I have a scenario where the associated filename will differ, I can use filename
to override it and prevent generation of another file if desired (or not, and use that file to help print deprecation notices).
from bashly.
This is what I was hoping to hear. I will work on the docs and example.
Will make a new release later today or tomorrow.
Thanks for raising an interesting issue.
from bashly.
Added a laser-focused example for command.function
I avoided use of imports, YAML aliases and filenames in order to just clarify this one concept.
from bashly.
Documentation examples look great, @DannyBen !
I'll likely write up a blog post about my use case once the feature is released and I've tested it with end users.
Thanks again!
from bashly.
Version 0.8.9 is released to both Rubygems and Docker Hub.
- Documentation:
command.function
- Example:
command-function
Thanks for raising this issue, I hope the solution works well for your use case.
from bashly.
Related Issues (20)
- Presence of `header.sh` breaks executable HOT 5
- How to access permanent environment variable in ./zshrc or ./bashrc HOT 3
- Automation Tools
- test/approve template missing HOT 3
- How can I install this on CentOS ? HOT 1
- Allow flags for commands that have subcommands HOT 20
- Flags not shown in help
- Is there a way to add alias "help" to fixed_flags_filter ? HOT 5
- Is there a way to put cli_usage to custom header? HOT 1
- Dafault environment_variables should be accessible in initialize script HOT 3
- Send errors to stderr HOT 7
- Upgrade template & utility functions HOT 3
- Only commands can be imported? HOT 4
- Is there a way to create global flags? HOT 17
- Issues with `normalize_input` and `catch_all` HOT 14
- Ambiguous error messages HOT 8
- Make invalid `expose` warn instead of error HOT 8
- Error when splitting configuration in sub/nested commands HOT 5
- Ambiguous error messages HOT 11
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from bashly.