Skip to content

fredbi/go-cli

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lint CI Coverage Status Vulnerability Check Go Report Card

GitHub tag (latest by date) Go Reference license

go-cli

This repo exposes a few utilities to (i) build command-line utilities with (ii) flexible configurations on top of 3 great libraries: github.com/spf13/cobra, github.com/spf13/viper, and github.com/spf13/pflag.

TL,DR: this is not yet another CLI-building library, but rather a mere wrapper on top of cobra to use that great lib with a functional style.

CLI

Example for CLI

Sample CLI-building code. This example is taken from one of the testable examples.

Notice our main objectives here:

  • no globals
  • inline flag registration & binding
  • access to settings using viper only
package main

import (
    "fmt"

    "github.com/fredbi/go-cli/cli"
    "github.com/spf13/cobra"
)

const (
    // viper config keys
	keyLog      = "app.log.level"
	keyDry      = "run.dryRun"
)

func main() {
    // no global vars, no init() ...
	if err := RootCmd().Execute(); err != nil {
		cli.Die("executing: %v", err)
	}
}

// RootCmd builds a runnable root command
func RootCmd() *cli.Command {
	return cli.NewCommand(
        // your usual cobra command, wrapped as a function
		&cobra.Command{
			Use:   "example",
			Short: "examplifies a cobra command",
			Long:  "...",
			RunE:  rootRunFunc,
		},
        // flag bindings
        // {flag name}, {the flag type is inferred from the default value}, {flag help description}
		cli.WithFlag("dry-run", false, "Dry run",
			cli.BindFlagToConfig(keyDry), // flag bindings to a viper config
		),
        // a flag inherited by subcommands
		cli.WithPersistentFlag("log-level", "info", "Controls logging verbosity",
			cli.BindFlagToConfig(keyLog),
		),
        // apply viper config to the command tree
        // command binding to a viper config -> config will be available from context
		cli.WithConfig(cli.Config()),
	)
}

// rootRunFunc runs the root command
func rootRunFunc(c *cobra.Command, _ []string) error {
    // retrieve injected dependencies, create new empty viper registry if unresolved
	cfg := injectable.ConfigFromContext(c.Context(), viper.New)

	fmt.Println(
		"example called\n",
		fmt.Sprintf("dry-run: %t\n", cfg.GetBool(keyDry)),
		fmt.Sprintf("log level config: %s\n", cfg.GetString(keyLog)),
	)

	return nil
}

Goals

The cli packages proposes an opinionated approach to building command-line binaries on top of github.com/spf13/cobra.

There are a few great existing libraries around to build a CLI. I believe that cobra stands out as the richest and most flexible, as CLIs are entirely built programmatically.

cobra is great, but building CLIs again and again, I came to identify a few repetitive boiler-plate patterns.

So this module reflects my opinions about how to build more elegant CLIs, wich abide by 12-factor out-of-the-box, with more expressive code and less low-level tinkering.

Feedback is always welcome, as opinions may evolve over time... Feel free to post issues to leave your comments and/or proposals.

More detailed design goals

Configuration

The config package proposes an opinionated approach to dealing with config files on top of github.com/spf13/viper.

It exposes configuration loaders which know about the deployment context (e.g a deployment environment such as dev, production) and secrets.

Although developped primarily to serve a CLI, this package may be used independently.

Example: loading a config

Other examples are available here.

import (
	"fmt"
	"log"

	"github.com/fredbi/go-cli/config"
)

...

// load and merge configuration files for environment "dev"
cfg, err := config.Load("dev", config.WithMute(true))
if err != nil {
	log.Fatalf("loading config: %w", err)

	return
}

Goals

This describes my approach to configuration. We want to:

  1. retrieve a config organized as a hierarchy of settings, e.g. a YAML document
  2. merge configuration files with environment-specific settings
  3. merge configuration files with secrets, usually these are environment-specific
  4. clearly isolate and merge default settings
  5. applications to be able to consume the settings from a single viper configuration registry

In addition,

  • we want the hierarchy to be agnostic to the environment context
  • most of the time, we don't want env-specific sections to propagate to the app level (e.g. in the style of .ini sections)

In our code, we should never check for a dev or prod specific section of the configuration.

Supported format: YAML, JSON

Supported file extensions: "yml", "yaml", "json"

See other examples

More detailed design goals

Folders structure for configurations

By default we have:

# <- root configuration
{base path}/config.yaml
            # <- environment-specifics folder
            config.d/
                     # <- extra configuration to merge
                     config.yaml
                     # <- possibly with a modified name: config.*.yaml
                     config.default.yaml
                     # <- configuration to merge for environment
                     {environment}/config.yaml
                     # other environment-specifics ....
                     {...}/config.yaml

Here is an example

When using default settings for this module (these are configurable), the base path is defined by the CONFIG_DIR environment variable.

Secret configurations:

{base path}/secrets.yaml
            config.d/
                     # <- secrets to merge
                     secrets.yaml
                     # <- configuration to merge for environment
                     {environment}/secrets.yaml

Side notes

TODOs

Dealing with secrets locally

TODO(fredbi)

Credits

The config part is largely based on some seminal past work by @casualjim. I am grateful to him for his much inspiring code.

The version-from-go-runtime piece of code is largely inspired by the wonderful work from the golangci community.

About

A few utilities to build CLI on top of cobra and viper

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Languages