Coder Social home page Coder Social logo

cli's Introduction

Command line interface

License Travis branch Coverage Status Go Report Card GoDoc

Screenshot

screenshot2

Key features

  • Lightweight and easy to use.
  • Defines flag by tag, e.g. flag name(short or/and long), description, default value, password, prompt and so on.
  • Type safety.
  • Output looks very nice.
  • Supports custom Validator.
  • Supports slice and map as a flag.
  • Supports any type as a flag field which implements cli.Decoder interface.
  • Supports any type as a flag field which uses FlagParser.
  • Suggestions for command.(e.g. hl => help, "veron" => "version").
  • Supports default value for flag, even expression about env variable(e.g. dft:"$HOME/dev").
  • Supports editor like git commit command.(See example 21 and 22)

API documentation

See godoc

Examples

Example 1: Hello

back to examples

// main.go
// This is a HelloWorld-like example

package main

import (
	"os"

	"github.com/mkideal/cli"
)

type argT struct {
	Name string `cli:"name" usage:"tell me your name"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("Hello, %s!\n", argv.Name)
		return nil
	}))
}
$ go build -o hello
$ ./hello --name Clipher
Hello, Clipher!

Example 2: Flag

back to examples

// main.go
// This example show basic usage of flag

package main

import (
	"os"

	"github.com/mkideal/cli"
)

type argT struct {
	cli.Helper
	Port int  `cli:"p,port" usage:"short and long format flags both are supported"`
	X    bool `cli:"x" usage:"boolean type"`
	Y    bool `cli:"y" usage:"boolean type, too"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("port=%d, x=%v, y=%v\n", argv.Port, argv.X, argv.Y)
		return nil
	}))
}
$ go build -o app
$ ./app -h
Options:

  -h, --help     display help information
  -p, --port     short and long format flags both are supported
  -x             boolean type
  -y             boolean type, too
$ ./app -p=8080 -x
port=8080, x=true, y=false
$ ./app -p 8080 -x=true
port=8080, x=true, y=false
$ ./app -p8080 -y true
port=8080, x=false, y=true
$ ./app --port=8080 -xy
port=8080, x=true, y=true
$ ./app --port 8080 -yx
port=8080, x=true, y=true

Example 3: Required flag

back to examples

// main.go
// This example show how to use required flag

package main

import (
	"os"

	"github.com/mkideal/cli"
)

type argT struct {
	cli.Helper
	Id uint8 `cli:"*id" usage:"this is a required flag, note the *"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("%d\n", argv.Id)
		return nil
	}))
}
$ go build -o app
$ ./app
ERR! required argument --id missing
$ ./app --id=2
2

Example 4: Default flag

back to examples

// main.go
// This example show how to use default flag

package main

import (
	"os"

	"github.com/mkideal/cli"
)

type argT struct {
	cli.Helper
	Basic  int    `cli:"basic" usage:"basic usage of default" dft:"2"`
	Env    string `cli:"env" usage:"env variable as default" dft:"$HOME"`
	Expr   int    `cli:"expr" usage:"expression as default" dft:"$BASE_PORT+1000"`
	DevDir string `cli:"devdir" usage:"directory of developer" dft:"$HOME/dev"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("%d, %s, %d, %s\n", argv.Basic, argv.Env, argv.Expr, argv.DevDir)
		return nil
	}))
}
$ go build -o app
$ ./app -h
Options:

  -h, --help                       display help information
      --basic[=2]                  basic usage of default
      --env[=$HOME]                env variable as default
      --expr[=$BASE_PORT+1000]     expression as default
      --devdir[=$HOME/dev]         directory of developer
$ ./app
2, /Users/wang, 1000, /Users/wang/dev
$ BASE_PORT=8000 ./app --basic=3
3, /Users/wang, 9000, /Users/wang/dev

Example 5: Slice

back to examples

// main.go
// This example show how to use slice as a flag

package main

import (
	"os"

	"github.com/mkideal/cli"
)

type argT struct {
	// []bool, []int, []float32, ... supported too.
	Friends []string `cli:"F" usage:"my friends"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		ctx.JSONln(ctx.Argv())
		return nil
	}))
}
$ go build -o app
$ ./app
{"Friends":null}
$ ./app -FAlice -FBob -F Charlie
{"Friends":["Alice","Bob","Charlie"]}

Example 6: Map

back to examples

// main.go
// This example show how to use map as a flag

package main

import (
	"os"

	"github.com/mkideal/cli"
)

type argT struct {
	Macros map[string]int `cli:"D" usage:"define macros"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		ctx.JSONln(ctx.Argv())
		return nil
	}))
}
$ go build -o app
$ ./app
{"Macros":null}
$ ./app -Dx=not-a-number
ERR! `not-a-number` couldn't converted to an int value
$ ./app -Dx=1 -D y=2
{"Macros":{"x":1,"y":2}}

Example 7: Force flag

back to examples

// main.go
// This example show usage of force flag
// Force flag has prefix !, and must be a boolean.
// Will prevent validating flags if some force flag assigned true

package main

import (
	"os"

	"github.com/mkideal/cli"
)

type argT struct {
	Version  bool `cli:"!v" usage:"force flag, note the !"`
	Required int  `cli:"*r" usage:"required flag"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		if argv.Version {
			ctx.String("v0.0.1\n")
		}
		return nil
	}))
}
$ go build -o app
$ ./app
ERR! required argument -r missing

# -v is a force flag, and assigned true, so `ERR` disappear.
$ ./app -v
v0.0.1

Example 8: Child command

back to examples

// main.go
// This example demonstrates usage of child command

package main

import (
	"fmt"
	"os"

	"github.com/mkideal/cli"
)

func main() {
	if err := cli.Root(root,
		cli.Tree(help),
		cli.Tree(child),
	).Run(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

var help = cli.HelpCommand("display help information")

// root command
type rootT struct {
	cli.Helper
	Name string `cli:"name" usage:"your name"`
}

var root = &cli.Command{
	Desc: "this is root command",
	// Argv is a factory function of argument object
	// ctx.Argv() is if Command.Argv == nil or Command.Argv() is nil
	Argv: func() interface{} { return new(rootT) },
	Fn: func(ctx *cli.Context) error {
		argv := ctx.Argv().(*rootT)
		ctx.String("Hello, root command, I am %s\n", argv.Name)
		return nil
	},
}

// child command
type childT struct {
	cli.Helper
	Name string `cli:"name" usage:"your name"`
}

var child = &cli.Command{
	Name: "child",
	Desc: "this is a child command",
	Argv: func() interface{} { return new(childT) },
	Fn: func(ctx *cli.Context) error {
		argv := ctx.Argv().(*childT)
		ctx.String("Hello, child command, I am %s\n", argv.Name)
		return nil
	},
}
$ go build -o app

# help for root
# equivalent to "./app -h"
$ ./app help
this is root command

Options:

  -h, --help     display help information
      --name     your name

Commands:
  help    display help information
  child   this is a child command

# help for specific command
# equivalent to "./app child -h"
$ ./app help child
this is a child command

Options:

  -h, --help     display help information
      --name     your name

# execute root command
$ ./app --name 123
Hello, root command, I am 123

# execute child command
$ ./app child --name=123
Hello, child command, I am 123

# something wrong, but got a suggestion.
$ ./app chd
ERR! command chd not found
Did you mean child?

Example 9: Auto help

back to examples

// main.go
// This example demonstrates cli.AutoHelper

package main

import (
	"os"

	"github.com/mkideal/cli"
)

type argT struct {
	Help bool `cli:"h,help" usage:"show help"`
}

// AutoHelp implements cli.AutoHelper interface
// NOTE: cli.Helper is a predefined type which implements cli.AutoHelper
func (argv *argT) AutoHelp() bool {
	return argv.Help
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		return nil
	}))
}
$ go build -o app
$ ./app -h
Options:

  -h, --help     show help

Try comment AutoHelp method and rerun it.

Example 10: Usage of Validator

back to examples

// main.go
// This example demonstrates how to utilize Validator

package main

import (
	"fmt"
	"os"

	"github.com/mkideal/cli"
)

type argT struct {
	cli.Helper
	Age    int    `cli:"age" usage:"your age"`
	Gender string `cli:"g,gender" usage:"your gender" dft:"male"`
}

// Validate implements cli.Validator interface
func (argv *argT) Validate(ctx *cli.Context) error {
	if argv.Age < 0 || argv.Age > 300 {
		return fmt.Errorf("age %d out of range", argv.Age)
	}
	if argv.Gender != "male" && argv.Gender != "female" {
		return fmt.Errorf("invalid gender %s", ctx.Color().Yellow(argv.Gender))
	}
	return nil
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		ctx.JSONln(ctx.Argv())
		return nil
	}))
}
$ go build -o app
$ ./app --age=-1
ERR! age -1 out of range
$ ./app --age=1000
ERR! age 1000 out of range
$ ./app -g balabala
ERR! invalid gender balabala
$ ./app --age 88 --gender female
{"Help":false,"Age":88,"Gender":"female"}

Example 11: Prompt and Password

back to examples

// main.go
// This example introduce prompt and pw tag

package main

import (
	"os"

	"github.com/mkideal/cli"
)

type argT struct {
	cli.Helper
	Username string `cli:"u,username" usage:"github account" prompt:"type github account"`
	Password string `pw:"p,password" usage:"password of github account" prompt:"type the password"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("username=%s, password=%s\n", argv.Username, argv.Password)
		return nil
	}))
}
$ go build -o app
$ ./app
type github account: hahaha # visible
type the password: # invisible because of `pw` tag
username=hahaha, password=123456

Example 12: Decoder

back to examples

// main.go
// This example show how to use decoder

package main

import (
	"os"
	"strings"

	"github.com/mkideal/cli"
)

type exampleDecoder struct {
	list []string
}

// Decode implements cli.Decoder interface
func (d *exampleDecoder) Decode(s string) error {
	d.list = strings.Split(s, ",")
	return nil
}

type argT struct {
	Example exampleDecoder `cli:"d" usage:"example decoder"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.JSONln(argv.Example.list)
		return nil
	}))
}
$ go build -o app
$ ./app -d a,b,c
["a","b","c"]

Example 13: Pid file

back to examples

// main.go
// This example show how to use builtin Decoder: PidFile

package main

import (
	"os"

	"github.com/mkideal/cli"
	clix "github.com/mkideal/cli/ext"
)

type argT struct {
	cli.Helper
	PidFile clix.PidFile `cli:"pid" usage:"pid file" dft:"013-pidfile.pid"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)

		if err := argv.PidFile.New(); err != nil {
			return err
		}
		defer argv.PidFile.Remove()

		return nil
	}))
}

Example 14: Time and Duration

back to examples

// main.go
// This example show how to use builtin Decoder: Time and Duration

package main

import (
	"os"

	"github.com/mkideal/cli"
	clix "github.com/mkideal/cli/ext"
)

type argT struct {
	Time     clix.Time     `cli:"t" usage:"time"`
	Duration clix.Duration `cli:"d" usage:"duration"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("time=%v, duration=%v\n", argv.Time, argv.Duration)
		return nil
	}))
}
$ go build -o app
$ ./app -t '2016-1-2 3:5' -d=10ms
time=2016-01-02 03:05:00 +0800 CST, duration=10ms

Example 15: File

back to examples

// main.go
// This example show how to use builtin Decoder: File

package main

import (
	"os"

	"github.com/mkideal/cli"
	clix "github.com/mkideal/cli/ext"
)

type argT struct {
	Content clix.File `cli:"f,file" usage:"read content from file or stdin"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String(argv.Content.String())
		return nil
	}))
}
$ go build -o app
# read from stdin
$ echo hello | ./app -f
hello
# read from file
$ echo hello > test.txt && ./app -f test.txt
hello
$ rm test.txt

Example 16: Parser

back to examples

// main.go
// This example introduce Parser
// `Parser` is another way to use custom type of data.
// Unlike `Decoder`, `Parser` used to parse string according to specific rule,
// like json,yaml and so on.
//
// Builtin parsers:
// * json
// * jsonfile

package main

import (
	"os"

	"github.com/mkideal/cli"
)

type config struct {
	A string
	B int
	C bool
}

type argT struct {
	JSON config `cli:"c,config" usage:"parse json string" parser:"json"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.JSONIndentln(argv.JSON, "", "    ")
		return nil
	}))
}
$ go build -o app
$ ./app
{
    "A": "",
    "B": 0,
    "C": false
}
$ ./app -c '{"A": "hello", "b": 22, "C": true}'
{
    "A": "hello",
    "B": 22,
    "C": true
}

Example 17: JSON file

back to examples

// main.go
// This example show how to use builtin parser: jsonfile
// It's similar to json, but read string from file.

package main

import (
	"os"

	"github.com/mkideal/cli"
)

type config struct {
	A string
	B int
	C bool
}

type argT struct {
	JSON config `cli:"c,config" usage:"parse json from file" parser:"jsonfile"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.JSONIndentln(argv.JSON, "", "    ")
		return nil
	}))
}
$ go build -o app
$ echo '{"A": "hello", "b": 22, "C": true}' > test.json
$ ./app -c test.json
{
    "A": "hello",
    "B": 22,
    "C": true
}
$ rm test.json

Example 18: Custom parser

back to examples

// main.go
// This example demonstrates how to use custom parser

package main

import (
	"os"
	"reflect"

	"github.com/mkideal/cli"
)

type myParser struct {
	ptr interface{}
}

func newMyParser(ptr interface{}) cli.FlagParser {
	return &myParser{ptr}
}

// Parse implements FlagParser.Parse interface
func (parser *myParser) Parse(s string) error {
	typ := reflect.TypeOf(parser.ptr)
	val := reflect.ValueOf(parser.ptr)
	if typ.Kind() == reflect.Ptr {
		kind := reflect.Indirect(val).Type().Kind()
		if kind == reflect.Struct {
			typElem, valElem := typ.Elem(), val.Elem()
			numField := valElem.NumField()
			for i := 0; i < numField; i++ {
				_, valField := typElem.Field(i), valElem.Field(i)
				if valField.Kind() == reflect.Int &&
					valField.CanSet() {
					valField.SetInt(2)
				}
				if valField.Kind() == reflect.String &&
					valField.CanSet() {
					valField.SetString("B")
				}
			}
		}
	}
	return nil
}

type config struct {
	A int
	B string
}

type argT struct {
	Cfg config `cli:"cfg" parser:"myparser"`
}

func main() {
	// register parser factory function
	cli.RegisterFlagParser("myparser", newMyParser)

	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("%v\n", argv.Cfg)
		return nil
	}))
}
$ go build -o app
$ ./app
{0 }
$ ./app --cfg xxx
{2 B}

Example 19: Hooks

back to examples

// main.go
// This example demonstrates how to use hooks

package main

import (
	"fmt"
	"os"

	"github.com/mkideal/cli"
)

func main() {
	if err := cli.Root(root,
		cli.Tree(child1),
		cli.Tree(child2),
	).Run(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

type argT struct {
	Error bool `cli:"e" usage:"return error"`
}

var root = &cli.Command{
	Name: "app",
	Argv: func() interface{} { return new(argT) },
	OnRootBefore: func(ctx *cli.Context) error {
		ctx.String("OnRootBefore invoked\n")
		return nil
	},
	OnRootAfter: func(ctx *cli.Context) error {
		ctx.String("OnRootAfter invoked\n")
		return nil
	},
	Fn: func(ctx *cli.Context) error {
		ctx.String("exec root command\n")
		argv := ctx.Argv().(*argT)
		if argv.Error {
			return fmt.Errorf("root command returns error")
		}
		return nil
	},
}

var child1 = &cli.Command{
	Name: "child1",
	Argv: func() interface{} { return new(argT) },
	OnBefore: func(ctx *cli.Context) error {
		ctx.String("child1's OnBefore invoked\n")
		return nil
	},
	OnAfter: func(ctx *cli.Context) error {
		ctx.String("child1's OnAfter invoked\n")
		return nil
	},
	Fn: func(ctx *cli.Context) error {
		ctx.String("exec child1 command\n")
		argv := ctx.Argv().(*argT)
		if argv.Error {
			return fmt.Errorf("child1 command returns error")
		}
		return nil
	},
}

var child2 = &cli.Command{
	Name:   "child2",
	NoHook: true,
	Fn: func(ctx *cli.Context) error {
		ctx.String("exec child2 command\n")
		return nil
	},
}
$ go build -o app
# OnRootBefore => Fn => OnRootAfter
$ ./app
OnRootBefore invoked
exec root command
OnRootAfter invoked
# OnBefore => OnRootBefore => Fn => OnRootAfter => OnAfter
$ ./app child1
child1 OnBefore invoked
OnRootBefore invoked
exec child1 command
OnRootAfter invoked
child1 OnAfter invoked
# No hooks
$ ./app child2
exec child2 command
# OnRootBefore => Fn --> Error
$ ./app -e
OnRootBefore invoked
exec root command
root command returns error
# OnBefore => OnRootBefore => Fn --> Error
$ ./app child1 -e
child1 OnBefore invoked
OnRootBefore invoked
exec child1 command
child1 command returns error

Example 20: Daemon

back to examples

// main.go
// This example demonstrates how to use `Daemon`

package main

import (
	"fmt"
	"os"
	"time"

	"github.com/mkideal/cli"
)

type argT struct {
	cli.Helper
	Wait  uint `cli:"wait" usage:"seconds for waiting" dft:"10"`
	Error bool `cli:"e" usage:"create an error"`
}

const successResponsePrefix = "start ok"

func main() {
	if err := cli.Root(root,
		cli.Tree(daemon),
	).Run(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

var root = &cli.Command{
	Argv: func() interface{} { return new(argT) },
	Fn: func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		if argv.Error {
			err := fmt.Errorf("occurs error")
			cli.DaemonResponse(err.Error())
			return err
		}
		cli.DaemonResponse(successResponsePrefix)
		<-time.After(time.Duration(argv.Wait) * time.Second)
		return nil
	},
}

var daemon = &cli.Command{
	Name: "daemon",
	Argv: func() interface{} { return new(argT) },
	Fn: func(ctx *cli.Context) error {
		return cli.Daemon(ctx, successResponsePrefix)
	},
}
$ go build -o daemon-app
$ ./daemone-app daemon
start ok
# Within 10 seconds, you will see process "./daemon-app"
$ ps | grep daemon-app
11913 ttys002    0:00.01 ./daemon-app
11915 ttys002    0:00.00 grep daemon-app
# After 10 seconds
$ ps | grep daemon-app
11936 ttys002    0:00.00 grep daemon-app
# try again with an error
$ ./daemon-app daemon -e
occurs error
$ ps | grep daemon-app
11936 ttys002    0:00.00 grep daemon-app

Example 21: Editor

back to examples

// main.go
// This example demonstrates how to use `editor`. This similar to git commit

package main

import (
	"os"

	"github.com/mkideal/cli"
)

type argT struct {
	cli.Helper
	Msg string `edit:"m" usage:"message"`
}

func main() {
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("msg: %s", argv.Msg)
		return nil
	}))
}
$ go build -o app
$ ./app -m "hello, editor"
msg: hello, editor
$ ./app # Then, launch a editor(default is vim) and type `hello, editor`, quit the editor
msg: hello, editor

Example 22: Custom Editor

back to examples

// main.go
// This example demonstrates specific editor.

package main

import (
	"os"

	"github.com/mkideal/cli"
)

type argT struct {
	cli.Helper
	Msg string `edit:"m" usage:"message"`
}

func main() {
	cli.GetEditor = func() (string, error) {
		if editor := os.Getenv("EDITOR"); editor != "" {
			return editor, nil
		}
		return cli.DefaultEditor, nil
	}
	os.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		ctx.String("msg: %s", argv.Msg)
		return nil
	}))
}
$ go build -o app
$ ./app -m "hello, editor"
msg: hello, editor
$ EDITOR=nano ./app # Then, launch nano and type `hello, editor`, quit the editor
msg: hello, editor

Example 23: Hide flag

back to examples

// main.go
// This example hides Gender and InternalUsage flags.
package main

import (
	"os"

	"github.com/mkideal/cli"
)

type helloT struct {
	cli.Helper
	Name          string `cli:"name" usage:"tell me your name" dft:"world"`
	Gender        string `cli:"-"` // deprecated
	InternalUsage string `cli:"-"` // hide
	Age           uint8  `cli:"a,age" usage:"tell me your age" dft:"100"`
}

func main() {
	os.Exit(cli.Run(new(helloT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*helloT)
		ctx.String("Hello, %s! Your age is %d?\n", argv.Name, argv.Age)
		return nil
	}))
}
$ go build -o app
$ ./app -h
Options:

  -h, --help           display help information
      --name[=world]   tell me your name
  -a, --age[=100]      tell me your age

cli's People

Contributors

chrjen avatar codelingobot avatar dependabot[bot] avatar dmitrys99 avatar h12w avatar keinos avatar mailund avatar meisterluk avatar mkideal avatar suntong avatar wangshijin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cli's Issues

Self config allows to go with the executable file

  • One niche cli has is to define a self-config .json file that allows using the .json file to control the executable file behavior.
  • Such self-config .json files almost always go with the executable file
  • However if we put such executable somewhere in the path, cli currently ready such self-config .json file from pwd, and would fail if it is not:

Using
https://github.com/suntong/lang/blob/master/lang/Go/src/sys/CLI/027-global-option.go#L21
to showcase the situation:

.../lang/Go/src/sys/CLI$ 027-global-option
root
{"Host":"regexp\\s+test","Port":8080}{"Host":"regexp\\s+test","Port":8080}
[2 15]

cd ../

.../lang/Go/src/sys$ CLI/027-global-option
ERR! open 027-global-option.json: no such file or directory

If we allow such self-config .json files to reside where the executable file is, then such with-self-config executable can truly be invoked from everywhere.

Parameter, option & argument

May be it is a good idea to standardize the usage of "parameter", "option" & "argument" throughout cli according to GNU getopt.

From man getopt:

The parameters are parsed from left to right. Each parameter is classified as a short option, a long option, an argument to an option, or a non-option parameter.

I.e., the parameter and option are (almost) interchangeable, while argument normally means the actual value for the option.

I don't know how cohesive such names are used throughout cli, but I did notice one that's not quite right -- the output of 003-required-flag.go

$ go run 003-required-flag.go 
ERR! required argument --id missing

It is better be called "required parameter" instead of argument here. and it might be a good idea to check the whole cli messages to check for such cohesive usage (to make 风格更为统一).

Add support for url.URL argument type

Urls are pretty common args to go apps and it makes sense to support url validation early on. The alternative is to rely on rather ugly boilerplate in all apps in need of that feature.

The example of ugly boilerplate that better be hidden inside the lib:

type urlValue struct {
	u *url.URL
}

func (uv *urlValue) Parse(s string) error {
	u, err := url.Parse(s)
	*uv.u = *u
	return err
}

cli.RegisterFlagParser("urlParser", func(ptr interface{}) cli.FlagParser {
	return &urlValue{
		u: ptr.(*url.URL),
	}
})

Something went wrong with dep

  1. Create simple main.go
package main

import (
	"github.com/mkideal/cli"
)

func main() {
	cli.HelpCommand("aaa")
}
  1. Run dep init
  2. Run go build
    Result:
$: go build
# github.com/alxmsl/temp/vendor/github.com/mkideal/cli
vendor/github.com/mkideal/cli/flag.go:66: cannot convert v (type expr.Value) to type int64
vendor/github.com/mkideal/cli/flag.go:68: cannot convert v (type expr.Value) to type float64

File options

  1. The clix.File allows to read content from file or stdin, does something equivalent exist for write to file or stdout as well?
  2. If there will be, for file options, better provide a function to expose the file-handle (io.Reader & io.Writer) for users to read/write files themselves.

Thanks

Verbose option

Can cli provide the accumulative option like what ssh does for verbose?

I.e., the more -v are specified on the command line, the more verbose it will be. This will be good for other purpose like debug as well. So far my code look like this:

       Verbose      int          `cli:"v,verbose"usage:"Be verbose (with level 1 & 2)"`

To use it, I have to specify -v 1 or -v 2, a little bit inconvenient than specifying -v or -v -v.

Thanks

Simplify access to nested args

In an app with nested sub-commands, it may be necessary to look at the intermediate command's args. Currently, this is only achievable via GetArgvList method, which is unnecessarily complicated if one only wants to look at the args of the immediate sub-command parent.

An addition of ArgvAt(pos int) method to Context object, with the obvious semantics of return ctx.argvList[pos], (or something similar in intent) will be most welcome.

Self in sub command

Hiya,

Please take a look at 027s-subc.go

This is how I run it,

echo '{ "Dir": "DDD", "Suffix": "sss", "Out": "oooo" }' > build.json

$ go run 027s-subc.go build aaa
[build]:
  &{Helper:{Help:false} Host: Port:80}
  &{Self:0xc8200184c0 Dir:./ Suffix:.go,.c,.s Out:}
  [aaa]

The Self in sub command is supposed to pick up values defined in the build.json file, and I surely remember it did it before, but now it seems not doing it.

add parser tag

I want add a new tag category, named parser, e.g.

type T struct {
    A string
    B int
}

type argT struct {
    Value T `cli:"t" usage:"parse from json" parser:"json"`
}

Wanted parsing -t '{"A": "string", B:2}' to argT.Value

Need separator to arrange parameters in groups

This is for better usage() output.

E.g.,

from

Options:

  -h, --help        display help information 
  -c, --config      config file [=wireframe_cfg.json]
  -H, --host        host addr [=$HOST]
  -p, --port        listening port 
  -D, --daemonize   daemonize the service 
  -o, --output      The output file (default: some file)
  -v, --verbose     Verbose mode (Multiple -v options increase the verbosity.) 

to

Options:

  -h, --help        display help information 

  -c, --config      config file [=wireframe_cfg.json]

  -H, --host        host addr [=$HOST]
  -p, --port        listening port 

  -D, --daemonize   daemonize the service 
  -o, --output      The output file (default: some file)
  -v, --verbose     Verbose mode (Multiple -v options increase the verbosity.) 

Note that adding \n at the end of usage text will not always work, for e.g., the above --config/--host case because the defaults are added after the user provided usage text.

panic: interface conversion: interface {} is nil

Hey, Please take a look at https://gist.github.com/suntong/cb2bf3d3ada7d7941aac8ef338a815a1#file-dump-go

the previous version was OK (or maybe it didn't work before either, but I can't recall clearly now). Anyway, when running this one, I got the errors like this:

$ go run /tmp/dump.go dump -i /tmp/ii aaa bbbb
panic: interface conversion: interface {} is nil, not *main.rootT

goroutine 1 [running]:
panic(0x7f3020, 0xc820018540)
        /usr/lib/go-1.6/src/runtime/panic.go:464 +0x3e6
main.dumpCLI(0xc8200a0aa0, 0x0, 0x0)
        /tmp/dump.go:72 +0x353
github.com/mkideal/cli.(*Command).RunWith(0xa84ec0, 0xc82000a130, 0x5, 0x5, 0x7f7ebe41e358, 0xc82003c010, 0x0, 0x0, 0x0, 0x0, ...)
        /export/home/u/l/ws/Go/src/github.com/mkideal/cli/command.go:205 +0x58d
github.com/mkideal/cli.(*Command).Run(0xa84ec0, 0xc82000a130, 0x5, 0x5, 0x0, 0x0)
        /export/home/u/l/ws/Go/src/github.com/mkideal/cli/command.go:162 +0x84
main.main()
        /tmp/dump.go:37 +0x134

What's wrong?

Command alias

Is there a way to register/provide shorter alias names to command, like npm i stands for npm install?

optional arguments

The GNU getopt supports optional arguments:

A long option normally begins with '--' followed by the long option
name. If the option has a required argument, it may be written
directly after the long option name, separated by '=', or as the next
argument (i.e. separated by whitespace on the command line). If the
option has an optional argument, it must be written directly after the
long option name, separated by '=', if present.

Would cli support optional arguments as well? If true, please make it clear of the following three stages:

  1. not specified
  2. specified but no argument
  3. specified and with argument

Thanks

Add new line after "Commands:"

For aesthetics reason, for either Usage Style of either NormalStyle or ManualStyle, it is better to add new line after the output of Commands:. Here is what it is looking now:

--------------------------------------------------------------------
global option redo

  redo global option via automatic code-gen

Options:

  -h, --help                 display help information
  -c, --config[=redo.json]   config file
  -H, --host[=$HOST]         host address
  -p, --port                 listening port

Commands:
  build   Build the network application

--------------------------------------------------------------------
global option redo

  redo global option via automatic code-gen

Options:

  -h, --help
      display help information

  -c, --config[=redo.json]
      config file

  -H, --host[=$HOST]
      host address

  -p, --port
      listening port

Commands:
  build   Build the network application

After adding the new line after Commands:, it will look better and more consistent with other components.

ctx.IsSet returns fl.isAssigned instead of fl.isSet

Not very intuitive. From the code (both comments makes reference to same field, copy and paste gone awry?)

// isAssigned indicates wether the flag is set(contains default value)
isAssigned bool
// isAssigned indicates wether the flag is set
isSet bool

Cheers,
J

Need a lint tool for cli

A command-line tool should be written by cli, used to lint code written by cli. It would be called clint.

NeedArgs clarification

Hey 仕晋,

Does NeedArgs means need argument or need option?

Please see my demo how to handle none-option parameters, in which I added a none-option parameters to build:

Build golang application

Usage:
  gogo build [Options] Arch(i386|amd64)

It runs fine:

$ gogo build --dir=.. -o out i386
build: {
    "Help": false,
    "Dir": "..",
    "Suffix": ".go,.c,.s",
    "Out": "out"
}
Arch: i386

However, my code would panic if the Arch none-option parameter is not specified. So we need clarification here. Once again, rsync takes an awful lot of options, but besides them, at the end of command line, there are none-option parameters, normally called argument. I hope that NeedArgs really means this, which goes with common understanding and all other commands.

Thanks

Customize Desc

Hey again 仕晋,

Any way for user to customize their own function description?

Please take a look at my such customization in demo how to handle none-option parameters,

My own function description customization looks good this way:

$ gogo build
Build golang application

Usage:
  gogo build [Options] Arch(i386|amd64)

Options:

  -h, --help
      display help information

  --dir[=./]
...

But it will have side-effects:

$ gogo
Golang package manager

  gogo is a new golang package manager
  very very good

Options:

  -h, --help
      display help information

  -v, --version
      display version

  -l, --list[=false]
      list all sub commands or not

Commands:
  build     Build golang application

Usage:
  gogo build [Options] Arch(i386|amd64)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  clean     Clean build data

Hope that you can provide a way for user to customize their function descriptions.

Thanks

Any way to handle none-option parameters?

Does CLI support handling none-option parameters?

For e.g., rsync takes an awful lot of options, but besides them, at the end of command line, there needs to be none-option parameters like source and optionally destination. Can CLI handle cases like this?

Thanks

Global options

Hi 仕晋,

I'll enhance my code-gen further before I write the wiki. Meanwhile, another question,

Does CLI support global options? By "global options" I mean the options supplied before the sub commands. E.g., both apt-get and git have options before the sub commands:

$ apt-get 
apt 1.2.10 (amd64)
Usage: apt-get [options] command
       apt-get [options] install|remove pkg1 [pkg2 ...]
       apt-get [options] source pkg1 [pkg2 ...]
...

$ git 
usage: git [--version] [--help] [-C <path>] [-c name=value]
           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
           <command> [<args>]
...

Any example that shows a sub command has global options and its own "private" options playing together?

On a very closely related question, when talking about global options, the most common request is for the application configuration, using something like -C config to override the defaults. Just like your 017-jsonfile example. My question is,

Would CLI allow the application to,

  • define those application configuration defaults in the program
  • and allow predefined configuration file to override those defaults. Most importantly, the user configuration file can be only a subset of the configurations. For all those not in the user configuration file, take the defaults from definition in the program
  • and allow user to use environment var to specify a different configuration file to use
  • and allow user to command line (e.g., -C config) to specify a different configuration file to use (just like your 017-jsonfile example)

Would CLI allow the above layered approach? How much it can do at present?

Thanks

Excessive spacing with defaults when in NormalStyle

Hope the help text can use double lines when using jsonfile in NormalStyle. Otherwise the option + default file will make the option section too wide and text section too narrow. Here is the contrast:

# Without  jsonfile option
Options:

  -h, --help       display help information
  -U, --rul        usage URL from ISP
      --css        css selection for usage text from usage rul
      --to         email address to send to
      --template   mail sending command template string
  -d, --days       days to shift from billing date
  -v, --verbose    Verbose mode (Multiple -v options increase the verbosity.)

# With  jsonfile option
  -h, --help                        display help information
  -U, --rul                         usage URL from ISP
      --css                         css selection for usage text from usage rul
      --to                          email address to send to
      --template                    mail sending command template string
  -d, --days                        days to shift from billing date
  -v, --verbose                     Verbose mode (Multiple -v options increase the verbosity.)
  -c, --config[=cfg_default.json]   config file

Too much space has been wasted in the second case.

I know there is also a ManualStyle, but that will make the help text too long if there are a few options. So using the second line for the default value seems to be a good compromise.

 -c, --config            config file
    [=cfg_default.json]

Please consider. Thx.

bash completion

Need bash completion for command, even for flags. For example, app has a command hello,
app hello

And hello has flags --aha and --oho.

Now, I want complete to app hello when typing app he in terminal and complete to app hello --aha when typing app hello --a

This project is now dormant, but there is a new forked repo

Hi @mkideal/@wangshijin,

Have you been looking at the issues / PRs lately?

I'm willing to step forward to take the small maintaining chores over, would you add me to the project co-maintainer please?

Otherwise, I'm afraid we have to fork the project, because it's been un-maintained for a while. Thx.

Preserve orders for map types

The go map is known to not preserver map orders. I.e., if we run the following sample code a couple of times,

$ go run 006-map.go -Dx=1 -D y=2
{"Macros":{"x":1,"y":2}}

The output might not always be the same. Is it possible to obtain/preserver output orders to that given on command line? I know the current data-structure Macros map[string]int need to be replaced by some cli specific types, i.e., API changes. So,

Just asking. Thanks.

Cloning with HTTPS

Hi,

This is not the first time that I upgrade cli package, but today, when I did it, I got:

$ go get -v github.com/mkideal/cli
github.com/mkideal/cli (download)
# cd .; git clone https://github.com/mkideal/cli /path/to/Go/src/github.com/mkideal/cli
Cloning into '/path/to/Go/src/github.com/mkideal/cli'...
fatal: 'origin' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
package github.com/mkideal/cli: exit status 128

Even git alone with give me errors:

cd /path/to/Go/src/github.com/mkideal

$ git clone https://github.com/mkideal/cli
Cloning into 'cli'...
fatal: 'origin' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

Do you have any idea why?

Show usage by default

Hi,

Can you add an option to show usage help by default, when no command line options are provided please?

Many of my sub commands have complicated cli options (that's why I need cli, hehe), so I'd need to have a way to show usage help when no command line options are provided.

  • The case when none-option parameters are needed can be dealt with NumArg, however
  • The case when all parameters are provided as cli options, including some mandatory (required) ones, this is the case I need to show usage help when no command line options are provided as well, even without specifying that -h.

This is not the same as when there is some error (required argument missing) happens then show usage help, because it is all about friendliness to me. I.e., as long as friendliness is concerned, here is my view of the order going from bad to better:

  1. Complains about required argument missing
  2. Complains about required argument missing but then show usage
  3. Show usage up front, when no command line options are provided. If users have given anything else on the command line, then complains about missing arguments etc

What do you think?

Need to loosen the tight control of the config name

With the introduction of $__EXEC_FILENAME the config can go with the executable now. However, now the problem is that the config MUST be named precisely according to the executable -- if people want to name it to something else, e.g., like this, things will be broken again.

I think it is time to loosen the tight control of how the config should be named, and give the freedom back to the user. I.e., even when people name their config file to whatever they like, the self-config file should still work.

Please consider.

PS. for my daily job, I have 3 or 4 different tools doing different things, but they all rely on a central config file. I put the exes together and they all use the same config file. I know this might be a rare case, but I do consider it a very valid one.

Please reconsider -- people need the freedom, not the unnecessary restriction. Thx.

Wish: YAML support

Hi 仕晋,

Hope you will consider adding YAML support as the config file format. I know it is almost 98% like json, however the difference is significant in expressiveness to me. E.g., please take a look at the following same info represented in YAML and json:

foo: "bar"
baz: 
  - "qux"
  - "quxx"
corge: null
grault: 1
garply: true
waldo: "false"
fred: "undefined"
emptyArray: []
emptyObject: {}
emptyString: ""
{
  "foo": "bar",
  "baz": [
    "qux",
    "quxx"
  ],
  "corge": null,
  "grault": 1,
  "garply": true,
  "waldo": "false",
  "fred": "undefined",
  "emptyArray": [],
  "emptyObject": {},
  "emptyString": ""
}

I had almost wanted to give up YAML and use json as the config file format, but once I started doing it, I realize it is going backward in expressiveness.

Please consider.

Thanks

Printing self help

Hi @mkideal,

Given a program, say https://github.com/mkideal/cli/blob/master/_examples/002-flag/main.go, if I want to do further logical process of the parameters, how do I print a self help if the given parameter is invalid.

Again, using https://github.com/mkideal/cli/blob/master/_examples/002-flag/main.go as the example, how to do:

 if argv.Port < 1024 { print self help }

where the self help is the output running the program with -h:

Options:

  -h, --help     display help information
  -p, --port     short and long format flags both are supported
  -x             boolean type
  -y             boolean type, too

thx!

Usage text for sub commands

Hi Pal,

Are we able to provide usage text for sub commands?

When there is no sub-commands, I can manage to get an output like this:

$ picv -h
picture vault
built on 2017-06-03

Tool to deal with camera pictures and put them in vault

Usage:
  picv [Options] dir [dirs...]

Options:

  -h, --help       display help information
 ...

Notice that "Usage:" line? It's come from here.

However when with sub commands, how can I provide a "Usage:" text like above, into the following?

$ picv cut -h 
Separate picture into groups

Options:

  -h, --help       display help information
 ...

Thanks

Enable Wiki

Hi 仕晋,

Please enable your CLI's Wiki.

I've got the following code automatically generated:

// -*- go -*-
package main

import (
    "github.com/mkideal/cli"
)

var _ = app.Register(&cli.Command{
    Name: "build",
    Desc: "Build golang application",
    Text: "Usage:\n  gogo build [Options] Arch(i386|amd64)",
    Argv: func() interface{} { return new(buildT) },
    Fn:   build,

    NumArg:      cli.ExactN(1),
    CanSubRoute: true,
})

type buildT struct {
    cli.Helper
    Dir  string  `cli:"dir" usage:"source code root dir" dft:"./"`
    Suffix  string  `cli:"suffix" usage:"source file suffix" dft:".go,.c,.s"`
    Out  string  `cli:"o,out" usage:"output filename"`
}

func build(ctx *cli.Context) error {
    argv := ctx.Argv().(*buildT)
    ctx.String("%s: %v", ctx.Path(), jsonIndent(argv))
    ctx.String("[build]: %v\n", ctx.Args())

    return nil
}

package main

import (
    "github.com/mkideal/cli"
)

var _ = app.Register(&cli.Command{
    Name: "install",
    Desc: "Install golang application",
    Text: "Usage:\n  gogo install [Options] package [package...]",
    Argv: func() interface{} { return new(installT) },
    Fn:   install,

    NumArg:      cli.AtLeast(1),
    CanSubRoute: true,
})

type installT struct {
    cli.Helper
    Dir  string  `cli:"dir" usage:"source code root dir" dft:"./"`
    Suffix  string  `cli:"suffix" usage:"source file suffix" dft:".go,.c,.s"`
    Out  string  `cli:"o,out" usage:"output filename"`
}

func install(ctx *cli.Context) error {
    argv := ctx.Argv().(*installT)
    ctx.String("%s: %v", ctx.Path(), jsonIndent(argv))
    ctx.String("[install]: %v\n", ctx.Args())

    return nil
}

I can explain how in your enabled CLI's Wiki.

Please expose the json conf reading function

Hiya,

Please expose the json conf reading function, i.e., the function behind the Self ... json:"-" parser:"jsonfile"... support.

I found myself need to deal with different json conf files for exactly the same functionality. So far, I've duplicated the CLI twice and now three times, just in order to to take advantage of the auto-magical "Self" reading feature.

Now I need to do more. So, once I can use the function to read different json conf files, I can consolidate those three (and more) sub commands into one.

Thanks

Abnormal with root options

Hi again 仕晋,

I found two issues with root options (i.e., options not for sub-commands). I've built a small example to show that, https://github.com/suntong/lang/tree/master/lang/Go/src/sys/CLI/015-fileI

  • issue 1, cli.AtLeast(1) works for sub-commands but not for root options
$ go run main.go 
ERR! required argument --name missing
ERR! required argument --tag missing

It should show usage instead of complaining.

  • issue 2, self config json not working.
$ cat fi.json 
{ "ID": "1234" }

$ go run main.go -n NN -t TT
{"Help":false,"Name":"NN","Tag":"TT","ID":"","Fi":{}}{"Help":false,"Name":"NN","Tag":"TT","ID":"","Fi":{}}

The "ID" should be "1234" instead of empty, right?

Terminate with non-zero exit code in case of an error

Consider the following example:

package main

import (
	"fmt"

	"github.com/mkideal/cli"
)

type args struct {
	cli.Helper
	Param string `cli:"*p,param" usage:"whatever"`
}

func main() {
	cli.Run(new(args), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*args)
		ctx.String("parameter was %s\n", argv.Param)
		return fmt.Errorf("Some random error")
	})
}

Now let us run this example:

ryu@gaku ~ % go build test.go
ryu@gaku ~ % ./test
ERR! required parameter --param missing
ryu@gaku ~ % echo $?
0
ryu@gaku ~ % ./test --param 1
parameter was 1
Some random error
ryu@gaku ~ % echo $?
0
ryu@gaku ~ % 

So apparently in case of ./test, the exit code was 0. The same holds true for ./text --param 1. In both cases, something went wrong (invalid parameter values, CommandFunc returned an error). It is common practice (and necessary requirement for every sysadmin to detect problems by looking for non-zero exit codes) to return non-zero if the software could not complete its main task correctly.

Technically, it seems to be the responsibility of the main routine to return the exit code. However, the whole information regarding the exit code is lost in the abstractions of cli. Therefore I am requesting for appropriate fixes to give a proper exit code.

Dealing with regexp in config

Hiya,

How to put regexp in the config?

Please try your 027-global-option or mine with the following in the config file:

{
        "host": "regexp\w+test",
        "port": 8080
}

That will give an error:

ERR! invalid character 'w' in string escape code

If I escape the \ like this:

{
        "host": "regexp\\w+test",
        "port": 8080
}

the content would be wrong:

{"Help":false,"Host":"regexp\\w+test","Port":8080}
                            ^^

If I use single quote in the config file, will in turn get:

ERR! invalid character '\'' looking for beginning of value

Need to deal with the escape sequences

Hi 王仕晋,

For the following,

Desc: "Golang package manager",
Text: `  gogo is a new golang package manager\n  very very good`,

I'll get

Golang package manager

  gogo is a new golang package manager\n  very very good

The github.com/voxelbrain/goptions was able to handle the escaping nicely, so should CLI. After all, the escaping sequence is a fundamental feature of modern programming language.

I'll change my sample code at https://github.com/suntong/lang/blob/master/lang/Go/src/sys/CommandLineGoptions.go to show that goptions handles the escaping nicely.

Done,

    Help      goptions.Help `goptions:"-h, --help, description='Show this help\n\nSub-commands (Verbs):\n\n\texecute\t\tExecute it\n\t\t\tExecute the given command\n\n\tdelete\t\tDelete it'"`
$ go run CommandLineGoptions.go
Usage: CommandLineGoptions [global options] <verb> [verb options]

Global options:
        -s, --server   Server to connect to
        -p, --password Don't prompt for password
        -t, --timeout  Connection timeout in seconds (default: 10s)
        -v, --verbose  Be verbose
        -q, --quiet    Do not print anything, even errors (except if --verbose is specified)
        -h, --help     Show this help

Sub-commands (Verbs):

    execute     Execute it
                Execute the given command

    delete     Delete it

Verbs:
    delete:
           -n, --name    Name of the entity to be deleted (*)
           -f, --force   Force removal
    execute:
           -c, --command Command to exectute (*)
               --script  Script to exectute

Please enable escape sequence handling.

Thanks

Catching up

So glad that the project wakes up from the dormant stage. Now would you think merging the changes from the temp repo that I maintained while you were away is a good idea, @mkideal?

I know you might not agree with every changes that we made, but please consider each request legitimate, and as long as it get the job done I believe cli users will be happy. I.e., please review each request and the fixed in https://github.com/go-easygen/cli/commits/master that people did.

OK, @mkideal? Thx!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.