Skip to content

Provides simple dependency injection for parsed command lines

License

Notifications You must be signed in to change notification settings

xmidt-org/clifx

Repository files navigation

clifx

Build Status codecov.io Go Report Card Apache V2 License GitHub Release GoDoc

Summary

Provides basic bootstrapping for a parsed command line into an fx.App. Kong is used to parse a command line into an arbitrary golang struct.

Table of Contents

Code of Conduct

This project and everyone participating in it are governed by the XMiDT Code Of Conduct. By participating, you agree to this Code.

Installation

go get github.com/xmidt-org/clifx@latest

Usage

Basic

import github.com/xmidt-org/clifx

type MyCLI struct {
  Debug bool
  Files []string
}

func main() {
  app := fx.New(
    clifx.Provide[MyCLI](
      clifx.StandardArguments(),
    ),
  
    // a component of type MyCLI will now
    // be available for dependency injection.
    // For example:
    fx.Invoke(
      func(cli MyCLI) error {
        // do things
        return nil
      },
    ),

    // you can bind dependencies to the kong.Context.  if desired,
    // you must do this before calling kong.Context.Run:
    fx.Supply("dependency"),
    fx.Invoke(
      func(kctx *kong.Context, dependency string) {
        kctx.Bind(dependency) // this is now available in CLI methods
      }
    ),

    // the kong.Context can be used to run the CLI.
    // This will cause fx.New to run the command:
    fx.Invoke(
      func(kctx *kong.Context, sh fx.Shutdowner) error {
        defer sh.Shutdown() // optional: this ensures the app exits from app.New
        return kctx.Run() // you could pass dependencies to Run
      },
    )
  )
}

Lifecycle

You can bind a CLI to the fx.Lifecycle in the same way as any other component. For example, it's common to want to run the CLI when fx.App.Run is called, then shutdown the app when finished:

import github.com/xmidt-org/clifx

type MyCLI struct {
  Debug bool
  Files []string
}

func main() {
  app := fx.New(
    clifx.Provide[MyCLI](
      clifx.StandardArguments(),
    ),

    fx.Invoke(
      func(kctx *kong.Context, l fx.Lifecycle, sh fx.Shutdowner /* any other dependencies from the enclosing app */) {
        l.Append(fx.Hook{
          OnStart: func(_ context.Context) error {
            // optional:  this just exits from app.Run when the CLI is done.
            // without this, app.Run will not return until explicitly stopped, such as
            // by hitting ctrl+C at a console.
            defer sh.Shutdown()

            // don't forget:  you can pass dependencies from the enclosing app here
            return kctx.Run()
          },
        })
      },
    ),
  )

  // this now causes the CLI to be executed.  Any error that is returned will be
  // from the CLI tool.
  app.Run()
}

Custom options

You can supply custom any number of kong options to Provide.

clifx.Provide[MyCLI](
 clifx.StandardArguments(),
 kong.UsageOnError(),
 kong.Description("here is a custom tool"),
)

Suppressing os.Exit

By default, kong will invoke os.Exit(1) anytime a parse fails. You can suppress this easily by providing a noop Exit function. clifx provides a kong option for this purpose:

import github.com/xmidt-org/clifx

type MyCLI struct {
  Debug bool
  Files []string
}

func main() {
  app := fx.New(
    clifx.Provide[MyCLI](
      clifx.StandardArguments(),
      clifx.SuppressExit(),
    ),
  
    fx.Invoke(
      func(cli MyCLI) error {
        return nil
      },
    ),
  )

  // since we didn't exit the process, we can test app.Err()
  var pe *kong.ParseError
  if errors.As(app.Err(), &pe) {
    // custom behavior in reaction to a bad command-line
  }
}

Custom arguments

clifx.StandardArguments returns the command-line passed to the process. You can supply an arguments you like, which is useful for testing or interactive use:

clifx.Provide[MyCLI](
  clifx.AsArguments("--bind", ":8080", "-v"),
)

Contributing

Refer to CONTRIBUTING.md.

About

Provides simple dependency injection for parsed command lines

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages