-
Notifications
You must be signed in to change notification settings - Fork 19
Should Document extend Command?
#70
The root CLI is a command.
Note
Most CLI frameworks model it this way:
- alecthomas/kong (Go): the root CLI and commands are all modeled as Go structs using Kong's grammar
- oclif/oclif (TS): the root CLI and commands are all modeled as classes that extend
Command - spf13/cobra (Go): the root command and subcommands are all instances of
*cobra.Command - clap-rs/clap (Rust): the root and subcommands are all
clap::Commandvalues composed into a tree - remkop/picocli (Java): the root command and subcommands are all
@Command-annotated classes/objects - commander-js/commander.js (JS): the "program" and its subcommands are all
Commandobjects - CLIUtils/CLI11 (C++): the root app and subcommands are all
CLI::Appobjects (subcommands are Apps too!)
There are exceptions —
- urfave/cli (Go): the root is a
cli.Appthat contains[]cli.Command - dotnet/command-line-api (C#): the root is a
RootCommandrather than justCommand - pallets/click (Python): the root is typically a
click.Grouprather than a plainclick.Command
— but even among these, it's typical for the root command to extend the branch command (System.CommandLine.RootCommand extends System.CommandLine.Command and click.Group extends click.Command)
Both Document and Command have fields for arguments, options, commands, examples, etc. However Document puts its name and description under info.
CliInfo subtly diverges from Command:
- Why does
CliInfohave atitlebutCommandhas aname?- Can we settle on
name?
- Can we settle on
- Why does
CliInfohave asummaryand adescriptionbutCommandonly has adescription?- How do
summaryanddescriptiondiffer? - Is
Command'sdescriptionintended to be the one-line string displayed in a menu of commands?
- How do
Command's hidden flag is perhaps meaningless for the root command, but other than that, I think the spec would be strengthened by modeling Document as a superset of Command.
All reactions
Implemented in #85
Replies: 4 comments 9 replies
Here's how this change might look: #74
All reactions
I agree that the current form is awkward (and I've implemented code against the spec). But the solution I would propose would be:
- Still have a Document Object with its
info,conventions, etc. - But the Document Object should have a required command field that is a single Command Object and this Command Object should be the root of the specification.
It think this will allow for important meta information for the spec as a whole document but remove the awkwardness of have two object that basically need to understood identically in processing code.
All reactions
I'm OK with this approach too.
To be clear, you're proposing something like moving arguments, options, commands, exitCodes, examples, interactive, and metadata off of Document and replacing them with a field, root, of type Command?
Would you pull title, summary, and description out of CliInfo as well, in favor of name, summary, description on Command?
All reactions
|
Yes, that is it:
And all the removed parts would be put in that root As for the overlap between between the data in the CliInfo Object
changes:
semantics:
@boblail does that makes sense? Another thing we could consider is the remove the |
All reactions
-
👍 1
I have nothing against this, but would the command name be required then? Is there some way that we can express that all commands must have a name, unless it's the root command?
All reactions
@patriksvensson, yes the command name would still be required. The root would basically be the root command. So for example, the tool is kubectl the root command name is kubectl, it would then have a list of sub-commands like get, apply, delete, etc. The info object title would be a separate bit of metadata. It would not have to actually match the in terminal command. For example maven could be used in the title since it is the title of the software package (eg. sudo yum install maven) but the in terminal command is mvn (that is what you type in your shell). So the root command name would be mvn.
Like I said, I'm writing TUI that uses opencli and this change would make things much more ergonomic. (I'm basically doing this under the hood in my code. I convert the Document object to a Command Object and process just the Command Object and it works very well).
Here is an example:
---
"$schema": https://json-schema.org/draft/2020-12/schema
"$id": OpenCLI_kubectl.json
opencli: '0.1'
info:
title: kubectl
version: 1.29.0
summary: Command-line tool for controlling Kubernetes clusters
description: kubectl controls Kubernetes clusters. You can deploy applications,
inspect and manage cluster resources, and view logs.
contact:
name: Kubernetes Contributors
url: https://kubernetes.io/docs/reference/kubectl/
email: kubernetes-dev@googlegroups.com
license:
name: Apache License 2.0
identifier: Apache-2.0
conventions:
groupOptions: true
optionSeparator: " "
command:
name: kubectl
options:
- name: "--kubeconfig"
description: Path to the kubeconfig file to use
- name: "--context"
description: The name of the kubeconfig context to use
commands:
- name: get
description: Display one or many resources
commands:
- name: send
description: Test Sub-Sub-Command
arguments:
- name: resource
required: true
description: The RESOURCE type argument tells kubectl get what kind of Kubernetes objects to get (list and/or describe), such as pods, services, or deployments.
- name: name
required: false
options:
- name: "--namespace"
description: If present, the namespace scope for this CLI request
arguments:
- name: "NAMESPACE"
description: the namespace scope for this CLI request
- name: "--output"
aliases:
- "-o"
description: 'Output format. One of: json|yaml|wide'
arguments:
- name: "FORMAT"
acceptedValues:
- "json"
- "yaml"
- "wide"
- name: "--output"
aliases:
- "-o"
description: 'Output format. One of: json|yaml|wide'
- name: "--output2"
aliases:
- "-o"
description: 'Output format. One of: json|yaml|wide'
- name: "--output3"
aliases:
- "-o"
description: 'Output format. One of: json|yaml|wide'
- name: "--output4"
aliases:
- "-o"
description: 'Output format. One of: json|yaml|wide'
- name: "--output5"
aliases:
- "-o"
description: 'Output format. One of: json|yaml|wide'
- name: "--output6"
aliases:
- "-o"
description: 'Output format. One of: json|yaml|wide'
- name: apply
description: Apply a configuration to a resource by filename or stdin
options:
- name: "-f"
description: Filename or directory to apply
required: true
- name: delete
description: Delete resources by filenames, stdin, resources and names, or by label
selector
arguments:
- name: resource
required: true
- name: name
required: false
examples:
- kubectl get pods
- kubectl apply -f deployment.yaml
- kubectl delete pod nginx
interactive: false
All reactions
@patriksvensson, does @devin-fisher's response sound reasonable to you?
It feels like we're 90% aligned here and can land this change with only a little more effort!!
All reactions
@boblail @devin-fisher Sorry for the radio silence. It has been some hectic weeks for me, and I wanted to respond when I had some dedicated time.
Yes, I think that this approach sounds reasonable. It's not perfect, but I understand where you all are coming from, and I think this makes sense.
Btw, completely random, I've set up a OpenCli group in the Spectre.Console Discord server: https://discord.com/invite/DxqCxpmA2K
All reactions
-
❤️ 1
Created issue #86