Menu Commander

Gem Version Build Status Maintainability


Easily create menus for any command line tool using simple YAML configuration.



Demo


Installation

$ gem install menu_commander

Usage

Menu Commander adds the menu command line tool to your path. When running it without arguments, it will look for a menu.yml file in the current directory, and will provide you with a menu to execute any shell command.

A basic menu configuration file looks like this:

# menu.yml

# Using %{variables} in a command will prompt for an input when executed
menu:
  hello: echo hello
  hi: echo hi %{name}

# Define sub menus for any %{variable} that was defined in the command
args:
  name:
  - Harry
  - Lloyd

Then, start the menu by running:

# Start the menu with ./menu.yml
$ menu

# Start the menu with ./some-other-file.yml
$ menu some-other-file
  • When a menu or sub menu has more than 10 items, it will become paginated and a search filter will be added.
  • Pressing Home from any nested menu will go back to the first menu.
  • Pressing Page Up from any nested menu will go back to the previous menu.
  • Pressing Ctrl+C will exit from the menu.

All features have an example configuration in the examples folder. To run an example, simply execute menu EAXMPLE_NAME form within the examples folder, where EXAMPLE_NAME is the name of the YAML file without the extension.

Minimal menu requirements

The only requirement for a minimal menu is to have a menu definition with key: command to run.

menu:
  hello: echo hello
  whoami: whoami

See: examples/minimal.yml

Argument sub-menus

Using %{variables} in a command will prompt for an input when executed. The sub-menu for that input is specified in the args definition.

menu:
  server: rails server --env %{environment}
  test: RAILS_ENV=%{environment} rspec

args:
  environment:
    - staging
    - production

See: examples/args-array.yml

In case the argument array contains only one array element for a given variable, it will be automatically used without prompting the user.

This is useful when you need to define variables that repeat multiple times in your menu.

args:
  server: [localhost]

See: examples/args-static.yml

Using key: value pairs in the args menu will create a sub-menu with labels that are different from their substituted value:

menu: 
  server: rails server --env %{environment}
  test: RAILS_ENV=%{environment} rspec

args:
  environment:
    Staging Environment: staging
    Production Environment: production

See: examples/args-hash.yml

In order to obtain the sub-menu items from a shell command, simply provide the command to run, instead of providing the menu options. The command is expected to provide newline-delimited output.

menu:
  show: cat %{file}
  edit: vi %{file}

args:
  file: ls 

See: examples/args-shell.yml

Free text input

When using a %{variable} that does not have a corresponding definition in the args section, you will simply be prompted for a free text input:

menu:
  release: 
    echo %{version} > version.txt &&
    git tag v%{version}

See: examples/args-free-text.yml

Nested menus

You can nest as many menu levels as you wish under the menu definition.

menu:
  docker:
    images: docker images ls
    containers: docker ps -a
    stack:
      deploy: docker stack deploy -c docker-compose.yml mystack
      list: docker stack ls

  git:
    status: git status
    branch: git branch

See: examples/args-nested.yml

Split menu into several files

Each menu configuration file can include any number of additional YAML files inside it. This is useful when:

  • Your menu configuration file becomes too long, and you wish to split it to separate logical units.
  • You have multiple menu files, and you want to include common configuration in each of them.

This is done by using the extends option:

# examples/extend.yml
extends: extend-parent.yml

menu:
  hello: echo hello
  hi: echo hi %{name}

args:
  name: [Harry, Lloyd]
  server: [example.com]

See: examples/extend.yml

The below configuration will be merged into the above menu:

# examples/extend-parent.yml
menu:
  ping: ping %{server}

args:
  server: [localhost, google.com]

See: examples/extend-parent.yml

Multi-line commands

Providing an array to a menu, will join the array with '&&' to a single command. Alternatively, you can use a simple YAML multi-line string.

menu:
  deploy:
    - run tests
    - git commit -am "automatic commit"
    - git push

  alternative: >
    run tests &&
    git commit -am "automatic commit" &&
    git push

See: examples/multiline.yml

You can tweak several aspects of the menu by adding an options section in your YAML file.

# Optional menu configuration
options:
  # Show header text
  header: Hello

  # Marker to show as the suffix of items that have submenus
  # Use false to disable
  submenu_marker: " ..."

  # Menu selection marker
  select_marker: ">"

  # Menu title marker
  title_marker: "-"

  # When a menu has more items than page_size, add pagiation
  # Default 10
  page_size: 2

  # When to show search filter
  # yes      = always show
  # no       = never show
  # auto     = show only when there aare more items than page_size (default)
  # <number> = show only when there are more items than <number>
  filter: yes

  # When arg lists generate one item only it is auto-selected by default.
  # Set this to false to disable this behavior
  auto_select: false

  # Show the command after execution
  echo: true

  # Marker to use when echoing the command and it was successful
  echo_marker_success: "==>"

  # Marker to use when echoing the command and it failed
  echo_marker_error: "ERROR ==>"

See: examples/options.yml

By default, menu files are looked for in the current working directory.

You may instruct Menu Commander to look in additional locations by setting the MENU_PATH environment variable to one or more paths. Note that when using this method, Menu Commander will not look in the current directory, unless you include it in MENU_PATH, like this:

$ export MENU_PATH=.:$HOME/menus:/etc/menus

If you wish this setting to be permanent, add it to your .bashrc or your preferred initialization script.