Coder Social home page Coder Social logo

Comments (20)

DannyBen avatar DannyBen commented on September 22, 2024
  1. I will read this more thoroughly tomorrow.
  2. Nested commands existed since the very early releases.
  3. Breaking backwards compatibility is an inseparable part of software development, that is what major versions are for :)

from bashly.

weierophinney avatar weierophinney commented on September 22, 2024

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.

DannyBen avatar DannyBen commented on September 22, 2024

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:

  1. We define both old and new methods, optionally using either YAML aliases or import to define it only once.
  2. The old method will be marked private so it can be executed, but not displayed in the --help text.
  3. The old method will have both a different filename and a different function name (using the function option - which does not yet exist).
  4. 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.

DannyBen avatar DannyBen commented on September 22, 2024

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.

weierophinney avatar weierophinney commented on September 22, 2024

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.

DannyBen avatar DannyBen commented on September 22, 2024

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.

weierophinney avatar weierophinney commented on September 22, 2024

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.

DannyBen avatar DannyBen commented on September 22, 2024

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.

weierophinney avatar weierophinney commented on September 22, 2024

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.

weierophinney avatar weierophinney commented on September 22, 2024

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.

weierophinney avatar weierophinney commented on September 22, 2024

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.

DannyBen avatar DannyBen commented on September 22, 2024

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.

DannyBen avatar DannyBen commented on September 22, 2024

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.

DannyBen avatar DannyBen commented on September 22, 2024

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.

DannyBen avatar DannyBen commented on September 22, 2024

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.

weierophinney avatar weierophinney commented on September 22, 2024

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.

DannyBen avatar DannyBen commented on September 22, 2024

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.

DannyBen avatar DannyBen commented on September 22, 2024

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.

weierophinney avatar weierophinney commented on September 22, 2024

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.

DannyBen avatar DannyBen commented on September 22, 2024

Version 0.8.9 is released to both Rubygems and Docker Hub.

Thanks for raising this issue, I hope the solution works well for your use case.

from bashly.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.