GitXplorerGitXplorer
b

simplecobra

public
36 stars
3 forks
0 issues

Commits

List of commits on branch main.
Unverified
38e3a117deadd9c6bcc3241e4180e7d9abdb05e2

deps: Update all direct dependencies

bbep committed a year ago
Verified
40c9e554ae0f8567357ff5e7c2af1d83550fe43c

github: Format workflow file

bbep committed a year ago
Verified
9c01cfa2d0bdbaf470637d218d3953c9c2c39c67

github: Bump Go test versions

bbep committed a year ago
Unverified
b2ef9d0a2a9f824f7363cce001e2314261732fba

Bump GitHub workflow actions to their latest versions

ddeining committed 2 years ago
Unverified
64fc57c3b2351c8d441abaecd64865d9d53d84ec

Fix handling of aliases

bbep committed 2 years ago
Verified
90e1e10023ebf9296af26e87d649ba307bb752d6

Update README.md

bbep committed 2 years ago

README

The README file for this repository.

Tests on Linux, MacOS and Windows Go Report Card codecov GoDoc

So, Cobra is a Go CLI library with a feature set that's hard to resist for bigger applications (autocompletion, docs and man pages auto generation etc.). But it's also complex to use beyond the simplest of applications. This package was built to help rewriting Hugo's commands package to something that's easier to understand and maintain.

I welcome suggestions to improve/simplify this further, but the core idea is that the command graph gets built in one go with a tree of struct pointers implementing a simple Commander interface:

// Commander is the interface that must be implemented by all commands.
type Commander interface {
	// The name of the command.
	Name() string

	// Init is called when the cobra command is created.
	// This is where the flags, short and long description etc. can be added.
	Init(*Commandeer) error

	// PreRun called on all ancestors and the executing command itself, before execution, starting from the root.
	// This is the place to evaluate flags and set up the this Commandeer.
	// The runner Commandeer holds the currently running command, which will be PreRun last.
	PreRun(this, runner *Commandeer) error

	// The command execution.
	Run(ctx context.Context, cd *Commandeer, args []string) error

	// Commands returns the sub commands, if any.
	Commands() []Commander
}

The Init method allows for flag compilation, referencing the parent and root etc. If needed, the full Cobra command is still available.

There's a runnable example in the documentation, but the gist of it is:

func main() {
	rootCmd := &rootCommand{name: "root",
		commands: []simplecobra.Commander{
			&lvl1Command{name: "foo"},
			&lvl1Command{name: "bar",
				commands: []simplecobra.Commander{
					&lvl2Command{name: "baz"},
				},
			},
		},
	}

	x, err := simplecobra.New(rootCmd)
	if err != nil {
		log.Fatal(err)
	}
	cd, err := x.Execute(context.Background(), []string{"bar", "baz", "--localFlagName", "baz_local", "--persistentFlagName", "baz_persistent"})
	if err != nil {
		log.Fatal(err)
	}

	// These are wired up in Init().
	lvl2 := cd.Command.(*lvl2Command)
	lvl1 := lvl2.parentCmd
	root := lvl1.rootCmd

	fmt.Printf("Executed %s.%s.%s with localFlagName %s and and persistentFlagName %s.\n", root.name, lvl1.name, lvl2.name, lvl2.localFlagName, root.persistentFlagName)
}

Differences to Cobra

You have access to the *cobra.Command pointer so there's not much you cannot do with this project compared to the more low-level Cobra, but there's one small, but important difference:

Cobra only treats the first level of misspelled commands as an unknown command with "Did you mean this?" suggestions, see see this issue for more context. The reason for this is the ambiguity between sub-command names and command arguments, but that is throwing away a very useful feature for a not very good reason. We recently rewrote Hugo's CLI using this package, and found only one sub command that needed to be adjusted to avoid this ambiguity.