pax_global_header00006660000000000000000000000064136407355030014520gustar00rootroot0000000000000052 comment=053ba9dd02d2bfb768da2c1c50370c59d41dc04b cli-1.22.4/000077500000000000000000000000001364073550300123555ustar00rootroot00000000000000cli-1.22.4/.flake8000066400000000000000000000000371364073550300135300ustar00rootroot00000000000000[flake8] max-line-length = 120 cli-1.22.4/.github/000077500000000000000000000000001364073550300137155ustar00rootroot00000000000000cli-1.22.4/.github/.codecov.yml000066400000000000000000000000161364073550300161350ustar00rootroot00000000000000comment: falsecli-1.22.4/.github/CODEOWNERS000066400000000000000000000001641364073550300153110ustar00rootroot00000000000000# See https://help.github.com/articles/about-codeowners/ # for more info about CODEOWNERS file * @urfave/cli cli-1.22.4/.github/workflows/000077500000000000000000000000001364073550300157525ustar00rootroot00000000000000cli-1.22.4/.github/workflows/cli.yml000066400000000000000000000033311364073550300172440ustar00rootroot00000000000000name: Run Tests on: pull_request: branches: - master - v1 env: GO111MODULE: on GOPROXY: https://proxy.golang.org jobs: test: strategy: matrix: os: [ubuntu-latest, macos-latest] go: [1.11, 1.12, 1.13] name: ${{ matrix.os }} @ Go ${{ matrix.go }} runs-on: ${{ matrix.os }} steps: - name: Set up Go ${{ matrix.go }} uses: actions/setup-go@v1 with: go-version: ${{ matrix.go }} - name: Set GOPATH and PATH run: | echo "::set-env name=GOPATH::$(dirname $GITHUB_WORKSPACE)" echo "::add-path::$(dirname $GITHUB_WORKSPACE)/bin" shell: bash - name: Checkout Code uses: actions/checkout@v1 with: ref: ${{ github.ref }} - name: Install Dependencies run: | mkdir -p $GOPATH/bin curl -L -o $GOPATH/bin/gfmrun "https://github.com/urfave/gfmrun/releases/download/v1.2.14/gfmrun-$(go env GOOS)-amd64-v1.2.14" chmod +x $GOPATH/bin/gfmrun npm install markdown-toc - name: Run Tests (v1) if: contains(github.base_ref, 'v1') run: | go run build.go vet go run build.go test go run build.go gfmrun docs/v1/manual.md go run build.go toc docs/v1/manual.md - name: Run Tests (v2) if: contains(github.base_ref, 'master') run: | go run build.go vet go run build.go test go run build.go gfmrun docs/v2/manual.md go run build.go toc docs/v2/manual.md - name: Send Coverage Report if: success() env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} run: bash <(curl -s https://codecov.io/bash)cli-1.22.4/.gitignore000066400000000000000000000000511364073550300143410ustar00rootroot00000000000000*.coverprofile node_modules/ vendor .ideacli-1.22.4/CODE_OF_CONDUCT.md000066400000000000000000000062251364073550300151610ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting Dan Buch at dan@meatballhat.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org cli-1.22.4/LICENSE000066400000000000000000000020741364073550300133650ustar00rootroot00000000000000MIT License Copyright (c) 2016 Jeremy Saenz & Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cli-1.22.4/README.md000066400000000000000000000036511364073550300136410ustar00rootroot00000000000000cli === [![Build Status](https://travis-ci.org/urfave/cli.svg?branch=master)](https://travis-ci.org/urfave/cli) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli) [![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli) [![codebeat](https://codebeat.co/badges/0a8f30aa-f975-404b-b878-5fab3ae1cc5f)](https://codebeat.co/projects/github-com-urfave-cli) [![Go Report Card](https://goreportcard.com/badge/urfave/cli)](https://goreportcard.com/report/urfave/cli) [![codecov](https://codecov.io/gh/urfave/cli/branch/master/graph/badge.svg)](https://codecov.io/gh/urfave/cli) cli is a simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way. ## Usage Documentation Usage documentation exists for each major version - `v1` - [./docs/v1/manual.md](./docs/v1/manual.md) - `v2` - 🚧 documentation for `v2` is WIP 🚧 ## Installation Make sure you have a working Go environment. Go version 1.10+ is supported. [See the install instructions for Go](http://golang.org/doc/install.html). ### GOPATH Make sure your `PATH` includes the `$GOPATH/bin` directory so your commands can be easily used: ``` export PATH=$PATH:$GOPATH/bin ``` ### Supported platforms cli is tested against multiple versions of Go on Linux, and against the latest released version of Go on OS X and Windows. For full details, see [`./.travis.yml`](./.travis.yml) and [`./appveyor.yml`](./appveyor.yml). ### Using `v1` releases ``` $ go get github.com/urfave/cli ``` ```go ... import ( "github.com/urfave/cli" ) ... ``` ### Using `v2` releases **Warning**: `v2` is in a pre-release state. ``` $ go get github.com/urfave/cli.v2 ``` ```go ... import ( "github.com/urfave/cli.v2" // imports as package "cli" ) ... ``` cli-1.22.4/altsrc/000077500000000000000000000000001364073550300136455ustar00rootroot00000000000000cli-1.22.4/altsrc/flag.go000066400000000000000000000165311364073550300151130ustar00rootroot00000000000000package altsrc import ( "fmt" "strconv" "strings" "syscall" "github.com/urfave/cli" ) // FlagInputSourceExtension is an extension interface of cli.Flag that // allows a value to be set on the existing parsed flags. type FlagInputSourceExtension interface { cli.Flag ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error } // ApplyInputSourceValues iterates over all provided flags and // executes ApplyInputSourceValue on flags implementing the // FlagInputSourceExtension interface to initialize these flags // to an alternate input source. func ApplyInputSourceValues(context *cli.Context, inputSourceContext InputSourceContext, flags []cli.Flag) error { for _, f := range flags { inputSourceExtendedFlag, isType := f.(FlagInputSourceExtension) if isType { err := inputSourceExtendedFlag.ApplyInputSourceValue(context, inputSourceContext) if err != nil { return err } } } return nil } // InitInputSource is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new // input source based on the func provided. If there is no error it will then apply the new input source to any flags // that are supported by the input source func InitInputSource(flags []cli.Flag, createInputSource func() (InputSourceContext, error)) cli.BeforeFunc { return func(context *cli.Context) error { inputSource, err := createInputSource() if err != nil { return fmt.Errorf("Unable to create input source: inner error: \n'%v'", err.Error()) } return ApplyInputSourceValues(context, inputSource, flags) } } // InitInputSourceWithContext is used to to setup an InputSourceContext on a cli.Command Before method. It will create a new // input source based on the func provided with potentially using existing cli.Context values to initialize itself. If there is // no error it will then apply the new input source to any flags that are supported by the input source func InitInputSourceWithContext(flags []cli.Flag, createInputSource func(context *cli.Context) (InputSourceContext, error)) cli.BeforeFunc { return func(context *cli.Context) error { inputSource, err := createInputSource(context) if err != nil { return fmt.Errorf("Unable to create input source with context: inner error: \n'%v'", err.Error()) } return ApplyInputSourceValues(context, inputSource, flags) } } // ApplyInputSourceValue applies a generic value to the flagSet if required func (f *GenericFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { value, err := isc.Generic(f.GenericFlag.Name) if err != nil { return err } if value != nil { eachName(f.Name, func(name string) { _ = f.set.Set(f.Name, value.String()) }) } } } return nil } // ApplyInputSourceValue applies a StringSlice value to the flagSet if required func (f *StringSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { value, err := isc.StringSlice(f.StringSliceFlag.Name) if err != nil { return err } if value != nil { var sliceValue cli.StringSlice = value eachName(f.Name, func(name string) { underlyingFlag := f.set.Lookup(f.Name) if underlyingFlag != nil { underlyingFlag.Value = &sliceValue } }) } } } return nil } // ApplyInputSourceValue applies a IntSlice value if required func (f *IntSliceFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { value, err := isc.IntSlice(f.IntSliceFlag.Name) if err != nil { return err } if value != nil { var sliceValue cli.IntSlice = value eachName(f.Name, func(name string) { underlyingFlag := f.set.Lookup(f.Name) if underlyingFlag != nil { underlyingFlag.Value = &sliceValue } }) } } } return nil } // ApplyInputSourceValue applies a Bool value to the flagSet if required func (f *BoolFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { value, err := isc.Bool(f.BoolFlag.Name) if err != nil { return err } if value { eachName(f.Name, func(name string) { _ = f.set.Set(f.Name, strconv.FormatBool(value)) }) } } } return nil } // ApplyInputSourceValue applies a BoolT value to the flagSet if required func (f *BoolTFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !context.IsSet(f.Name) && !isEnvVarSet(f.EnvVar) { value, err := isc.BoolT(f.BoolTFlag.Name) if err != nil { return err } if !value { eachName(f.Name, func(name string) { _ = f.set.Set(f.Name, strconv.FormatBool(value)) }) } } } return nil } // ApplyInputSourceValue applies a String value to the flagSet if required func (f *StringFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) { value, err := isc.String(f.StringFlag.Name) if err != nil { return err } if value != "" { eachName(f.Name, func(name string) { _ = f.set.Set(f.Name, value) }) } } } return nil } // ApplyInputSourceValue applies a int value to the flagSet if required func (f *IntFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) { value, err := isc.Int(f.IntFlag.Name) if err != nil { return err } if value > 0 { eachName(f.Name, func(name string) { _ = f.set.Set(f.Name, strconv.FormatInt(int64(value), 10)) }) } } } return nil } // ApplyInputSourceValue applies a Duration value to the flagSet if required func (f *DurationFlag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) { value, err := isc.Duration(f.DurationFlag.Name) if err != nil { return err } if value > 0 { eachName(f.Name, func(name string) { _ = f.set.Set(f.Name, value.String()) }) } } } return nil } // ApplyInputSourceValue applies a Float64 value to the flagSet if required func (f *Float64Flag) ApplyInputSourceValue(context *cli.Context, isc InputSourceContext) error { if f.set != nil { if !(context.IsSet(f.Name) || isEnvVarSet(f.EnvVar)) { value, err := isc.Float64(f.Float64Flag.Name) if err != nil { return err } if value > 0 { floatStr := float64ToString(value) eachName(f.Name, func(name string) { _ = f.set.Set(f.Name, floatStr) }) } } } return nil } func isEnvVarSet(envVars string) bool { for _, envVar := range strings.Split(envVars, ",") { envVar = strings.TrimSpace(envVar) if _, ok := syscall.Getenv(envVar); ok { // TODO: Can't use this for bools as // set means that it was true or false based on // Bool flag type, should work for other types return true } } return false } func float64ToString(f float64) string { return fmt.Sprintf("%v", f) } func eachName(longName string, fn func(string)) { parts := strings.Split(longName, ",") for _, name := range parts { name = strings.Trim(name, " ") fn(name) } } cli-1.22.4/altsrc/flag_generated.go000066400000000000000000000226001364073550300171230ustar00rootroot00000000000000// Code generated by fg; DO NOT EDIT. package altsrc import ( "flag" "github.com/urfave/cli" ) // BoolFlag is the flag type that wraps cli.BoolFlag to allow // for other values to be specified type BoolFlag struct { cli.BoolFlag set *flag.FlagSet } // NewBoolFlag creates a new BoolFlag func NewBoolFlag(fl cli.BoolFlag) *BoolFlag { return &BoolFlag{ BoolFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls // the wrapped BoolFlag.Apply func (f *BoolFlag) Apply(set *flag.FlagSet) { f.set = set f.BoolFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls // the wrapped BoolFlag.ApplyWithError func (f *BoolFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.BoolFlag.ApplyWithError(set) } // BoolTFlag is the flag type that wraps cli.BoolTFlag to allow // for other values to be specified type BoolTFlag struct { cli.BoolTFlag set *flag.FlagSet } // NewBoolTFlag creates a new BoolTFlag func NewBoolTFlag(fl cli.BoolTFlag) *BoolTFlag { return &BoolTFlag{ BoolTFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls // the wrapped BoolTFlag.Apply func (f *BoolTFlag) Apply(set *flag.FlagSet) { f.set = set f.BoolTFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls // the wrapped BoolTFlag.ApplyWithError func (f *BoolTFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.BoolTFlag.ApplyWithError(set) } // DurationFlag is the flag type that wraps cli.DurationFlag to allow // for other values to be specified type DurationFlag struct { cli.DurationFlag set *flag.FlagSet } // NewDurationFlag creates a new DurationFlag func NewDurationFlag(fl cli.DurationFlag) *DurationFlag { return &DurationFlag{ DurationFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls // the wrapped DurationFlag.Apply func (f *DurationFlag) Apply(set *flag.FlagSet) { f.set = set f.DurationFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls // the wrapped DurationFlag.ApplyWithError func (f *DurationFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.DurationFlag.ApplyWithError(set) } // Float64Flag is the flag type that wraps cli.Float64Flag to allow // for other values to be specified type Float64Flag struct { cli.Float64Flag set *flag.FlagSet } // NewFloat64Flag creates a new Float64Flag func NewFloat64Flag(fl cli.Float64Flag) *Float64Flag { return &Float64Flag{ Float64Flag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls // the wrapped Float64Flag.Apply func (f *Float64Flag) Apply(set *flag.FlagSet) { f.set = set f.Float64Flag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls // the wrapped Float64Flag.ApplyWithError func (f *Float64Flag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.Float64Flag.ApplyWithError(set) } // GenericFlag is the flag type that wraps cli.GenericFlag to allow // for other values to be specified type GenericFlag struct { cli.GenericFlag set *flag.FlagSet } // NewGenericFlag creates a new GenericFlag func NewGenericFlag(fl cli.GenericFlag) *GenericFlag { return &GenericFlag{ GenericFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls // the wrapped GenericFlag.Apply func (f *GenericFlag) Apply(set *flag.FlagSet) { f.set = set f.GenericFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls // the wrapped GenericFlag.ApplyWithError func (f *GenericFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.GenericFlag.ApplyWithError(set) } // Int64Flag is the flag type that wraps cli.Int64Flag to allow // for other values to be specified type Int64Flag struct { cli.Int64Flag set *flag.FlagSet } // NewInt64Flag creates a new Int64Flag func NewInt64Flag(fl cli.Int64Flag) *Int64Flag { return &Int64Flag{ Int64Flag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls // the wrapped Int64Flag.Apply func (f *Int64Flag) Apply(set *flag.FlagSet) { f.set = set f.Int64Flag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls // the wrapped Int64Flag.ApplyWithError func (f *Int64Flag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.Int64Flag.ApplyWithError(set) } // IntFlag is the flag type that wraps cli.IntFlag to allow // for other values to be specified type IntFlag struct { cli.IntFlag set *flag.FlagSet } // NewIntFlag creates a new IntFlag func NewIntFlag(fl cli.IntFlag) *IntFlag { return &IntFlag{ IntFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls // the wrapped IntFlag.Apply func (f *IntFlag) Apply(set *flag.FlagSet) { f.set = set f.IntFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls // the wrapped IntFlag.ApplyWithError func (f *IntFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.IntFlag.ApplyWithError(set) } // IntSliceFlag is the flag type that wraps cli.IntSliceFlag to allow // for other values to be specified type IntSliceFlag struct { cli.IntSliceFlag set *flag.FlagSet } // NewIntSliceFlag creates a new IntSliceFlag func NewIntSliceFlag(fl cli.IntSliceFlag) *IntSliceFlag { return &IntSliceFlag{ IntSliceFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls // the wrapped IntSliceFlag.Apply func (f *IntSliceFlag) Apply(set *flag.FlagSet) { f.set = set f.IntSliceFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls // the wrapped IntSliceFlag.ApplyWithError func (f *IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.IntSliceFlag.ApplyWithError(set) } // Int64SliceFlag is the flag type that wraps cli.Int64SliceFlag to allow // for other values to be specified type Int64SliceFlag struct { cli.Int64SliceFlag set *flag.FlagSet } // NewInt64SliceFlag creates a new Int64SliceFlag func NewInt64SliceFlag(fl cli.Int64SliceFlag) *Int64SliceFlag { return &Int64SliceFlag{ Int64SliceFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls // the wrapped Int64SliceFlag.Apply func (f *Int64SliceFlag) Apply(set *flag.FlagSet) { f.set = set f.Int64SliceFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls // the wrapped Int64SliceFlag.ApplyWithError func (f *Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.Int64SliceFlag.ApplyWithError(set) } // StringFlag is the flag type that wraps cli.StringFlag to allow // for other values to be specified type StringFlag struct { cli.StringFlag set *flag.FlagSet } // NewStringFlag creates a new StringFlag func NewStringFlag(fl cli.StringFlag) *StringFlag { return &StringFlag{ StringFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls // the wrapped StringFlag.Apply func (f *StringFlag) Apply(set *flag.FlagSet) { f.set = set f.StringFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls // the wrapped StringFlag.ApplyWithError func (f *StringFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.StringFlag.ApplyWithError(set) } // StringSliceFlag is the flag type that wraps cli.StringSliceFlag to allow // for other values to be specified type StringSliceFlag struct { cli.StringSliceFlag set *flag.FlagSet } // NewStringSliceFlag creates a new StringSliceFlag func NewStringSliceFlag(fl cli.StringSliceFlag) *StringSliceFlag { return &StringSliceFlag{ StringSliceFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls // the wrapped StringSliceFlag.Apply func (f *StringSliceFlag) Apply(set *flag.FlagSet) { f.set = set f.StringSliceFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls // the wrapped StringSliceFlag.ApplyWithError func (f *StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.StringSliceFlag.ApplyWithError(set) } // Uint64Flag is the flag type that wraps cli.Uint64Flag to allow // for other values to be specified type Uint64Flag struct { cli.Uint64Flag set *flag.FlagSet } // NewUint64Flag creates a new Uint64Flag func NewUint64Flag(fl cli.Uint64Flag) *Uint64Flag { return &Uint64Flag{ Uint64Flag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls // the wrapped Uint64Flag.Apply func (f *Uint64Flag) Apply(set *flag.FlagSet) { f.set = set f.Uint64Flag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls // the wrapped Uint64Flag.ApplyWithError func (f *Uint64Flag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.Uint64Flag.ApplyWithError(set) } // UintFlag is the flag type that wraps cli.UintFlag to allow // for other values to be specified type UintFlag struct { cli.UintFlag set *flag.FlagSet } // NewUintFlag creates a new UintFlag func NewUintFlag(fl cli.UintFlag) *UintFlag { return &UintFlag{ UintFlag: fl, set: nil } } // Apply saves the flagSet for later usage calls, then calls // the wrapped UintFlag.Apply func (f *UintFlag) Apply(set *flag.FlagSet) { f.set = set f.UintFlag.Apply(set) } // ApplyWithError saves the flagSet for later usage calls, then calls // the wrapped UintFlag.ApplyWithError func (f *UintFlag) ApplyWithError(set *flag.FlagSet) error { f.set = set return f.UintFlag.ApplyWithError(set) } cli-1.22.4/altsrc/flag_test.go000066400000000000000000000225131364073550300161470ustar00rootroot00000000000000package altsrc import ( "flag" "fmt" "os" "strings" "testing" "time" "github.com/urfave/cli" ) type testApplyInputSource struct { Flag FlagInputSourceExtension FlagName string FlagSetName string Expected string ContextValueString string ContextValue flag.Value EnvVarValue string EnvVarName string MapValue interface{} } func TestGenericApplyInputSourceValue(t *testing.T) { v := &Parser{"abc", "def"} c := runTest(t, testApplyInputSource{ Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}}), FlagName: "test", MapValue: v, }) expect(t, v, c.Generic("test")) } func TestGenericApplyInputSourceMethodContextSet(t *testing.T) { p := &Parser{"abc", "def"} c := runTest(t, testApplyInputSource{ Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}}), FlagName: "test", MapValue: &Parser{"efg", "hig"}, ContextValueString: p.String(), }) expect(t, p, c.Generic("test")) } func TestGenericApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewGenericFlag(cli.GenericFlag{Name: "test", Value: &Parser{}, EnvVar: "TEST"}), FlagName: "test", MapValue: &Parser{"efg", "hij"}, EnvVarName: "TEST", EnvVarValue: "abc,def", }) expect(t, &Parser{"abc", "def"}, c.Generic("test")) } func TestStringSliceApplyInputSourceValue(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test"}), FlagName: "test", MapValue: []interface{}{"hello", "world"}, }) expect(t, c.StringSlice("test"), []string{"hello", "world"}) } func TestStringSliceApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test"}), FlagName: "test", MapValue: []interface{}{"hello", "world"}, ContextValueString: "ohno", }) expect(t, c.StringSlice("test"), []string{"ohno"}) } func TestStringSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewStringSliceFlag(cli.StringSliceFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: []interface{}{"hello", "world"}, EnvVarName: "TEST", EnvVarValue: "oh,no", }) expect(t, c.StringSlice("test"), []string{"oh", "no"}) } func TestIntSliceApplyInputSourceValue(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test"}), FlagName: "test", MapValue: []interface{}{1, 2}, }) expect(t, c.IntSlice("test"), []int{1, 2}) } func TestIntSliceApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test"}), FlagName: "test", MapValue: []interface{}{1, 2}, ContextValueString: "3", }) expect(t, c.IntSlice("test"), []int{3}) } func TestIntSliceApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewIntSliceFlag(cli.IntSliceFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: []interface{}{1, 2}, EnvVarName: "TEST", EnvVarValue: "3,4", }) expect(t, c.IntSlice("test"), []int{3, 4}) } func TestBoolApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewBoolFlag(cli.BoolFlag{Name: "test"}), FlagName: "test", MapValue: true, }) expect(t, true, c.Bool("test")) } func TestBoolApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewBoolFlag(cli.BoolFlag{Name: "test"}), FlagName: "test", MapValue: false, ContextValueString: "true", }) expect(t, true, c.Bool("test")) } func TestBoolApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewBoolFlag(cli.BoolFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: false, EnvVarName: "TEST", EnvVarValue: "true", }) expect(t, true, c.Bool("test")) } func TestBoolTApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewBoolTFlag(cli.BoolTFlag{Name: "test"}), FlagName: "test", MapValue: false, }) expect(t, false, c.BoolT("test")) } func TestBoolTApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewBoolTFlag(cli.BoolTFlag{Name: "test"}), FlagName: "test", MapValue: true, ContextValueString: "false", }) expect(t, false, c.BoolT("test")) } func TestBoolTApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewBoolTFlag(cli.BoolTFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: true, EnvVarName: "TEST", EnvVarValue: "false", }) expect(t, false, c.BoolT("test")) } func TestStringApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewStringFlag(cli.StringFlag{Name: "test"}), FlagName: "test", MapValue: "hello", }) expect(t, "hello", c.String("test")) } func TestStringApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewStringFlag(cli.StringFlag{Name: "test"}), FlagName: "test", MapValue: "hello", ContextValueString: "goodbye", }) expect(t, "goodbye", c.String("test")) } func TestStringApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewStringFlag(cli.StringFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: "hello", EnvVarName: "TEST", EnvVarValue: "goodbye", }) expect(t, "goodbye", c.String("test")) } func TestIntApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewIntFlag(cli.IntFlag{Name: "test"}), FlagName: "test", MapValue: 15, }) expect(t, 15, c.Int("test")) } func TestIntApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewIntFlag(cli.IntFlag{Name: "test"}), FlagName: "test", MapValue: 15, ContextValueString: "7", }) expect(t, 7, c.Int("test")) } func TestIntApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: 15, EnvVarName: "TEST", EnvVarValue: "12", }) expect(t, 12, c.Int("test")) } func TestDurationApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewDurationFlag(cli.DurationFlag{Name: "test"}), FlagName: "test", MapValue: 30 * time.Second, }) expect(t, 30*time.Second, c.Duration("test")) } func TestDurationApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewDurationFlag(cli.DurationFlag{Name: "test"}), FlagName: "test", MapValue: 30 * time.Second, ContextValueString: (15 * time.Second).String(), }) expect(t, 15*time.Second, c.Duration("test")) } func TestDurationApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewDurationFlag(cli.DurationFlag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: 30 * time.Second, EnvVarName: "TEST", EnvVarValue: (15 * time.Second).String(), }) expect(t, 15*time.Second, c.Duration("test")) } func TestFloat64ApplyInputSourceMethodSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewFloat64Flag(cli.Float64Flag{Name: "test"}), FlagName: "test", MapValue: 1.3, }) expect(t, 1.3, c.Float64("test")) } func TestFloat64ApplyInputSourceMethodContextSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewFloat64Flag(cli.Float64Flag{Name: "test"}), FlagName: "test", MapValue: 1.3, ContextValueString: fmt.Sprintf("%v", 1.4), }) expect(t, 1.4, c.Float64("test")) } func TestFloat64ApplyInputSourceMethodEnvVarSet(t *testing.T) { c := runTest(t, testApplyInputSource{ Flag: NewFloat64Flag(cli.Float64Flag{Name: "test", EnvVar: "TEST"}), FlagName: "test", MapValue: 1.3, EnvVarName: "TEST", EnvVarValue: fmt.Sprintf("%v", 1.4), }) expect(t, 1.4, c.Float64("test")) } func runTest(t *testing.T, test testApplyInputSource) *cli.Context { inputSource := &MapInputSource{valueMap: map[interface{}]interface{}{test.FlagName: test.MapValue}} set := flag.NewFlagSet(test.FlagSetName, flag.ContinueOnError) c := cli.NewContext(nil, set, nil) if test.EnvVarName != "" && test.EnvVarValue != "" { _ = os.Setenv(test.EnvVarName, test.EnvVarValue) defer os.Setenv(test.EnvVarName, "") } test.Flag.Apply(set) if test.ContextValue != nil { f := set.Lookup(test.FlagName) f.Value = test.ContextValue } if test.ContextValueString != "" { _ = set.Set(test.FlagName, test.ContextValueString) } _ = test.Flag.ApplyInputSourceValue(c, inputSource) return c } type Parser [2]string func (p *Parser) Set(value string) error { parts := strings.Split(value, ",") if len(parts) != 2 { return fmt.Errorf("invalid format") } (*p)[0] = parts[0] (*p)[1] = parts[1] return nil } func (p *Parser) String() string { return fmt.Sprintf("%s,%s", p[0], p[1]) } cli-1.22.4/altsrc/helpers_test.go000066400000000000000000000006531364073550300167010ustar00rootroot00000000000000package altsrc import ( "reflect" "testing" ) func expect(t *testing.T, a interface{}, b interface{}) { if !reflect.DeepEqual(b, a) { t.Errorf("Expected %#v (type %v) - Got %#v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) } } func refute(t *testing.T, a interface{}, b interface{}) { if a == b { t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) } } cli-1.22.4/altsrc/input_source_context.go000066400000000000000000000010451364073550300204570ustar00rootroot00000000000000package altsrc import ( "time" "github.com/urfave/cli" ) // InputSourceContext is an interface used to allow // other input sources to be implemented as needed. type InputSourceContext interface { Int(name string) (int, error) Duration(name string) (time.Duration, error) Float64(name string) (float64, error) String(name string) (string, error) StringSlice(name string) ([]string, error) IntSlice(name string) ([]int, error) Generic(name string) (cli.Generic, error) Bool(name string) (bool, error) BoolT(name string) (bool, error) } cli-1.22.4/altsrc/json_command_test.go000066400000000000000000000177211364073550300177120ustar00rootroot00000000000000package altsrc import ( "flag" "io/ioutil" "os" "testing" "github.com/urfave/cli" ) const ( fileName = "current.json" simpleJSON = `{"test": 15}` nestedJSON = `{"top": {"test": 15}}` ) func TestCommandJSONFileTest(t *testing.T) { cleanup := writeTempFile(t, fileName, simpleJSON) defer cleanup() app := cli.NewApp() set := flag.NewFlagSet("test", 0) test := []string{"test-cmd", "--load", fileName} _ = set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 15) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test"}), &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandJSONFileTestGlobalEnvVarWins(t *testing.T) { cleanup := writeTempFile(t, fileName, simpleJSON) defer cleanup() app := cli.NewApp() set := flag.NewFlagSet("test", 0) _ = os.Setenv("THE_TEST", "10") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", fileName} _ = set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 10) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}), &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandJSONFileTestGlobalEnvVarWinsNested(t *testing.T) { cleanup := writeTempFile(t, fileName, nestedJSON) defer cleanup() app := cli.NewApp() set := flag.NewFlagSet("test", 0) _ = os.Setenv("THE_TEST", "10") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", fileName} _ = set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 10) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test", EnvVar: "THE_TEST"}), &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandJSONFileTestSpecifiedFlagWins(t *testing.T) { cleanup := writeTempFile(t, fileName, simpleJSON) defer cleanup() app := cli.NewApp() set := flag.NewFlagSet("test", 0) test := []string{"test-cmd", "--load", fileName, "--test", "7"} _ = set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 7) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test"}), &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandJSONFileTestSpecifiedFlagWinsNested(t *testing.T) { cleanup := writeTempFile(t, fileName, nestedJSON) defer cleanup() app := cli.NewApp() set := flag.NewFlagSet("test", 0) test := []string{"test-cmd", "--load", fileName, "--top.test", "7"} _ = set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 7) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test"}), &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandJSONFileTestDefaultValueFileWins(t *testing.T) { cleanup := writeTempFile(t, fileName, simpleJSON) defer cleanup() app := cli.NewApp() set := flag.NewFlagSet("test", 0) test := []string{"test-cmd", "--load", fileName} _ = set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 15) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test", Value: 7}), &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandJSONFileTestDefaultValueFileWinsNested(t *testing.T) { cleanup := writeTempFile(t, fileName, nestedJSON) defer cleanup() app := cli.NewApp() set := flag.NewFlagSet("test", 0) test := []string{"test-cmd", "--load", fileName} _ = set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 15) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7}), &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWins(t *testing.T) { cleanup := writeTempFile(t, fileName, simpleJSON) defer cleanup() app := cli.NewApp() set := flag.NewFlagSet("test", 0) _ = os.Setenv("THE_TEST", "11") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", fileName} _ = set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 11) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVar: "THE_TEST"}), &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWinsNested(t *testing.T) { cleanup := writeTempFile(t, fileName, nestedJSON) defer cleanup() app := cli.NewApp() set := flag.NewFlagSet("test", 0) _ = os.Setenv("THE_TEST", "11") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", fileName} _ = set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 11) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7, EnvVar: "THE_TEST"}), &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func writeTempFile(t *testing.T, name string, content string) func() { if err := ioutil.WriteFile(name, []byte(content), 0666); err != nil { t.Fatalf("cannot write %q: %v", name, err) } return func() { if err := os.Remove(name); err != nil { t.Errorf("cannot remove %q: %v", name, err) } } } cli-1.22.4/altsrc/json_source_context.go000066400000000000000000000120451364073550300202730ustar00rootroot00000000000000package altsrc import ( "encoding/json" "fmt" "io" "io/ioutil" "strings" "time" "github.com/urfave/cli" ) // NewJSONSourceFromFlagFunc returns a func that takes a cli.Context // and returns an InputSourceContext suitable for retrieving config // variables from a file containing JSON data with the file name defined // by the given flag. func NewJSONSourceFromFlagFunc(flag string) func(c *cli.Context) (InputSourceContext, error) { return func(context *cli.Context) (InputSourceContext, error) { return NewJSONSourceFromFile(context.String(flag)) } } // NewJSONSourceFromFile returns an InputSourceContext suitable for // retrieving config variables from a file (or url) containing JSON // data. func NewJSONSourceFromFile(f string) (InputSourceContext, error) { data, err := loadDataFrom(f) if err != nil { return nil, err } return NewJSONSource(data) } // NewJSONSourceFromReader returns an InputSourceContext suitable for // retrieving config variables from an io.Reader that returns JSON data. func NewJSONSourceFromReader(r io.Reader) (InputSourceContext, error) { data, err := ioutil.ReadAll(r) if err != nil { return nil, err } return NewJSONSource(data) } // NewJSONSource returns an InputSourceContext suitable for retrieving // config variables from raw JSON data. func NewJSONSource(data []byte) (InputSourceContext, error) { var deserialized map[string]interface{} if err := json.Unmarshal(data, &deserialized); err != nil { return nil, err } return &jsonSource{deserialized: deserialized}, nil } func (x *jsonSource) Int(name string) (int, error) { i, err := x.getValue(name) if err != nil { return 0, err } switch v := i.(type) { default: return 0, fmt.Errorf("unexpected type %T for %q", i, name) case int: return v, nil case float64: return int(float64(v)), nil case float32: return int(float32(v)), nil } } func (x *jsonSource) Duration(name string) (time.Duration, error) { i, err := x.getValue(name) if err != nil { return 0, err } v, ok := (time.Duration)(0), false if v, ok = i.(time.Duration); !ok { return v, fmt.Errorf("unexpected type %T for %q", i, name) } return v, nil } func (x *jsonSource) Float64(name string) (float64, error) { i, err := x.getValue(name) if err != nil { return 0, err } v, ok := (float64)(0), false if v, ok = i.(float64); !ok { return v, fmt.Errorf("unexpected type %T for %q", i, name) } return v, nil } func (x *jsonSource) String(name string) (string, error) { i, err := x.getValue(name) if err != nil { return "", err } v, ok := "", false if v, ok = i.(string); !ok { return v, fmt.Errorf("unexpected type %T for %q", i, name) } return v, nil } func (x *jsonSource) StringSlice(name string) ([]string, error) { i, err := x.getValue(name) if err != nil { return nil, err } switch v := i.(type) { default: return nil, fmt.Errorf("unexpected type %T for %q", i, name) case []string: return v, nil case []interface{}: c := []string{} for _, s := range v { if str, ok := s.(string); ok { c = append(c, str) } else { return c, fmt.Errorf("unexpected item type %T in %T for %q", s, c, name) } } return c, nil } } func (x *jsonSource) IntSlice(name string) ([]int, error) { i, err := x.getValue(name) if err != nil { return nil, err } switch v := i.(type) { default: return nil, fmt.Errorf("unexpected type %T for %q", i, name) case []int: return v, nil case []interface{}: c := []int{} for _, s := range v { if i2, ok := s.(int); ok { c = append(c, i2) } else { return c, fmt.Errorf("unexpected item type %T in %T for %q", s, c, name) } } return c, nil } } func (x *jsonSource) Generic(name string) (cli.Generic, error) { i, err := x.getValue(name) if err != nil { return nil, err } v, ok := (cli.Generic)(nil), false if v, ok = i.(cli.Generic); !ok { return v, fmt.Errorf("unexpected type %T for %q", i, name) } return v, nil } func (x *jsonSource) Bool(name string) (bool, error) { i, err := x.getValue(name) if err != nil { return false, err } v, ok := false, false if v, ok = i.(bool); !ok { return v, fmt.Errorf("unexpected type %T for %q", i, name) } return v, nil } // since this source appears to require all configuration to be specified, the // concept of a boolean defaulting to true seems inconsistent with no defaults func (x *jsonSource) BoolT(name string) (bool, error) { return false, fmt.Errorf("unsupported type BoolT for JSONSource") } func (x *jsonSource) getValue(key string) (interface{}, error) { return jsonGetValue(key, x.deserialized) } func jsonGetValue(key string, m map[string]interface{}) (interface{}, error) { var ret interface{} var ok bool working := m keys := strings.Split(key, ".") for ix, k := range keys { if ret, ok = working[k]; !ok { return ret, fmt.Errorf("missing key %q", key) } if working, ok = ret.(map[string]interface{}); !ok { if ix < len(keys)-1 { return ret, fmt.Errorf("unexpected intermediate value at %q segment of %q: %T", k, key, ret) } } } return ret, nil } type jsonSource struct { deserialized map[string]interface{} } cli-1.22.4/altsrc/map_input_source.go000066400000000000000000000162141364073550300175540ustar00rootroot00000000000000package altsrc import ( "fmt" "reflect" "strings" "time" "github.com/urfave/cli" ) // MapInputSource implements InputSourceContext to return // data from the map that is loaded. type MapInputSource struct { valueMap map[interface{}]interface{} } // nestedVal checks if the name has '.' delimiters. // If so, it tries to traverse the tree by the '.' delimited sections to find // a nested value for the key. func nestedVal(name string, tree map[interface{}]interface{}) (interface{}, bool) { if sections := strings.Split(name, "."); len(sections) > 1 { node := tree for _, section := range sections[:len(sections)-1] { child, ok := node[section] if !ok { return nil, false } ctype, ok := child.(map[interface{}]interface{}) if !ok { return nil, false } node = ctype } if val, ok := node[sections[len(sections)-1]]; ok { return val, true } } return nil, false } // Int returns an int from the map if it exists otherwise returns 0 func (fsm *MapInputSource) Int(name string) (int, error) { otherGenericValue, exists := fsm.valueMap[name] if exists { otherValue, isType := otherGenericValue.(int) if !isType { return 0, incorrectTypeForFlagError(name, "int", otherGenericValue) } return otherValue, nil } nestedGenericValue, exists := nestedVal(name, fsm.valueMap) if exists { otherValue, isType := nestedGenericValue.(int) if !isType { return 0, incorrectTypeForFlagError(name, "int", nestedGenericValue) } return otherValue, nil } return 0, nil } // Duration returns a duration from the map if it exists otherwise returns 0 func (fsm *MapInputSource) Duration(name string) (time.Duration, error) { otherGenericValue, exists := fsm.valueMap[name] if exists { otherValue, isType := otherGenericValue.(time.Duration) if !isType { return 0, incorrectTypeForFlagError(name, "duration", otherGenericValue) } return otherValue, nil } nestedGenericValue, exists := nestedVal(name, fsm.valueMap) if exists { otherValue, isType := nestedGenericValue.(time.Duration) if !isType { return 0, incorrectTypeForFlagError(name, "duration", nestedGenericValue) } return otherValue, nil } return 0, nil } // Float64 returns an float64 from the map if it exists otherwise returns 0 func (fsm *MapInputSource) Float64(name string) (float64, error) { otherGenericValue, exists := fsm.valueMap[name] if exists { otherValue, isType := otherGenericValue.(float64) if !isType { return 0, incorrectTypeForFlagError(name, "float64", otherGenericValue) } return otherValue, nil } nestedGenericValue, exists := nestedVal(name, fsm.valueMap) if exists { otherValue, isType := nestedGenericValue.(float64) if !isType { return 0, incorrectTypeForFlagError(name, "float64", nestedGenericValue) } return otherValue, nil } return 0, nil } // String returns a string from the map if it exists otherwise returns an empty string func (fsm *MapInputSource) String(name string) (string, error) { otherGenericValue, exists := fsm.valueMap[name] if exists { otherValue, isType := otherGenericValue.(string) if !isType { return "", incorrectTypeForFlagError(name, "string", otherGenericValue) } return otherValue, nil } nestedGenericValue, exists := nestedVal(name, fsm.valueMap) if exists { otherValue, isType := nestedGenericValue.(string) if !isType { return "", incorrectTypeForFlagError(name, "string", nestedGenericValue) } return otherValue, nil } return "", nil } // StringSlice returns an []string from the map if it exists otherwise returns nil func (fsm *MapInputSource) StringSlice(name string) ([]string, error) { otherGenericValue, exists := fsm.valueMap[name] if !exists { otherGenericValue, exists = nestedVal(name, fsm.valueMap) if !exists { return nil, nil } } otherValue, isType := otherGenericValue.([]interface{}) if !isType { return nil, incorrectTypeForFlagError(name, "[]interface{}", otherGenericValue) } var stringSlice = make([]string, 0, len(otherValue)) for i, v := range otherValue { stringValue, isType := v.(string) if !isType { return nil, incorrectTypeForFlagError(fmt.Sprintf("%s[%d]", name, i), "string", v) } stringSlice = append(stringSlice, stringValue) } return stringSlice, nil } // IntSlice returns an []int from the map if it exists otherwise returns nil func (fsm *MapInputSource) IntSlice(name string) ([]int, error) { otherGenericValue, exists := fsm.valueMap[name] if !exists { otherGenericValue, exists = nestedVal(name, fsm.valueMap) if !exists { return nil, nil } } otherValue, isType := otherGenericValue.([]interface{}) if !isType { return nil, incorrectTypeForFlagError(name, "[]interface{}", otherGenericValue) } var intSlice = make([]int, 0, len(otherValue)) for i, v := range otherValue { intValue, isType := v.(int) if !isType { return nil, incorrectTypeForFlagError(fmt.Sprintf("%s[%d]", name, i), "int", v) } intSlice = append(intSlice, intValue) } return intSlice, nil } // Generic returns an cli.Generic from the map if it exists otherwise returns nil func (fsm *MapInputSource) Generic(name string) (cli.Generic, error) { otherGenericValue, exists := fsm.valueMap[name] if exists { otherValue, isType := otherGenericValue.(cli.Generic) if !isType { return nil, incorrectTypeForFlagError(name, "cli.Generic", otherGenericValue) } return otherValue, nil } nestedGenericValue, exists := nestedVal(name, fsm.valueMap) if exists { otherValue, isType := nestedGenericValue.(cli.Generic) if !isType { return nil, incorrectTypeForFlagError(name, "cli.Generic", nestedGenericValue) } return otherValue, nil } return nil, nil } // Bool returns an bool from the map otherwise returns false func (fsm *MapInputSource) Bool(name string) (bool, error) { otherGenericValue, exists := fsm.valueMap[name] if exists { otherValue, isType := otherGenericValue.(bool) if !isType { return false, incorrectTypeForFlagError(name, "bool", otherGenericValue) } return otherValue, nil } nestedGenericValue, exists := nestedVal(name, fsm.valueMap) if exists { otherValue, isType := nestedGenericValue.(bool) if !isType { return false, incorrectTypeForFlagError(name, "bool", nestedGenericValue) } return otherValue, nil } return false, nil } // BoolT returns an bool from the map otherwise returns true func (fsm *MapInputSource) BoolT(name string) (bool, error) { otherGenericValue, exists := fsm.valueMap[name] if exists { otherValue, isType := otherGenericValue.(bool) if !isType { return true, incorrectTypeForFlagError(name, "bool", otherGenericValue) } return otherValue, nil } nestedGenericValue, exists := nestedVal(name, fsm.valueMap) if exists { otherValue, isType := nestedGenericValue.(bool) if !isType { return true, incorrectTypeForFlagError(name, "bool", nestedGenericValue) } return otherValue, nil } return true, nil } func incorrectTypeForFlagError(name, expectedTypeName string, value interface{}) error { valueType := reflect.TypeOf(value) valueTypeName := "" if valueType != nil { valueTypeName = valueType.Name() } return fmt.Errorf("Mismatched type for flag '%s'. Expected '%s' but actual is '%s'", name, expectedTypeName, valueTypeName) } cli-1.22.4/altsrc/toml_command_test.go000066400000000000000000000177101364073550300177120ustar00rootroot00000000000000// Disabling building of toml support in cases where golang is 1.0 or 1.1 // as the encoding library is not implemented or supported. // +build go1.2 package altsrc import ( "flag" "io/ioutil" "os" "testing" "github.com/urfave/cli" ) func TestCommandTomFileTest(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") test := []string{"test-cmd", "--load", "current.toml"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 15) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileTestGlobalEnvVarWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") os.Setenv("THE_TEST", "10") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", "current.toml"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 10) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileTestGlobalEnvVarWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) defer os.Remove("current.toml") os.Setenv("THE_TEST", "10") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", "current.toml"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 10) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test", EnvVar: "THE_TEST"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileTestSpecifiedFlagWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") test := []string{"test-cmd", "--load", "current.toml", "--test", "7"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 7) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileTestSpecifiedFlagWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte(`[top] test = 15`), 0666) defer os.Remove("current.toml") test := []string{"test-cmd", "--load", "current.toml", "--top.test", "7"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 7) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileTestDefaultValueFileWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") test := []string{"test-cmd", "--load", "current.toml"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 15) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test", Value: 7}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileTestDefaultValueFileWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) defer os.Remove("current.toml") test := []string{"test-cmd", "--load", "current.toml"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 15) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("test = 15"), 0666) defer os.Remove("current.toml") os.Setenv("THE_TEST", "11") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", "current.toml"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 11) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVar: "THE_TEST"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.toml", []byte("[top]\ntest = 15"), 0666) defer os.Remove("current.toml") os.Setenv("THE_TEST", "11") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", "current.toml"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 11) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7, EnvVar: "THE_TEST"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } cli-1.22.4/altsrc/toml_file_loader.go000066400000000000000000000054551364073550300175050ustar00rootroot00000000000000// Disabling building of toml support in cases where golang is 1.0 or 1.1 // as the encoding library is not implemented or supported. // +build go1.2 package altsrc import ( "fmt" "reflect" "github.com/BurntSushi/toml" "github.com/urfave/cli" ) type tomlMap struct { Map map[interface{}]interface{} } func unmarshalMap(i interface{}) (ret map[interface{}]interface{}, err error) { ret = make(map[interface{}]interface{}) m := i.(map[string]interface{}) for key, val := range m { v := reflect.ValueOf(val) switch v.Kind() { case reflect.Bool: ret[key] = val.(bool) case reflect.String: ret[key] = val.(string) case reflect.Int: ret[key] = int(val.(int)) case reflect.Int8: ret[key] = int(val.(int8)) case reflect.Int16: ret[key] = int(val.(int16)) case reflect.Int32: ret[key] = int(val.(int32)) case reflect.Int64: ret[key] = int(val.(int64)) case reflect.Uint: ret[key] = int(val.(uint)) case reflect.Uint8: ret[key] = int(val.(uint8)) case reflect.Uint16: ret[key] = int(val.(uint16)) case reflect.Uint32: ret[key] = int(val.(uint32)) case reflect.Uint64: ret[key] = int(val.(uint64)) case reflect.Float32: ret[key] = float64(val.(float32)) case reflect.Float64: ret[key] = float64(val.(float64)) case reflect.Map: if tmp, err := unmarshalMap(val); err == nil { ret[key] = tmp } else { return nil, err } case reflect.Array, reflect.Slice: ret[key] = val.([]interface{}) default: return nil, fmt.Errorf("Unsupported: type = %#v", v.Kind()) } } return ret, nil } func (tm *tomlMap) UnmarshalTOML(i interface{}) error { if tmp, err := unmarshalMap(i); err == nil { tm.Map = tmp } else { return err } return nil } type tomlSourceContext struct { FilePath string } // NewTomlSourceFromFile creates a new TOML InputSourceContext from a filepath. func NewTomlSourceFromFile(file string) (InputSourceContext, error) { tsc := &tomlSourceContext{FilePath: file} var results tomlMap = tomlMap{} if err := readCommandToml(tsc.FilePath, &results); err != nil { return nil, fmt.Errorf("Unable to load TOML file '%s': inner error: \n'%v'", tsc.FilePath, err.Error()) } return &MapInputSource{valueMap: results.Map}, nil } // NewTomlSourceFromFlagFunc creates a new TOML InputSourceContext from a provided flag name and source context. func NewTomlSourceFromFlagFunc(flagFileName string) func(context *cli.Context) (InputSourceContext, error) { return func(context *cli.Context) (InputSourceContext, error) { filePath := context.String(flagFileName) return NewTomlSourceFromFile(filePath) } } func readCommandToml(filePath string, container interface{}) (err error) { b, err := loadDataFrom(filePath) if err != nil { return err } err = toml.Unmarshal(b, container) if err != nil { return err } err = nil return } cli-1.22.4/altsrc/yaml_command_test.go000066400000000000000000000176771364073550300177150ustar00rootroot00000000000000// Disabling building of yaml support in cases where golang is 1.0 or 1.1 // as the encoding library is not implemented or supported. // +build go1.2 package altsrc import ( "flag" "io/ioutil" "os" "testing" "github.com/urfave/cli" ) func TestCommandYamlFileTest(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666) defer os.Remove("current.yaml") test := []string{"test-cmd", "--load", "current.yaml"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 15) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666) defer os.Remove("current.yaml") os.Setenv("THE_TEST", "10") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", "current.yaml"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 10) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test", EnvVar: "THE_TEST"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.yaml", []byte(`top: test: 15`), 0666) defer os.Remove("current.yaml") os.Setenv("THE_TEST", "10") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", "current.yaml"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 10) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test", EnvVar: "THE_TEST"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666) defer os.Remove("current.yaml") test := []string{"test-cmd", "--load", "current.yaml", "--test", "7"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 7) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandYamlFileTestSpecifiedFlagWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.yaml", []byte(`top: test: 15`), 0666) defer os.Remove("current.yaml") test := []string{"test-cmd", "--load", "current.yaml", "--top.test", "7"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 7) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666) defer os.Remove("current.yaml") test := []string{"test-cmd", "--load", "current.yaml"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 15) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test", Value: 7}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandYamlFileTestDefaultValueFileWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.yaml", []byte(`top: test: 15`), 0666) defer os.Remove("current.yaml") test := []string{"test-cmd", "--load", "current.yaml"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 15) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.yaml", []byte("test: 15"), 0666) defer os.Remove("current.yaml") os.Setenv("THE_TEST", "11") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", "current.yaml"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("test") expect(t, val, 11) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "test", Value: 7, EnvVar: "THE_TEST"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWinsNested(t *testing.T) { app := cli.NewApp() set := flag.NewFlagSet("test", 0) ioutil.WriteFile("current.yaml", []byte(`top: test: 15`), 0666) defer os.Remove("current.yaml") os.Setenv("THE_TEST", "11") defer os.Setenv("THE_TEST", "") test := []string{"test-cmd", "--load", "current.yaml"} set.Parse(test) c := cli.NewContext(app, set, nil) command := &cli.Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(c *cli.Context) error { val := c.Int("top.test") expect(t, val, 11) return nil }, Flags: []cli.Flag{ NewIntFlag(cli.IntFlag{Name: "top.test", Value: 7, EnvVar: "THE_TEST"}), cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) err := command.Run(c) expect(t, err, nil) } cli-1.22.4/altsrc/yaml_file_loader.go000066400000000000000000000050471364073550300174710ustar00rootroot00000000000000// Disabling building of yaml support in cases where golang is 1.0 or 1.1 // as the encoding library is not implemented or supported. // +build go1.2 package altsrc import ( "fmt" "io/ioutil" "net/http" "net/url" "os" "runtime" "strings" "github.com/urfave/cli" "gopkg.in/yaml.v2" ) type yamlSourceContext struct { FilePath string } // NewYamlSourceFromFile creates a new Yaml InputSourceContext from a filepath. func NewYamlSourceFromFile(file string) (InputSourceContext, error) { ysc := &yamlSourceContext{FilePath: file} var results map[interface{}]interface{} err := readCommandYaml(ysc.FilePath, &results) if err != nil { return nil, fmt.Errorf("Unable to load Yaml file '%s': inner error: \n'%v'", ysc.FilePath, err.Error()) } return &MapInputSource{valueMap: results}, nil } // NewYamlSourceFromFlagFunc creates a new Yaml InputSourceContext from a provided flag name and source context. func NewYamlSourceFromFlagFunc(flagFileName string) func(context *cli.Context) (InputSourceContext, error) { return func(context *cli.Context) (InputSourceContext, error) { filePath := context.String(flagFileName) return NewYamlSourceFromFile(filePath) } } func readCommandYaml(filePath string, container interface{}) (err error) { b, err := loadDataFrom(filePath) if err != nil { return err } err = yaml.Unmarshal(b, container) if err != nil { return err } err = nil return } func loadDataFrom(filePath string) ([]byte, error) { u, err := url.Parse(filePath) if err != nil { return nil, err } if u.Host != "" { // i have a host, now do i support the scheme? switch u.Scheme { case "http", "https": res, err := http.Get(filePath) if err != nil { return nil, err } return ioutil.ReadAll(res.Body) default: return nil, fmt.Errorf("scheme of %s is unsupported", filePath) } } else if u.Path != "" { // i dont have a host, but I have a path. I am a local file. if _, notFoundFileErr := os.Stat(filePath); notFoundFileErr != nil { return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath) } return ioutil.ReadFile(filePath) } else if runtime.GOOS == "windows" && strings.Contains(u.String(), "\\") { // on Windows systems u.Path is always empty, so we need to check the string directly. if _, notFoundFileErr := os.Stat(filePath); notFoundFileErr != nil { return nil, fmt.Errorf("Cannot read from file: '%s' because it does not exist.", filePath) } return ioutil.ReadFile(filePath) } return nil, fmt.Errorf("unable to determine how to load from path %s", filePath) } cli-1.22.4/app.go000066400000000000000000000323041364073550300134660ustar00rootroot00000000000000package cli import ( "flag" "fmt" "io" "os" "path/filepath" "sort" "time" ) var ( changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md" appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL) // unused variable. commented for now. will remove in future if agreed upon by everyone //runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL) contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you." errInvalidActionType = NewExitError("ERROR invalid Action type. "+ fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+ fmt.Sprintf("See %s", appActionDeprecationURL), 2) ) // App is the main structure of a cli application. It is recommended that // an app be created with the cli.NewApp() function type App struct { // The name of the program. Defaults to path.Base(os.Args[0]) Name string // Full name of command for help, defaults to Name HelpName string // Description of the program. Usage string // Text to override the USAGE section of help UsageText string // Description of the program argument format. ArgsUsage string // Version of the program Version string // Description of the program Description string // List of commands to execute Commands []Command // List of flags to parse Flags []Flag // Boolean to enable bash completion commands EnableBashCompletion bool // Boolean to hide built-in help command HideHelp bool // Boolean to hide built-in version flag and the VERSION section of help HideVersion bool // Populate on app startup, only gettable through method Categories() categories CommandCategories // An action to execute when the bash-completion flag is set BashComplete BashCompleteFunc // An action to execute before any subcommands are run, but after the context is ready // If a non-nil error is returned, no subcommands are run Before BeforeFunc // An action to execute after any subcommands are run, but after the subcommand has finished // It is run even if Action() panics After AfterFunc // The action to execute when no subcommands are specified // Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}` // *Note*: support for the deprecated `Action` signature will be removed in a future version Action interface{} // Execute this function if the proper command cannot be found CommandNotFound CommandNotFoundFunc // Execute this function if an usage error occurs OnUsageError OnUsageErrorFunc // Compilation date Compiled time.Time // List of all authors who contributed Authors []Author // Copyright of the binary if any Copyright string // Name of Author (Note: Use App.Authors, this is deprecated) Author string // Email of Author (Note: Use App.Authors, this is deprecated) Email string // Writer writer to write output to Writer io.Writer // ErrWriter writes error output ErrWriter io.Writer // Execute this function to handle ExitErrors. If not provided, HandleExitCoder is provided to // function as a default, so this is optional. ExitErrHandler ExitErrHandlerFunc // Other custom info Metadata map[string]interface{} // Carries a function which returns app specific info. ExtraInfo func() map[string]string // CustomAppHelpTemplate the text template for app help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. CustomAppHelpTemplate string // Boolean to enable short-option handling so user can combine several // single-character bool arguements into one // i.e. foobar -o -v -> foobar -ov UseShortOptionHandling bool didSetup bool } // Tries to find out when this binary was compiled. // Returns the current time if it fails to find it. func compileTime() time.Time { info, err := os.Stat(os.Args[0]) if err != nil { return time.Now() } return info.ModTime() } // NewApp creates a new cli Application with some reasonable defaults for Name, // Usage, Version and Action. func NewApp() *App { return &App{ Name: filepath.Base(os.Args[0]), HelpName: filepath.Base(os.Args[0]), Usage: "A new cli application", UsageText: "", BashComplete: DefaultAppComplete, Action: helpCommand.Action, Compiled: compileTime(), Writer: os.Stdout, } } // Setup runs initialization code to ensure all data structures are ready for // `Run` or inspection prior to `Run`. It is internally called by `Run`, but // will return early if setup has already happened. func (a *App) Setup() { if a.didSetup { return } a.didSetup = true if a.Author != "" || a.Email != "" { a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email}) } var newCmds []Command for _, c := range a.Commands { if c.HelpName == "" { c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) } newCmds = append(newCmds, c) } a.Commands = newCmds if a.Command(helpCommand.Name) == nil && !a.HideHelp { a.Commands = append(a.Commands, helpCommand) if (HelpFlag != BoolFlag{}) { a.appendFlag(HelpFlag) } } if a.Version == "" { a.HideVersion = true } if !a.HideVersion { a.appendFlag(VersionFlag) } a.categories = CommandCategories{} for _, command := range a.Commands { a.categories = a.categories.AddCommand(command.Category, command) } sort.Sort(a.categories) if a.Metadata == nil { a.Metadata = make(map[string]interface{}) } if a.Writer == nil { a.Writer = os.Stdout } } func (a *App) newFlagSet() (*flag.FlagSet, error) { return flagSet(a.Name, a.Flags) } func (a *App) useShortOptionHandling() bool { return a.UseShortOptionHandling } // Run is the entry point to the cli app. Parses the arguments slice and routes // to the proper flag/args combination func (a *App) Run(arguments []string) (err error) { a.Setup() // handle the completion flag separately from the flagset since // completion could be attempted after a flag, but before its value was put // on the command line. this causes the flagset to interpret the completion // flag name as the value of the flag before it which is undesirable // note that we can only do this because the shell autocomplete function // always appends the completion flag at the end of the command shellComplete, arguments := checkShellCompleteFlag(a, arguments) set, err := a.newFlagSet() if err != nil { return err } err = parseIter(set, a, arguments[1:], shellComplete) nerr := normalizeFlags(a.Flags, set) context := NewContext(a, set, nil) if nerr != nil { _, _ = fmt.Fprintln(a.Writer, nerr) _ = ShowAppHelp(context) return nerr } context.shellComplete = shellComplete if checkCompletions(context) { return nil } if err != nil { if a.OnUsageError != nil { err := a.OnUsageError(context, err, false) a.handleExitCoder(context, err) return err } _, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) _ = ShowAppHelp(context) return err } if !a.HideHelp && checkHelp(context) { _ = ShowAppHelp(context) return nil } if !a.HideVersion && checkVersion(context) { ShowVersion(context) return nil } cerr := checkRequiredFlags(a.Flags, context) if cerr != nil { _ = ShowAppHelp(context) return cerr } if a.After != nil { defer func() { if afterErr := a.After(context); afterErr != nil { if err != nil { err = NewMultiError(err, afterErr) } else { err = afterErr } } }() } if a.Before != nil { beforeErr := a.Before(context) if beforeErr != nil { _, _ = fmt.Fprintf(a.Writer, "%v\n\n", beforeErr) _ = ShowAppHelp(context) a.handleExitCoder(context, beforeErr) err = beforeErr return err } } args := context.Args() if args.Present() { name := args.First() c := a.Command(name) if c != nil { return c.Run(context) } } if a.Action == nil { a.Action = helpCommand.Action } // Run default Action err = HandleAction(a.Action, context) a.handleExitCoder(context, err) return err } // RunAndExitOnError calls .Run() and exits non-zero if an error was returned // // Deprecated: instead you should return an error that fulfills cli.ExitCoder // to cli.App.Run. This will cause the application to exit with the given eror // code in the cli.ExitCoder func (a *App) RunAndExitOnError() { if err := a.Run(os.Args); err != nil { _, _ = fmt.Fprintln(a.errWriter(), err) OsExiter(1) } } // RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to // generate command-specific flags func (a *App) RunAsSubcommand(ctx *Context) (err error) { // append help to commands if len(a.Commands) > 0 { if a.Command(helpCommand.Name) == nil && !a.HideHelp { a.Commands = append(a.Commands, helpCommand) if (HelpFlag != BoolFlag{}) { a.appendFlag(HelpFlag) } } } newCmds := []Command{} for _, c := range a.Commands { if c.HelpName == "" { c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) } newCmds = append(newCmds, c) } a.Commands = newCmds set, err := a.newFlagSet() if err != nil { return err } err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete) nerr := normalizeFlags(a.Flags, set) context := NewContext(a, set, ctx) if nerr != nil { _, _ = fmt.Fprintln(a.Writer, nerr) _, _ = fmt.Fprintln(a.Writer) if len(a.Commands) > 0 { _ = ShowSubcommandHelp(context) } else { _ = ShowCommandHelp(ctx, context.Args().First()) } return nerr } if checkCompletions(context) { return nil } if err != nil { if a.OnUsageError != nil { err = a.OnUsageError(context, err, true) a.handleExitCoder(context, err) return err } _, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) _ = ShowSubcommandHelp(context) return err } if len(a.Commands) > 0 { if checkSubcommandHelp(context) { return nil } } else { if checkCommandHelp(ctx, context.Args().First()) { return nil } } cerr := checkRequiredFlags(a.Flags, context) if cerr != nil { _ = ShowSubcommandHelp(context) return cerr } if a.After != nil { defer func() { afterErr := a.After(context) if afterErr != nil { a.handleExitCoder(context, err) if err != nil { err = NewMultiError(err, afterErr) } else { err = afterErr } } }() } if a.Before != nil { beforeErr := a.Before(context) if beforeErr != nil { a.handleExitCoder(context, beforeErr) err = beforeErr return err } } args := context.Args() if args.Present() { name := args.First() c := a.Command(name) if c != nil { return c.Run(context) } } // Run default Action err = HandleAction(a.Action, context) a.handleExitCoder(context, err) return err } // Command returns the named command on App. Returns nil if the command does not exist func (a *App) Command(name string) *Command { for _, c := range a.Commands { if c.HasName(name) { return &c } } return nil } // Categories returns a slice containing all the categories with the commands they contain func (a *App) Categories() CommandCategories { return a.categories } // VisibleCategories returns a slice of categories and commands that are // Hidden=false func (a *App) VisibleCategories() []*CommandCategory { ret := []*CommandCategory{} for _, category := range a.categories { if visible := func() *CommandCategory { for _, command := range category.Commands { if !command.Hidden { return category } } return nil }(); visible != nil { ret = append(ret, visible) } } return ret } // VisibleCommands returns a slice of the Commands with Hidden=false func (a *App) VisibleCommands() []Command { var ret []Command for _, command := range a.Commands { if !command.Hidden { ret = append(ret, command) } } return ret } // VisibleFlags returns a slice of the Flags with Hidden=false func (a *App) VisibleFlags() []Flag { return visibleFlags(a.Flags) } func (a *App) hasFlag(flag Flag) bool { for _, f := range a.Flags { if flag == f { return true } } return false } func (a *App) errWriter() io.Writer { // When the app ErrWriter is nil use the package level one. if a.ErrWriter == nil { return ErrWriter } return a.ErrWriter } func (a *App) appendFlag(flag Flag) { if !a.hasFlag(flag) { a.Flags = append(a.Flags, flag) } } func (a *App) handleExitCoder(context *Context, err error) { if a.ExitErrHandler != nil { a.ExitErrHandler(context, err) } else { HandleExitCoder(err) } } // Author represents someone who has contributed to a cli project. type Author struct { Name string // The Authors name Email string // The Authors email } // String makes Author comply to the Stringer interface, to allow an easy print in the templating process func (a Author) String() string { e := "" if a.Email != "" { e = " <" + a.Email + ">" } return fmt.Sprintf("%v%v", a.Name, e) } // HandleAction attempts to figure out which Action signature was used. If // it's an ActionFunc or a func with the legacy signature for Action, the func // is run! func HandleAction(action interface{}, context *Context) (err error) { switch a := action.(type) { case ActionFunc: return a(context) case func(*Context) error: return a(context) case func(*Context): // deprecated function signature a(context) return nil } return errInvalidActionType } cli-1.22.4/app_regression_test.go000066400000000000000000000031711364073550300167650ustar00rootroot00000000000000package cli import ( "testing" ) // TestRegression tests a regression that was merged between versions 1.20.0 and 1.21.0 // The included app.Run line worked in 1.20.0, and then was broken in 1.21.0. // Relevant PR: https://github.com/urfave/cli/pull/872 func TestVersionOneTwoOneRegression(t *testing.T) { testData := []struct { testCase string appRunInput []string skipArgReorder bool }{ { testCase: "with_dash_dash", appRunInput: []string{"cli", "command", "--flagone", "flagvalue", "--", "docker", "image", "ls", "--no-trunc"}, }, { testCase: "with_dash_dash_and_skip_reorder", appRunInput: []string{"cli", "command", "--flagone", "flagvalue", "--", "docker", "image", "ls", "--no-trunc"}, skipArgReorder: true, }, { testCase: "without_dash_dash", appRunInput: []string{"cli", "command", "--flagone", "flagvalue", "docker", "image", "ls", "--no-trunc"}, }, { testCase: "without_dash_dash_and_skip_reorder", appRunInput: []string{"cli", "command", "--flagone", "flagvalue", "docker", "image", "ls", "--no-trunc"}, skipArgReorder: true, }, } for _, test := range testData { t.Run(test.testCase, func(t *testing.T) { // setup app := NewApp() app.Commands = []Command{{ Name: "command", SkipArgReorder: test.skipArgReorder, Flags: []Flag{ StringFlag{ Name: "flagone", }, }, Action: func(c *Context) error { return nil }, }} // logic under test err := app.Run(test.appRunInput) // assertions if err != nil { t.Errorf("did not expected an error, but there was one: %s", err) } }) } } cli-1.22.4/app_test.go000066400000000000000000001450411364073550300145300ustar00rootroot00000000000000package cli import ( "bytes" "errors" "flag" "fmt" "io" "io/ioutil" "os" "reflect" "strings" "testing" ) var ( lastExitCode = 0 fakeOsExiter = func(rc int) { lastExitCode = rc } fakeErrWriter = &bytes.Buffer{} ) func init() { OsExiter = fakeOsExiter ErrWriter = fakeErrWriter } type opCounts struct { Total, BashComplete, OnUsageError, Before, CommandNotFound, Action, After, SubCommand int } func ExampleApp_Run() { // set args for examples sake os.Args = []string{"greet", "--name", "Jeremy"} app := NewApp() app.Name = "greet" app.Flags = []Flag{ StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, } app.Action = func(c *Context) error { fmt.Printf("Hello %v\n", c.String("name")) return nil } app.UsageText = "app [first_arg] [second_arg]" app.Author = "Harrison" app.Email = "harrison@lolwut.com" app.Authors = []Author{{Name: "Oliver Allen", Email: "oliver@toyshop.com"}} _ = app.Run(os.Args) // Output: // Hello Jeremy } func ExampleApp_Run_subcommand() { // set args for examples sake os.Args = []string{"say", "hi", "english", "--name", "Jeremy"} app := NewApp() app.Name = "say" app.Commands = []Command{ { Name: "hello", Aliases: []string{"hi"}, Usage: "use it to see a description", Description: "This is how we describe hello the function", Subcommands: []Command{ { Name: "english", Aliases: []string{"en"}, Usage: "sends a greeting in english", Description: "greets someone in english", Flags: []Flag{ StringFlag{ Name: "name", Value: "Bob", Usage: "Name of the person to greet", }, }, Action: func(c *Context) error { fmt.Println("Hello,", c.String("name")) return nil }, }, }, }, } _ = app.Run(os.Args) // Output: // Hello, Jeremy } func ExampleApp_Run_appHelp() { // set args for examples sake os.Args = []string{"greet", "help"} app := NewApp() app.Name = "greet" app.Version = "0.1.0" app.Description = "This is how we describe greet the app" app.Authors = []Author{ {Name: "Harrison", Email: "harrison@lolwut.com"}, {Name: "Oliver Allen", Email: "oliver@toyshop.com"}, } app.Flags = []Flag{ StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, } app.Commands = []Command{ { Name: "describeit", Aliases: []string{"d"}, Usage: "use it to see a description", Description: "This is how we describe describeit the function", Action: func(c *Context) error { fmt.Printf("i like to describe things") return nil }, }, } _ = app.Run(os.Args) // Output: // NAME: // greet - A new cli application // // USAGE: // greet [global options] command [command options] [arguments...] // // VERSION: // 0.1.0 // // DESCRIPTION: // This is how we describe greet the app // // AUTHORS: // Harrison // Oliver Allen // // COMMANDS: // describeit, d use it to see a description // help, h Shows a list of commands or help for one command // // GLOBAL OPTIONS: // --name value a name to say (default: "bob") // --help, -h show help // --version, -v print the version } func ExampleApp_Run_commandHelp() { // set args for examples sake os.Args = []string{"greet", "h", "describeit"} app := NewApp() app.Name = "greet" app.Flags = []Flag{ StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, } app.Commands = []Command{ { Name: "describeit", Aliases: []string{"d"}, Usage: "use it to see a description", Description: "This is how we describe describeit the function", Action: func(c *Context) error { fmt.Printf("i like to describe things") return nil }, }, } _ = app.Run(os.Args) // Output: // NAME: // greet describeit - use it to see a description // // USAGE: // greet describeit [arguments...] // // DESCRIPTION: // This is how we describe describeit the function } func ExampleApp_Run_noAction() { app := App{} app.Name = "greet" _ = app.Run([]string{"greet"}) // Output: // NAME: // greet // // USAGE: // [global options] command [command options] [arguments...] // // COMMANDS: // help, h Shows a list of commands or help for one command // // GLOBAL OPTIONS: // --help, -h show help } func ExampleApp_Run_subcommandNoAction() { app := App{} app.Name = "greet" app.Commands = []Command{ { Name: "describeit", Aliases: []string{"d"}, Usage: "use it to see a description", Description: "This is how we describe describeit the function", }, } _ = app.Run([]string{"greet", "describeit"}) // Output: // NAME: // describeit - use it to see a description // // USAGE: // describeit [arguments...] // // DESCRIPTION: // This is how we describe describeit the function } func ExampleApp_Run_bashComplete_withShortFlag() { os.Args = []string{"greet", "-", "--generate-bash-completion"} app := NewApp() app.Name = "greet" app.EnableBashCompletion = true app.Flags = []Flag{ IntFlag{ Name: "other,o", }, StringFlag{ Name: "xyz,x", }, } _ = app.Run(os.Args) // Output: // --other // -o // --xyz // -x // --help // -h } func ExampleApp_Run_bashComplete_withLongFlag() { os.Args = []string{"greet", "--s", "--generate-bash-completion"} app := NewApp() app.Name = "greet" app.EnableBashCompletion = true app.Flags = []Flag{ IntFlag{ Name: "other,o", }, StringFlag{ Name: "xyz,x", }, StringFlag{ Name: "some-flag,s", }, StringFlag{ Name: "similar-flag", }, } _ = app.Run(os.Args) // Output: // --some-flag // --similar-flag } func ExampleApp_Run_bashComplete_withMultipleLongFlag() { os.Args = []string{"greet", "--st", "--generate-bash-completion"} app := NewApp() app.Name = "greet" app.EnableBashCompletion = true app.Flags = []Flag{ IntFlag{ Name: "int-flag,i", }, StringFlag{ Name: "string,s", }, StringFlag{ Name: "string-flag-2", }, StringFlag{ Name: "similar-flag", }, StringFlag{ Name: "some-flag", }, } _ = app.Run(os.Args) // Output: // --string // --string-flag-2 } func ExampleApp_Run_bashComplete() { // set args for examples sake os.Args = []string{"greet", "--generate-bash-completion"} app := NewApp() app.Name = "greet" app.EnableBashCompletion = true app.Commands = []Command{ { Name: "describeit", Aliases: []string{"d"}, Usage: "use it to see a description", Description: "This is how we describe describeit the function", Action: func(c *Context) error { fmt.Printf("i like to describe things") return nil }, }, { Name: "next", Usage: "next example", Description: "more stuff to see when generating bash completion", Action: func(c *Context) error { fmt.Printf("the next example") return nil }, }, } _ = app.Run(os.Args) // Output: // describeit // d // next // help // h } func ExampleApp_Run_zshComplete() { // set args for examples sake os.Args = []string{"greet", "--generate-bash-completion"} _ = os.Setenv("_CLI_ZSH_AUTOCOMPLETE_HACK", "1") app := NewApp() app.Name = "greet" app.EnableBashCompletion = true app.Commands = []Command{ { Name: "describeit", Aliases: []string{"d"}, Usage: "use it to see a description", Description: "This is how we describe describeit the function", Action: func(c *Context) error { fmt.Printf("i like to describe things") return nil }, }, { Name: "next", Usage: "next example", Description: "more stuff to see when generating bash completion", Action: func(c *Context) error { fmt.Printf("the next example") return nil }, }, } _ = app.Run(os.Args) // Output: // describeit:use it to see a description // d:use it to see a description // next:next example // help:Shows a list of commands or help for one command // h:Shows a list of commands or help for one command } func TestApp_Run(t *testing.T) { s := "" app := NewApp() app.Action = func(c *Context) error { s = s + c.Args().First() return nil } err := app.Run([]string{"command", "foo"}) expect(t, err, nil) err = app.Run([]string{"command", "bar"}) expect(t, err, nil) expect(t, s, "foobar") } var commandAppTests = []struct { name string expected bool }{ {"foobar", true}, {"batbaz", true}, {"b", true}, {"f", true}, {"bat", false}, {"nothing", false}, } func TestApp_Command(t *testing.T) { app := NewApp() fooCommand := Command{Name: "foobar", Aliases: []string{"f"}} batCommand := Command{Name: "batbaz", Aliases: []string{"b"}} app.Commands = []Command{ fooCommand, batCommand, } for _, test := range commandAppTests { expect(t, app.Command(test.name) != nil, test.expected) } } func TestApp_Setup_defaultsWriter(t *testing.T) { app := &App{} app.Setup() expect(t, app.Writer, os.Stdout) } func TestApp_CommandWithArgBeforeFlags(t *testing.T) { var parsedOption, firstArg string app := NewApp() command := Command{ Name: "cmd", Flags: []Flag{ StringFlag{Name: "option", Value: "", Usage: "some option"}, }, Action: func(c *Context) error { parsedOption = c.String("option") firstArg = c.Args().First() return nil }, } app.Commands = []Command{command} _ = app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"}) expect(t, parsedOption, "my-option") expect(t, firstArg, "my-arg") } func TestApp_CommandWithArgBeforeBoolFlags(t *testing.T) { var parsedOption, parsedSecondOption, firstArg string var parsedBool, parsedSecondBool bool app := NewApp() command := Command{ Name: "cmd", Flags: []Flag{ StringFlag{Name: "option", Value: "", Usage: "some option"}, StringFlag{Name: "secondOption", Value: "", Usage: "another option"}, BoolFlag{Name: "boolflag", Usage: "some bool"}, BoolFlag{Name: "b", Usage: "another bool"}, }, Action: func(c *Context) error { parsedOption = c.String("option") parsedSecondOption = c.String("secondOption") parsedBool = c.Bool("boolflag") parsedSecondBool = c.Bool("b") firstArg = c.Args().First() return nil }, } app.Commands = []Command{command} _ = app.Run([]string{"", "cmd", "my-arg", "--boolflag", "--option", "my-option", "-b", "--secondOption", "fancy-option"}) expect(t, parsedOption, "my-option") expect(t, parsedSecondOption, "fancy-option") expect(t, parsedBool, true) expect(t, parsedSecondBool, true) expect(t, firstArg, "my-arg") } func TestApp_RunAsSubcommandParseFlags(t *testing.T) { var context *Context a := NewApp() a.Commands = []Command{ { Name: "foo", Action: func(c *Context) error { context = c return nil }, Flags: []Flag{ StringFlag{ Name: "lang", Value: "english", Usage: "language for the greeting", }, }, Before: func(_ *Context) error { return nil }, }, } _ = a.Run([]string{"", "foo", "--lang", "spanish", "abcd"}) expect(t, context.Args().Get(0), "abcd") expect(t, context.String("lang"), "spanish") } func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) { a := App{ Flags: []Flag{ StringFlag{Name: "--foo"}, }, Writer: bytes.NewBufferString(""), } set := flag.NewFlagSet("", flag.ContinueOnError) _ = set.Parse([]string{"", "---foo"}) c := &Context{flagSet: set} err := a.RunAsSubcommand(c) expect(t, err, errors.New("bad flag syntax: ---foo")) } func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) { var parsedOption string var args []string app := NewApp() command := Command{ Name: "cmd", Flags: []Flag{ StringFlag{Name: "option", Value: "", Usage: "some option"}, }, Action: func(c *Context) error { parsedOption = c.String("option") args = c.Args() return nil }, } app.Commands = []Command{command} _ = app.Run([]string{"", "cmd", "my-arg", "--option", "my-option", "--", "--notARealFlag"}) expect(t, parsedOption, "my-option") expect(t, args[0], "my-arg") expect(t, args[1], "--") expect(t, args[2], "--notARealFlag") } func TestApp_CommandWithDash(t *testing.T) { var args []string app := NewApp() command := Command{ Name: "cmd", Action: func(c *Context) error { args = c.Args() return nil }, } app.Commands = []Command{command} _ = app.Run([]string{"", "cmd", "my-arg", "-"}) expect(t, args[0], "my-arg") expect(t, args[1], "-") } func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) { var args []string app := NewApp() command := Command{ Name: "cmd", Action: func(c *Context) error { args = c.Args() return nil }, } app.Commands = []Command{command} _ = app.Run([]string{"", "cmd", "my-arg", "--", "notAFlagAtAll"}) expect(t, args[0], "my-arg") expect(t, args[1], "--") expect(t, args[2], "notAFlagAtAll") } func TestApp_VisibleCommands(t *testing.T) { app := NewApp() app.Commands = []Command{ { Name: "frob", HelpName: "foo frob", Action: func(_ *Context) error { return nil }, }, { Name: "frib", HelpName: "foo frib", Hidden: true, Action: func(_ *Context) error { return nil }, }, } app.Setup() expected := []Command{ app.Commands[0], app.Commands[2], // help } actual := app.VisibleCommands() expect(t, len(expected), len(actual)) for i, actualCommand := range actual { expectedCommand := expected[i] if expectedCommand.Action != nil { // comparing func addresses is OK! expect(t, fmt.Sprintf("%p", expectedCommand.Action), fmt.Sprintf("%p", actualCommand.Action)) } // nil out funcs, as they cannot be compared // (https://github.com/golang/go/issues/8554) expectedCommand.Action = nil actualCommand.Action = nil if !reflect.DeepEqual(expectedCommand, actualCommand) { t.Errorf("expected\n%#v\n!=\n%#v", expectedCommand, actualCommand) } } } func TestApp_UseShortOptionHandling(t *testing.T) { var one, two bool var name string expected := "expectedName" app := NewApp() app.UseShortOptionHandling = true app.Flags = []Flag{ BoolFlag{Name: "one, o"}, BoolFlag{Name: "two, t"}, StringFlag{Name: "name, n"}, } app.Action = func(c *Context) error { one = c.Bool("one") two = c.Bool("two") name = c.String("name") return nil } app.Run([]string{"", "-on", expected}) expect(t, one, true) expect(t, two, false) expect(t, name, expected) } func TestApp_UseShortOptionHandling_missing_value(t *testing.T) { app := NewApp() app.UseShortOptionHandling = true app.Flags = []Flag{ StringFlag{Name: "name, n"}, } err := app.Run([]string{"", "-n"}) expect(t, err, errors.New("flag needs an argument: -n")) } func TestApp_UseShortOptionHandlingCommand(t *testing.T) { var one, two bool var name string expected := "expectedName" app := NewApp() app.UseShortOptionHandling = true command := Command{ Name: "cmd", Flags: []Flag{ BoolFlag{Name: "one, o"}, BoolFlag{Name: "two, t"}, StringFlag{Name: "name, n"}, }, Action: func(c *Context) error { one = c.Bool("one") two = c.Bool("two") name = c.String("name") return nil }, } app.Commands = []Command{command} app.Run([]string{"", "cmd", "-on", expected}) expect(t, one, true) expect(t, two, false) expect(t, name, expected) } func TestApp_UseShortOptionHandlingCommand_missing_value(t *testing.T) { app := NewApp() app.UseShortOptionHandling = true command := Command{ Name: "cmd", Flags: []Flag{ StringFlag{Name: "name, n"}, }, } app.Commands = []Command{command} err := app.Run([]string{"", "cmd", "-n"}) expect(t, err, errors.New("flag needs an argument: -n")) } func TestApp_UseShortOptionHandlingSubCommand(t *testing.T) { var one, two bool var name string expected := "expectedName" app := NewApp() app.UseShortOptionHandling = true command := Command{ Name: "cmd", } subCommand := Command{ Name: "sub", Flags: []Flag{ BoolFlag{Name: "one, o"}, BoolFlag{Name: "two, t"}, StringFlag{Name: "name, n"}, }, Action: func(c *Context) error { one = c.Bool("one") two = c.Bool("two") name = c.String("name") return nil }, } command.Subcommands = []Command{subCommand} app.Commands = []Command{command} err := app.Run([]string{"", "cmd", "sub", "-on", expected}) expect(t, err, nil) expect(t, one, true) expect(t, two, false) expect(t, name, expected) } func TestApp_UseShortOptionHandlingSubCommand_missing_value(t *testing.T) { app := NewApp() app.UseShortOptionHandling = true command := Command{ Name: "cmd", } subCommand := Command{ Name: "sub", Flags: []Flag{ StringFlag{Name: "name, n"}, }, } command.Subcommands = []Command{subCommand} app.Commands = []Command{command} err := app.Run([]string{"", "cmd", "sub", "-n"}) expect(t, err, errors.New("flag needs an argument: -n")) } func TestApp_Float64Flag(t *testing.T) { var meters float64 app := NewApp() app.Flags = []Flag{ Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"}, } app.Action = func(c *Context) error { meters = c.Float64("height") return nil } _ = app.Run([]string{"", "--height", "1.93"}) expect(t, meters, 1.93) } func TestApp_ParseSliceFlags(t *testing.T) { var parsedIntSlice []int var parsedStringSlice []string app := NewApp() command := Command{ Name: "cmd", Flags: []Flag{ IntSliceFlag{Name: "p", Value: &IntSlice{}, Usage: "set one or more ip addr"}, StringSliceFlag{Name: "ip", Value: &StringSlice{}, Usage: "set one or more ports to open"}, }, Action: func(c *Context) error { parsedIntSlice = c.IntSlice("p") parsedStringSlice = c.StringSlice("ip") return nil }, } app.Commands = []Command{command} _ = app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"}) IntsEquals := func(a, b []int) bool { if len(a) != len(b) { return false } for i, v := range a { if v != b[i] { return false } } return true } StrsEquals := func(a, b []string) bool { if len(a) != len(b) { return false } for i, v := range a { if v != b[i] { return false } } return true } var expectedIntSlice = []int{22, 80} var expectedStringSlice = []string{"8.8.8.8", "8.8.4.4"} if !IntsEquals(parsedIntSlice, expectedIntSlice) { t.Errorf("%v does not match %v", parsedIntSlice, expectedIntSlice) } if !StrsEquals(parsedStringSlice, expectedStringSlice) { t.Errorf("%v does not match %v", parsedStringSlice, expectedStringSlice) } } func TestApp_ParseSliceFlagsWithMissingValue(t *testing.T) { var parsedIntSlice []int var parsedStringSlice []string app := NewApp() command := Command{ Name: "cmd", Flags: []Flag{ IntSliceFlag{Name: "a", Usage: "set numbers"}, StringSliceFlag{Name: "str", Usage: "set strings"}, }, Action: func(c *Context) error { parsedIntSlice = c.IntSlice("a") parsedStringSlice = c.StringSlice("str") return nil }, } app.Commands = []Command{command} _ = app.Run([]string{"", "cmd", "my-arg", "-a", "2", "-str", "A"}) var expectedIntSlice = []int{2} var expectedStringSlice = []string{"A"} if parsedIntSlice[0] != expectedIntSlice[0] { t.Errorf("%v does not match %v", parsedIntSlice[0], expectedIntSlice[0]) } if parsedStringSlice[0] != expectedStringSlice[0] { t.Errorf("%v does not match %v", parsedIntSlice[0], expectedIntSlice[0]) } } func TestApp_DefaultStdout(t *testing.T) { app := NewApp() if app.Writer != os.Stdout { t.Error("Default output writer not set.") } } type mockWriter struct { written []byte } func (fw *mockWriter) Write(p []byte) (n int, err error) { if fw.written == nil { fw.written = p } else { fw.written = append(fw.written, p...) } return len(p), nil } func (fw *mockWriter) GetWritten() (b []byte) { return fw.written } func TestApp_SetStdout(t *testing.T) { w := &mockWriter{} app := NewApp() app.Name = "test" app.Writer = w err := app.Run([]string{"help"}) if err != nil { t.Fatalf("Run error: %s", err) } if len(w.written) == 0 { t.Error("App did not write output to desired writer.") } } func TestApp_BeforeFunc(t *testing.T) { counts := &opCounts{} beforeError := fmt.Errorf("fail") var err error app := NewApp() app.Before = func(c *Context) error { counts.Total++ counts.Before = counts.Total s := c.String("opt") if s == "fail" { return beforeError } return nil } app.Commands = []Command{ { Name: "sub", Action: func(c *Context) error { counts.Total++ counts.SubCommand = counts.Total return nil }, }, } app.Flags = []Flag{ StringFlag{Name: "opt"}, } // run with the Before() func succeeding err = app.Run([]string{"command", "--opt", "succeed", "sub"}) if err != nil { t.Fatalf("Run error: %s", err) } if counts.Before != 1 { t.Errorf("Before() not executed when expected") } if counts.SubCommand != 2 { t.Errorf("Subcommand not executed when expected") } // reset counts = &opCounts{} // run with the Before() func failing err = app.Run([]string{"command", "--opt", "fail", "sub"}) // should be the same error produced by the Before func if err != beforeError { t.Errorf("Run error expected, but not received") } if counts.Before != 1 { t.Errorf("Before() not executed when expected") } if counts.SubCommand != 0 { t.Errorf("Subcommand executed when NOT expected") } // reset counts = &opCounts{} afterError := errors.New("fail again") app.After = func(_ *Context) error { return afterError } // run with the Before() func failing, wrapped by After() err = app.Run([]string{"command", "--opt", "fail", "sub"}) // should be the same error produced by the Before func if _, ok := err.(MultiError); !ok { t.Errorf("MultiError expected, but not received") } if counts.Before != 1 { t.Errorf("Before() not executed when expected") } if counts.SubCommand != 0 { t.Errorf("Subcommand executed when NOT expected") } } func TestApp_AfterFunc(t *testing.T) { counts := &opCounts{} afterError := fmt.Errorf("fail") var err error app := NewApp() app.After = func(c *Context) error { counts.Total++ counts.After = counts.Total s := c.String("opt") if s == "fail" { return afterError } return nil } app.Commands = []Command{ { Name: "sub", Action: func(c *Context) error { counts.Total++ counts.SubCommand = counts.Total return nil }, }, } app.Flags = []Flag{ StringFlag{Name: "opt"}, } // run with the After() func succeeding err = app.Run([]string{"command", "--opt", "succeed", "sub"}) if err != nil { t.Fatalf("Run error: %s", err) } if counts.After != 2 { t.Errorf("After() not executed when expected") } if counts.SubCommand != 1 { t.Errorf("Subcommand not executed when expected") } // reset counts = &opCounts{} // run with the Before() func failing err = app.Run([]string{"command", "--opt", "fail", "sub"}) // should be the same error produced by the Before func if err != afterError { t.Errorf("Run error expected, but not received") } if counts.After != 2 { t.Errorf("After() not executed when expected") } if counts.SubCommand != 1 { t.Errorf("Subcommand not executed when expected") } } func TestAppNoHelpFlag(t *testing.T) { oldFlag := HelpFlag defer func() { HelpFlag = oldFlag }() HelpFlag = BoolFlag{} app := NewApp() app.Writer = ioutil.Discard err := app.Run([]string{"test", "-h"}) if err != flag.ErrHelp { t.Errorf("expected error about missing help flag, but got: %s (%T)", err, err) } } func TestRequiredFlagAppRunBehavior(t *testing.T) { tdata := []struct { testCase string appFlags []Flag appRunInput []string appCommands []Command expectedAnError bool }{ // assertion: empty input, when a required flag is present, errors { testCase: "error_case_empty_input_with_required_flag_on_app", appRunInput: []string{"myCLI"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, expectedAnError: true, }, { testCase: "error_case_empty_input_with_required_flag_on_command", appRunInput: []string{"myCLI", "myCommand"}, appCommands: []Command{{ Name: "myCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, expectedAnError: true, }, { testCase: "error_case_empty_input_with_required_flag_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand"}, appCommands: []Command{{ Name: "myCommand", Subcommands: []Command{{ Name: "mySubCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, }}, expectedAnError: true, }, // assertion: inputing --help, when a required flag is present, does not error { testCase: "valid_case_help_input_with_required_flag_on_app", appRunInput: []string{"myCLI", "--help"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }, { testCase: "valid_case_help_input_with_required_flag_on_command", appRunInput: []string{"myCLI", "myCommand", "--help"}, appCommands: []Command{{ Name: "myCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, }, { testCase: "valid_case_help_input_with_required_flag_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--help"}, appCommands: []Command{{ Name: "myCommand", Subcommands: []Command{{ Name: "mySubCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, }}, }, // assertion: giving optional input, when a required flag is present, errors { testCase: "error_case_optional_input_with_required_flag_on_app", appRunInput: []string{"myCLI", "--optional", "cats"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, expectedAnError: true, }, { testCase: "error_case_optional_input_with_required_flag_on_command", appRunInput: []string{"myCLI", "myCommand", "--optional", "cats"}, appCommands: []Command{{ Name: "myCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, }}, expectedAnError: true, }, { testCase: "error_case_optional_input_with_required_flag_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--optional", "cats"}, appCommands: []Command{{ Name: "myCommand", Subcommands: []Command{{ Name: "mySubCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optional"}}, }}, }}, expectedAnError: true, }, // assertion: when a required flag is present, inputting that required flag does not error { testCase: "valid_case_required_flag_input_on_app", appRunInput: []string{"myCLI", "--requiredFlag", "cats"}, appFlags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }, { testCase: "valid_case_required_flag_input_on_command", appRunInput: []string{"myCLI", "myCommand", "--requiredFlag", "cats"}, appCommands: []Command{{ Name: "myCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, }, { testCase: "valid_case_required_flag_input_on_subcommand", appRunInput: []string{"myCLI", "myCommand", "mySubCommand", "--requiredFlag", "cats"}, appCommands: []Command{{ Name: "myCommand", Subcommands: []Command{{ Name: "mySubCommand", Flags: []Flag{StringFlag{Name: "requiredFlag", Required: true}}, }}, }}, }, } for _, test := range tdata { t.Run(test.testCase, func(t *testing.T) { // setup app := NewApp() app.Flags = test.appFlags app.Commands = test.appCommands // logic under test err := app.Run(test.appRunInput) // assertions if test.expectedAnError && err == nil { t.Errorf("expected an error, but there was none") } if _, ok := err.(requiredFlagsErr); test.expectedAnError && !ok { t.Errorf("expected a requiredFlagsErr, but got: %s", err) } if !test.expectedAnError && err != nil { t.Errorf("did not expected an error, but there was one: %s", err) } }) } } func TestAppHelpPrinter(t *testing.T) { oldPrinter := HelpPrinter defer func() { HelpPrinter = oldPrinter }() var wasCalled = false HelpPrinter = func(w io.Writer, template string, data interface{}) { wasCalled = true } app := NewApp() _ = app.Run([]string{"-h"}) if wasCalled == false { t.Errorf("Help printer expected to be called, but was not") } } func TestApp_VersionPrinter(t *testing.T) { oldPrinter := VersionPrinter defer func() { VersionPrinter = oldPrinter }() var wasCalled = false VersionPrinter = func(c *Context) { wasCalled = true } app := NewApp() ctx := NewContext(app, nil, nil) ShowVersion(ctx) if wasCalled == false { t.Errorf("Version printer expected to be called, but was not") } } func TestApp_CommandNotFound(t *testing.T) { counts := &opCounts{} app := NewApp() app.CommandNotFound = func(c *Context, command string) { counts.Total++ counts.CommandNotFound = counts.Total } app.Commands = []Command{ { Name: "bar", Action: func(c *Context) error { counts.Total++ counts.SubCommand = counts.Total return nil }, }, } _ = app.Run([]string{"command", "foo"}) expect(t, counts.CommandNotFound, 1) expect(t, counts.SubCommand, 0) expect(t, counts.Total, 1) } func TestApp_OrderOfOperations(t *testing.T) { counts := &opCounts{} resetCounts := func() { counts = &opCounts{} } app := NewApp() app.EnableBashCompletion = true app.BashComplete = func(c *Context) { counts.Total++ counts.BashComplete = counts.Total } app.OnUsageError = func(c *Context, err error, isSubcommand bool) error { counts.Total++ counts.OnUsageError = counts.Total return errors.New("hay OnUsageError") } beforeNoError := func(c *Context) error { counts.Total++ counts.Before = counts.Total return nil } beforeError := func(c *Context) error { counts.Total++ counts.Before = counts.Total return errors.New("hay Before") } app.Before = beforeNoError app.CommandNotFound = func(c *Context, command string) { counts.Total++ counts.CommandNotFound = counts.Total } afterNoError := func(c *Context) error { counts.Total++ counts.After = counts.Total return nil } afterError := func(c *Context) error { counts.Total++ counts.After = counts.Total return errors.New("hay After") } app.After = afterNoError app.Commands = []Command{ { Name: "bar", Action: func(c *Context) error { counts.Total++ counts.SubCommand = counts.Total return nil }, }, } app.Action = func(c *Context) error { counts.Total++ counts.Action = counts.Total return nil } _ = app.Run([]string{"command", "--nope"}) expect(t, counts.OnUsageError, 1) expect(t, counts.Total, 1) resetCounts() _ = app.Run([]string{"command", "--generate-bash-completion"}) expect(t, counts.BashComplete, 1) expect(t, counts.Total, 1) resetCounts() oldOnUsageError := app.OnUsageError app.OnUsageError = nil _ = app.Run([]string{"command", "--nope"}) expect(t, counts.Total, 0) app.OnUsageError = oldOnUsageError resetCounts() _ = app.Run([]string{"command", "foo"}) expect(t, counts.OnUsageError, 0) expect(t, counts.Before, 1) expect(t, counts.CommandNotFound, 0) expect(t, counts.Action, 2) expect(t, counts.After, 3) expect(t, counts.Total, 3) resetCounts() app.Before = beforeError _ = app.Run([]string{"command", "bar"}) expect(t, counts.OnUsageError, 0) expect(t, counts.Before, 1) expect(t, counts.After, 2) expect(t, counts.Total, 2) app.Before = beforeNoError resetCounts() app.After = nil _ = app.Run([]string{"command", "bar"}) expect(t, counts.OnUsageError, 0) expect(t, counts.Before, 1) expect(t, counts.SubCommand, 2) expect(t, counts.Total, 2) app.After = afterNoError resetCounts() app.After = afterError err := app.Run([]string{"command", "bar"}) if err == nil { t.Fatalf("expected a non-nil error") } expect(t, counts.OnUsageError, 0) expect(t, counts.Before, 1) expect(t, counts.SubCommand, 2) expect(t, counts.After, 3) expect(t, counts.Total, 3) app.After = afterNoError resetCounts() oldCommands := app.Commands app.Commands = nil _ = app.Run([]string{"command"}) expect(t, counts.OnUsageError, 0) expect(t, counts.Before, 1) expect(t, counts.Action, 2) expect(t, counts.After, 3) expect(t, counts.Total, 3) app.Commands = oldCommands } func TestApp_Run_CommandWithSubcommandHasHelpTopic(t *testing.T) { var subcommandHelpTopics = [][]string{ {"command", "foo", "--help"}, {"command", "foo", "-h"}, {"command", "foo", "help"}, } for _, flagSet := range subcommandHelpTopics { t.Logf("==> checking with flags %v", flagSet) app := NewApp() buf := new(bytes.Buffer) app.Writer = buf subCmdBar := Command{ Name: "bar", Usage: "does bar things", } subCmdBaz := Command{ Name: "baz", Usage: "does baz things", } cmd := Command{ Name: "foo", Description: "descriptive wall of text about how it does foo things", Subcommands: []Command{subCmdBar, subCmdBaz}, Action: func(c *Context) error { return nil }, } app.Commands = []Command{cmd} err := app.Run(flagSet) if err != nil { t.Error(err) } output := buf.String() t.Logf("output: %q\n", buf.Bytes()) if strings.Contains(output, "No help topic for") { t.Errorf("expect a help topic, got none: \n%q", output) } for _, shouldContain := range []string{ cmd.Name, cmd.Description, subCmdBar.Name, subCmdBar.Usage, subCmdBaz.Name, subCmdBaz.Usage, } { if !strings.Contains(output, shouldContain) { t.Errorf("want help to contain %q, did not: \n%q", shouldContain, output) } } } } func TestApp_Run_SubcommandFullPath(t *testing.T) { app := NewApp() buf := new(bytes.Buffer) app.Writer = buf app.Name = "command" subCmd := Command{ Name: "bar", Usage: "does bar things", } cmd := Command{ Name: "foo", Description: "foo commands", Subcommands: []Command{subCmd}, } app.Commands = []Command{cmd} err := app.Run([]string{"command", "foo", "bar", "--help"}) if err != nil { t.Error(err) } output := buf.String() if !strings.Contains(output, "command foo bar - does bar things") { t.Errorf("expected full path to subcommand: %s", output) } if !strings.Contains(output, "command foo bar [arguments...]") { t.Errorf("expected full path to subcommand: %s", output) } } func TestApp_Run_SubcommandHelpName(t *testing.T) { app := NewApp() buf := new(bytes.Buffer) app.Writer = buf app.Name = "command" subCmd := Command{ Name: "bar", HelpName: "custom", Usage: "does bar things", } cmd := Command{ Name: "foo", Description: "foo commands", Subcommands: []Command{subCmd}, } app.Commands = []Command{cmd} err := app.Run([]string{"command", "foo", "bar", "--help"}) if err != nil { t.Error(err) } output := buf.String() if !strings.Contains(output, "custom - does bar things") { t.Errorf("expected HelpName for subcommand: %s", output) } if !strings.Contains(output, "custom [arguments...]") { t.Errorf("expected HelpName to subcommand: %s", output) } } func TestApp_Run_CommandHelpName(t *testing.T) { app := NewApp() buf := new(bytes.Buffer) app.Writer = buf app.Name = "command" subCmd := Command{ Name: "bar", Usage: "does bar things", } cmd := Command{ Name: "foo", HelpName: "custom", Description: "foo commands", Subcommands: []Command{subCmd}, } app.Commands = []Command{cmd} err := app.Run([]string{"command", "foo", "bar", "--help"}) if err != nil { t.Error(err) } output := buf.String() if !strings.Contains(output, "command foo bar - does bar things") { t.Errorf("expected full path to subcommand: %s", output) } if !strings.Contains(output, "command foo bar [arguments...]") { t.Errorf("expected full path to subcommand: %s", output) } } func TestApp_Run_CommandSubcommandHelpName(t *testing.T) { app := NewApp() buf := new(bytes.Buffer) app.Writer = buf app.Name = "base" subCmd := Command{ Name: "bar", HelpName: "custom", Usage: "does bar things", } cmd := Command{ Name: "foo", Description: "foo commands", Subcommands: []Command{subCmd}, } app.Commands = []Command{cmd} err := app.Run([]string{"command", "foo", "--help"}) if err != nil { t.Error(err) } output := buf.String() if !strings.Contains(output, "base foo - foo commands") { t.Errorf("expected full path to subcommand: %s", output) } if !strings.Contains(output, "base foo command [command options] [arguments...]") { t.Errorf("expected full path to subcommand: %s", output) } } func TestApp_Run_Help(t *testing.T) { var helpArguments = [][]string{{"boom", "--help"}, {"boom", "-h"}, {"boom", "help"}} for _, args := range helpArguments { buf := new(bytes.Buffer) t.Logf("==> checking with arguments %v", args) app := NewApp() app.Name = "boom" app.Usage = "make an explosive entrance" app.Writer = buf app.Action = func(c *Context) error { buf.WriteString("boom I say!") return nil } err := app.Run(args) if err != nil { t.Error(err) } output := buf.String() t.Logf("output: %q\n", buf.Bytes()) if !strings.Contains(output, "boom - make an explosive entrance") { t.Errorf("want help to contain %q, did not: \n%q", "boom - make an explosive entrance", output) } } } func TestApp_Run_Version(t *testing.T) { var versionArguments = [][]string{{"boom", "--version"}, {"boom", "-v"}} for _, args := range versionArguments { buf := new(bytes.Buffer) t.Logf("==> checking with arguments %v", args) app := NewApp() app.Name = "boom" app.Usage = "make an explosive entrance" app.Version = "0.1.0" app.Writer = buf app.Action = func(c *Context) error { buf.WriteString("boom I say!") return nil } err := app.Run(args) if err != nil { t.Error(err) } output := buf.String() t.Logf("output: %q\n", buf.Bytes()) if !strings.Contains(output, "0.1.0") { t.Errorf("want version to contain %q, did not: \n%q", "0.1.0", output) } } } func TestApp_Run_Categories(t *testing.T) { app := NewApp() app.Name = "categories" app.HideHelp = true app.Commands = []Command{ { Name: "command1", Category: "1", }, { Name: "command2", Category: "1", }, { Name: "command3", Category: "2", }, } buf := new(bytes.Buffer) app.Writer = buf _ = app.Run([]string{"categories"}) expect := CommandCategories{ &CommandCategory{ Name: "1", Commands: []Command{ app.Commands[0], app.Commands[1], }, }, &CommandCategory{ Name: "2", Commands: []Command{ app.Commands[2], }, }, } if !reflect.DeepEqual(app.Categories(), expect) { t.Fatalf("expected categories %#v, to equal %#v", app.Categories(), expect) } output := buf.String() t.Logf("output: %q\n", buf.Bytes()) if !strings.Contains(output, "1:\n command1") { t.Errorf("want buffer to include category %q, did not: \n%q", "1:\n command1", output) } } func TestApp_VisibleCategories(t *testing.T) { app := NewApp() app.Name = "visible-categories" app.HideHelp = true app.Commands = []Command{ { Name: "command1", Category: "1", HelpName: "foo command1", Hidden: true, }, { Name: "command2", Category: "2", HelpName: "foo command2", }, { Name: "command3", Category: "3", HelpName: "foo command3", }, } expected := []*CommandCategory{ { Name: "2", Commands: []Command{ app.Commands[1], }, }, { Name: "3", Commands: []Command{ app.Commands[2], }, }, } app.Setup() expect(t, expected, app.VisibleCategories()) app = NewApp() app.Name = "visible-categories" app.HideHelp = true app.Commands = []Command{ { Name: "command1", Category: "1", HelpName: "foo command1", Hidden: true, }, { Name: "command2", Category: "2", HelpName: "foo command2", Hidden: true, }, { Name: "command3", Category: "3", HelpName: "foo command3", }, } expected = []*CommandCategory{ { Name: "3", Commands: []Command{ app.Commands[2], }, }, } app.Setup() expect(t, expected, app.VisibleCategories()) app = NewApp() app.Name = "visible-categories" app.HideHelp = true app.Commands = []Command{ { Name: "command1", Category: "1", HelpName: "foo command1", Hidden: true, }, { Name: "command2", Category: "2", HelpName: "foo command2", Hidden: true, }, { Name: "command3", Category: "3", HelpName: "foo command3", Hidden: true, }, } expected = []*CommandCategory{} app.Setup() expect(t, expected, app.VisibleCategories()) } func TestApp_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { app := NewApp() app.Action = func(c *Context) error { return nil } app.Before = func(c *Context) error { return fmt.Errorf("before error") } app.After = func(c *Context) error { return fmt.Errorf("after error") } err := app.Run([]string{"foo"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.Contains(err.Error(), "before error") { t.Errorf("expected text of error from Before method, but got none in \"%v\"", err) } if !strings.Contains(err.Error(), "after error") { t.Errorf("expected text of error from After method, but got none in \"%v\"", err) } } func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) { app := NewApp() app.Commands = []Command{ { Subcommands: []Command{ { Name: "sub", }, }, Name: "bar", Before: func(c *Context) error { return fmt.Errorf("before error") }, After: func(c *Context) error { return fmt.Errorf("after error") }, }, } err := app.Run([]string{"foo", "bar"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.Contains(err.Error(), "before error") { t.Errorf("expected text of error from Before method, but got none in \"%v\"", err) } if !strings.Contains(err.Error(), "after error") { t.Errorf("expected text of error from After method, but got none in \"%v\"", err) } } func TestApp_OnUsageError_WithWrongFlagValue(t *testing.T) { app := NewApp() app.Flags = []Flag{ IntFlag{Name: "flag"}, } app.OnUsageError = func(c *Context, err error, isSubcommand bool) error { if isSubcommand { t.Errorf("Expect no subcommand") } if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") { t.Errorf("Expect an invalid value error, but got \"%v\"", err) } return errors.New("intercepted: " + err.Error()) } app.Commands = []Command{ { Name: "bar", }, } err := app.Run([]string{"foo", "--flag=wrong"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.HasPrefix(err.Error(), "intercepted: invalid value") { t.Errorf("Expect an intercepted error, but got \"%v\"", err) } } func TestApp_OnUsageError_WithWrongFlagValue_ForSubcommand(t *testing.T) { app := NewApp() app.Flags = []Flag{ IntFlag{Name: "flag"}, } app.OnUsageError = func(c *Context, err error, isSubcommand bool) error { if isSubcommand { t.Errorf("Expect subcommand") } if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") { t.Errorf("Expect an invalid value error, but got \"%v\"", err) } return errors.New("intercepted: " + err.Error()) } app.Commands = []Command{ { Name: "bar", }, } err := app.Run([]string{"foo", "--flag=wrong", "bar"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.HasPrefix(err.Error(), "intercepted: invalid value") { t.Errorf("Expect an intercepted error, but got \"%v\"", err) } } // A custom flag that conforms to the relevant interfaces, but has none of the // fields that the other flag types do. type customBoolFlag struct { Nombre string } // Don't use the normal FlagStringer func (c *customBoolFlag) String() string { return "***" + c.Nombre + "***" } func (c *customBoolFlag) GetName() string { return c.Nombre } func (c *customBoolFlag) TakesValue() bool { return false } func (c *customBoolFlag) GetValue() string { return "value" } func (c *customBoolFlag) GetUsage() string { return "usage" } func (c *customBoolFlag) Apply(set *flag.FlagSet) { set.String(c.Nombre, c.Nombre, "") } func TestCustomFlagsUnused(t *testing.T) { app := NewApp() app.Flags = []Flag{&customBoolFlag{"custom"}} err := app.Run([]string{"foo"}) if err != nil { t.Errorf("Run returned unexpected error: %v", err) } } func TestCustomFlagsUsed(t *testing.T) { app := NewApp() app.Flags = []Flag{&customBoolFlag{"custom"}} err := app.Run([]string{"foo", "--custom=bar"}) if err != nil { t.Errorf("Run returned unexpected error: %v", err) } } func TestCustomHelpVersionFlags(t *testing.T) { app := NewApp() // Be sure to reset the global flags defer func(helpFlag Flag, versionFlag Flag) { HelpFlag = helpFlag VersionFlag = versionFlag }(HelpFlag, VersionFlag) HelpFlag = &customBoolFlag{"help-custom"} VersionFlag = &customBoolFlag{"version-custom"} err := app.Run([]string{"foo", "--help-custom=bar"}) if err != nil { t.Errorf("Run returned unexpected error: %v", err) } } func TestHandleAction_WithNonFuncAction(t *testing.T) { app := NewApp() app.Action = 42 fs, err := flagSet(app.Name, app.Flags) if err != nil { t.Errorf("error creating FlagSet: %s", err) } err = HandleAction(app.Action, NewContext(app, fs, nil)) if err == nil { t.Fatalf("expected to receive error from Run, got none") } exitErr, ok := err.(*ExitError) if !ok { t.Fatalf("expected to receive a *ExitError") } if !strings.HasPrefix(exitErr.Error(), "ERROR invalid Action type.") { t.Fatalf("expected an unknown Action error, but got: %v", exitErr.Error()) } if exitErr.ExitCode() != 2 { t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode()) } } func TestHandleAction_WithInvalidFuncSignature(t *testing.T) { app := NewApp() app.Action = func() string { return "" } fs, err := flagSet(app.Name, app.Flags) if err != nil { t.Errorf("error creating FlagSet: %s", err) } err = HandleAction(app.Action, NewContext(app, fs, nil)) if err == nil { t.Fatalf("expected to receive error from Run, got none") } exitErr, ok := err.(*ExitError) if !ok { t.Fatalf("expected to receive a *ExitError") } if !strings.HasPrefix(exitErr.Error(), "ERROR invalid Action type") { t.Fatalf("expected an unknown Action error, but got: %v", exitErr.Error()) } if exitErr.ExitCode() != 2 { t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode()) } } func TestHandleAction_WithInvalidFuncReturnSignature(t *testing.T) { app := NewApp() app.Action = func(_ *Context) (int, error) { return 0, nil } fs, err := flagSet(app.Name, app.Flags) if err != nil { t.Errorf("error creating FlagSet: %s", err) } err = HandleAction(app.Action, NewContext(app, fs, nil)) if err == nil { t.Fatalf("expected to receive error from Run, got none") } exitErr, ok := err.(*ExitError) if !ok { t.Fatalf("expected to receive a *ExitError") } if !strings.HasPrefix(exitErr.Error(), "ERROR invalid Action type") { t.Fatalf("expected an invalid Action signature error, but got: %v", exitErr.Error()) } if exitErr.ExitCode() != 2 { t.Fatalf("expected error exit code to be 2, but got: %v", exitErr.ExitCode()) } } func TestHandleExitCoder_Default(t *testing.T) { app := NewApp() fs, err := flagSet(app.Name, app.Flags) if err != nil { t.Errorf("error creating FlagSet: %s", err) } ctx := NewContext(app, fs, nil) app.handleExitCoder(ctx, NewExitError("Default Behavior Error", 42)) output := fakeErrWriter.String() if !strings.Contains(output, "Default") { t.Fatalf("Expected Default Behavior from Error Handler but got: %s", output) } } func TestHandleExitCoder_Custom(t *testing.T) { app := NewApp() fs, err := flagSet(app.Name, app.Flags) if err != nil { t.Errorf("error creating FlagSet: %s", err) } app.ExitErrHandler = func(_ *Context, _ error) { _, _ = fmt.Fprintln(ErrWriter, "I'm a Custom error handler, I print what I want!") } ctx := NewContext(app, fs, nil) app.handleExitCoder(ctx, NewExitError("Default Behavior Error", 42)) output := fakeErrWriter.String() if !strings.Contains(output, "Custom") { t.Fatalf("Expected Custom Behavior from Error Handler but got: %s", output) } } func TestHandleAction_WithUnknownPanic(t *testing.T) { defer func() { refute(t, recover(), nil) }() var fn ActionFunc app := NewApp() app.Action = func(ctx *Context) error { _ = fn(ctx) return nil } fs, err := flagSet(app.Name, app.Flags) if err != nil { t.Errorf("error creating FlagSet: %s", err) } _ = HandleAction(app.Action, NewContext(app, fs, nil)) } func TestShellCompletionForIncompleteFlags(t *testing.T) { app := NewApp() app.Flags = []Flag{ IntFlag{ Name: "test-completion", }, } app.EnableBashCompletion = true app.BashComplete = func(ctx *Context) { for _, command := range ctx.App.Commands { if command.Hidden { continue } for _, name := range command.Names() { _, _ = fmt.Fprintln(ctx.App.Writer, name) } } for _, f := range ctx.App.Flags { for _, name := range strings.Split(f.GetName(), ",") { if name == BashCompletionFlag.GetName() { continue } switch name = strings.TrimSpace(name); len(name) { case 0: case 1: _, _ = fmt.Fprintln(ctx.App.Writer, "-"+name) default: _, _ = fmt.Fprintln(ctx.App.Writer, "--"+name) } } } } app.Action = func(ctx *Context) error { return fmt.Errorf("should not get here") } err := app.Run([]string{"", "--test-completion", "--" + BashCompletionFlag.GetName()}) if err != nil { t.Errorf("app should not return an error: %s", err) } } func TestHandleActionActuallyWorksWithActions(t *testing.T) { var f ActionFunc called := false f = func(c *Context) error { called = true return nil } err := HandleAction(f, nil) if err != nil { t.Errorf("Should not have errored: %v", err) } if !called { t.Errorf("Function was not called") } } func TestWhenExitSubCommandWithCodeThenAppQuitUnexpectedly(t *testing.T) { testCode := 104 app := NewApp() app.Commands = []Command{ Command{ Name: "cmd", Subcommands: []Command{ Command{ Name: "subcmd", Action: func(c *Context) error { return NewExitError("exit error", testCode) }, }, }, }, } // set user function as ExitErrHandler var exitCodeFromExitErrHandler int app.ExitErrHandler = func(c *Context, err error) { if exitErr, ok := err.(ExitCoder); ok { t.Log(exitErr) exitCodeFromExitErrHandler = exitErr.ExitCode() } } // keep and restore original OsExiter origExiter := OsExiter defer func() { OsExiter = origExiter }() // set user function as OsExiter var exitCodeFromOsExiter int OsExiter = func(exitCode int) { exitCodeFromOsExiter = exitCode } app.Run([]string{ "myapp", "cmd", "subcmd", }) if exitCodeFromOsExiter != 0 { t.Errorf("exitCodeFromOsExiter should not change, but its value is %v", exitCodeFromOsExiter) } if exitCodeFromExitErrHandler != testCode { t.Errorf("exitCodeFromOsExiter valeu should be %v, but its value is %v", testCode, exitCodeFromExitErrHandler) } } cli-1.22.4/appveyor.yml000066400000000000000000000007741364073550300147550ustar00rootroot00000000000000version: "{build}" os: Windows Server 2016 image: Visual Studio 2017 clone_folder: c:\gopath\src\github.com\urfave\cli cache: - node_modules environment: GOPATH: C:\gopath GOVERSION: 1.11.x GO111MODULE: on GOPROXY: https://proxy.golang.org install: - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% - go version - go env - go get github.com/urfave/gfmrun/cmd/gfmrun - go mod vendor build_script: - go run build.go vet - go run build.go test - go run build.go gfmrun docs/v1/manual.md cli-1.22.4/autocomplete/000077500000000000000000000000001364073550300150565ustar00rootroot00000000000000cli-1.22.4/autocomplete/bash_autocomplete000077500000000000000000000010741364073550300205040ustar00rootroot00000000000000#! /bin/bash : ${PROG:=$(basename ${BASH_SOURCE})} _cli_bash_autocomplete() { if [[ "${COMP_WORDS[0]}" != "source" ]]; then local cur opts base COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" if [[ "$cur" == "-"* ]]; then opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion ) else opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) fi COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi } complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG unset PROG cli-1.22.4/autocomplete/zsh_autocomplete000066400000000000000000000003561364073550300203720ustar00rootroot00000000000000#compdef $PROG _cli_zsh_autocomplete() { local -a opts opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-bash-completion)}") _describe 'values' opts return } compdef _cli_zsh_autocomplete $PROG cli-1.22.4/build.go000066400000000000000000000053041364073550300140050ustar00rootroot00000000000000//+build ignore package main import ( "bufio" "bytes" "fmt" "io/ioutil" "log" "os" "os/exec" "strings" "github.com/urfave/cli" ) var packages = []string{"cli", "altsrc"} func main() { app := cli.NewApp() app.Name = "builder" app.Usage = "Generates a new urfave/cli build!" app.Commands = cli.Commands{ cli.Command{ Name: "vet", Action: VetActionFunc, }, cli.Command{ Name: "test", Action: TestActionFunc, }, cli.Command{ Name: "gfmrun", Action: GfmrunActionFunc, }, cli.Command{ Name: "toc", Action: TocActionFunc, }, } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } func runCmd(arg string, args ...string) error { cmd := exec.Command(arg, args...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() } func VetActionFunc(_ *cli.Context) error { return runCmd("go", "vet") } func TestActionFunc(c *cli.Context) error { for _, pkg := range packages { var packageName string if pkg == "cli" { packageName = "github.com/urfave/cli" } else { packageName = fmt.Sprintf("github.com/urfave/cli/%s", pkg) } coverProfile := fmt.Sprintf("--coverprofile=%s.coverprofile", pkg) err := runCmd("go", "test", "-v", coverProfile, packageName) if err != nil { return err } } return testCleanup() } func testCleanup() error { var out bytes.Buffer for _, pkg := range packages { file, err := os.Open(fmt.Sprintf("%s.coverprofile", pkg)) if err != nil { return err } b, err := ioutil.ReadAll(file) if err != nil { return err } out.Write(b) err = file.Close() if err != nil { return err } err = os.Remove(fmt.Sprintf("%s.coverprofile", pkg)) if err != nil { return err } } outFile, err := os.Create("coverage.txt") if err != nil { return err } _, err = out.WriteTo(outFile) if err != nil { return err } err = outFile.Close() if err != nil { return err } return nil } func GfmrunActionFunc(c *cli.Context) error { filename := c.Args().Get(0) if filename == "" { filename = "README.md" } file, err := os.Open(filename) if err != nil { return err } var counter int scanner := bufio.NewScanner(file) for scanner.Scan() { if strings.Contains(scanner.Text(), "package main") { counter++ } } err = scanner.Err() if err != nil { return err } return runCmd("gfmrun", "-c", fmt.Sprint(counter), "-s", filename) } func TocActionFunc(c *cli.Context) error { filename := c.Args().Get(0) if filename == "" { filename = "README.md" } err := runCmd("node_modules/.bin/markdown-toc", "-i", filename) if err != nil { return err } err = runCmd("git", "diff", "--exit-code") if err != nil { return err } return nil } cli-1.22.4/category.go000066400000000000000000000021231364073550300145170ustar00rootroot00000000000000package cli // CommandCategories is a slice of *CommandCategory. type CommandCategories []*CommandCategory // CommandCategory is a category containing commands. type CommandCategory struct { Name string Commands Commands } func (c CommandCategories) Less(i, j int) bool { return lexicographicLess(c[i].Name, c[j].Name) } func (c CommandCategories) Len() int { return len(c) } func (c CommandCategories) Swap(i, j int) { c[i], c[j] = c[j], c[i] } // AddCommand adds a command to a category. func (c CommandCategories) AddCommand(category string, command Command) CommandCategories { for _, commandCategory := range c { if commandCategory.Name == category { commandCategory.Commands = append(commandCategory.Commands, command) return c } } return append(c, &CommandCategory{Name: category, Commands: []Command{command}}) } // VisibleCommands returns a slice of the Commands with Hidden=false func (c *CommandCategory) VisibleCommands() []Command { ret := []Command{} for _, command := range c.Commands { if !command.Hidden { ret = append(ret, command) } } return ret } cli-1.22.4/cli.go000066400000000000000000000012731364073550300134560ustar00rootroot00000000000000// Package cli provides a minimal framework for creating and organizing command line // Go applications. cli is designed to be easy to understand and write, the most simple // cli application can be written as follows: // func main() { // cli.NewApp().Run(os.Args) // } // // Of course this application does not do much, so let's make this an actual application: // func main() { // app := cli.NewApp() // app.Name = "greet" // app.Usage = "say a greeting" // app.Action = func(c *cli.Context) error { // println("Greetings") // return nil // } // // app.Run(os.Args) // } package cli //go:generate go run flag-gen/main.go flag-gen/assets_vfsdata.go cli-1.22.4/command.go000066400000000000000000000233361364073550300143310ustar00rootroot00000000000000package cli import ( "flag" "fmt" "sort" "strings" ) // Command is a subcommand for a cli.App. type Command struct { // The name of the command Name string // short name of the command. Typically one character (deprecated, use `Aliases`) ShortName string // A list of aliases for the command Aliases []string // A short description of the usage of this command Usage string // Custom text to show on USAGE section of help UsageText string // A longer explanation of how the command works Description string // A short description of the arguments of this command ArgsUsage string // The category the command is part of Category string // The function to call when checking for bash command completions BashComplete BashCompleteFunc // An action to execute before any sub-subcommands are run, but after the context is ready // If a non-nil error is returned, no sub-subcommands are run Before BeforeFunc // An action to execute after any subcommands are run, but after the subcommand has finished // It is run even if Action() panics After AfterFunc // The function to call when this command is invoked Action interface{} // TODO: replace `Action: interface{}` with `Action: ActionFunc` once some kind // of deprecation period has passed, maybe? // Execute this function if a usage error occurs. OnUsageError OnUsageErrorFunc // List of child commands Subcommands Commands // List of flags to parse Flags []Flag // Treat all flags as normal arguments if true SkipFlagParsing bool // Skip argument reordering which attempts to move flags before arguments, // but only works if all flags appear after all arguments. This behavior was // removed n version 2 since it only works under specific conditions so we // backport here by exposing it as an option for compatibility. SkipArgReorder bool // Boolean to hide built-in help command HideHelp bool // Boolean to hide this command from help or completion Hidden bool // Boolean to enable short-option handling so user can combine several // single-character bool arguments into one // i.e. foobar -o -v -> foobar -ov UseShortOptionHandling bool // Full name of command for help, defaults to full command name, including parent commands. HelpName string commandNamePath []string // CustomHelpTemplate the text template for the command help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. CustomHelpTemplate string } type CommandsByName []Command func (c CommandsByName) Len() int { return len(c) } func (c CommandsByName) Less(i, j int) bool { return lexicographicLess(c[i].Name, c[j].Name) } func (c CommandsByName) Swap(i, j int) { c[i], c[j] = c[j], c[i] } // FullName returns the full name of the command. // For subcommands this ensures that parent commands are part of the command path func (c Command) FullName() string { if c.commandNamePath == nil { return c.Name } return strings.Join(c.commandNamePath, " ") } // Commands is a slice of Command type Commands []Command // Run invokes the command given the context, parses ctx.Args() to generate command-specific flags func (c Command) Run(ctx *Context) (err error) { if len(c.Subcommands) > 0 { return c.startApp(ctx) } if !c.HideHelp && (HelpFlag != BoolFlag{}) { // append help to flags c.Flags = append( c.Flags, HelpFlag, ) } if ctx.App.UseShortOptionHandling { c.UseShortOptionHandling = true } set, err := c.parseFlags(ctx.Args().Tail(), ctx.shellComplete) context := NewContext(ctx.App, set, ctx) context.Command = c if checkCommandCompletions(context, c.Name) { return nil } if err != nil { if c.OnUsageError != nil { err := c.OnUsageError(context, err, false) context.App.handleExitCoder(context, err) return err } _, _ = fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error()) _, _ = fmt.Fprintln(context.App.Writer) _ = ShowCommandHelp(context, c.Name) return err } if checkCommandHelp(context, c.Name) { return nil } cerr := checkRequiredFlags(c.Flags, context) if cerr != nil { _ = ShowCommandHelp(context, c.Name) return cerr } if c.After != nil { defer func() { afterErr := c.After(context) if afterErr != nil { context.App.handleExitCoder(context, err) if err != nil { err = NewMultiError(err, afterErr) } else { err = afterErr } } }() } if c.Before != nil { err = c.Before(context) if err != nil { _ = ShowCommandHelp(context, c.Name) context.App.handleExitCoder(context, err) return err } } if c.Action == nil { c.Action = helpSubcommand.Action } err = HandleAction(c.Action, context) if err != nil { context.App.handleExitCoder(context, err) } return err } func (c *Command) parseFlags(args Args, shellComplete bool) (*flag.FlagSet, error) { if c.SkipFlagParsing { set, err := c.newFlagSet() if err != nil { return nil, err } return set, set.Parse(append([]string{"--"}, args...)) } if !c.SkipArgReorder { args = reorderArgs(c.Flags, args) } set, err := c.newFlagSet() if err != nil { return nil, err } err = parseIter(set, c, args, shellComplete) if err != nil { return nil, err } err = normalizeFlags(c.Flags, set) if err != nil { return nil, err } return set, nil } func (c *Command) newFlagSet() (*flag.FlagSet, error) { return flagSet(c.Name, c.Flags) } func (c *Command) useShortOptionHandling() bool { return c.UseShortOptionHandling } // reorderArgs moves all flags (via reorderedArgs) before the rest of // the arguments (remainingArgs) as this is what flag expects. func reorderArgs(commandFlags []Flag, args []string) []string { var remainingArgs, reorderedArgs []string nextIndexMayContainValue := false for i, arg := range args { // dont reorder any args after a -- // read about -- here: // https://unix.stackexchange.com/questions/11376/what-does-double-dash-mean-also-known-as-bare-double-dash if arg == "--" { remainingArgs = append(remainingArgs, args[i:]...) break // checks if this arg is a value that should be re-ordered next to its associated flag } else if nextIndexMayContainValue && !strings.HasPrefix(arg, "-") { nextIndexMayContainValue = false reorderedArgs = append(reorderedArgs, arg) // checks if this is an arg that should be re-ordered } else if argIsFlag(commandFlags, arg) { // we have determined that this is a flag that we should re-order reorderedArgs = append(reorderedArgs, arg) // if this arg does not contain a "=", then the next index may contain the value for this flag nextIndexMayContainValue = !strings.Contains(arg, "=") // simply append any remaining args } else { remainingArgs = append(remainingArgs, arg) } } return append(reorderedArgs, remainingArgs...) } // argIsFlag checks if an arg is one of our command flags func argIsFlag(commandFlags []Flag, arg string) bool { // checks if this is just a `-`, and so definitely not a flag if arg == "-" { return false } // flags always start with a - if !strings.HasPrefix(arg, "-") { return false } // this line turns `--flag` into `flag` if strings.HasPrefix(arg, "--") { arg = strings.Replace(arg, "-", "", 2) } // this line turns `-flag` into `flag` if strings.HasPrefix(arg, "-") { arg = strings.Replace(arg, "-", "", 1) } // this line turns `flag=value` into `flag` arg = strings.Split(arg, "=")[0] // look through all the flags, to see if the `arg` is one of our flags for _, flag := range commandFlags { for _, key := range strings.Split(flag.GetName(), ",") { key := strings.TrimSpace(key) if key == arg { return true } } } // return false if this arg was not one of our flags return false } // Names returns the names including short names and aliases. func (c Command) Names() []string { names := []string{c.Name} if c.ShortName != "" { names = append(names, c.ShortName) } return append(names, c.Aliases...) } // HasName returns true if Command.Name or Command.ShortName matches given name func (c Command) HasName(name string) bool { for _, n := range c.Names() { if n == name { return true } } return false } func (c Command) startApp(ctx *Context) error { app := NewApp() app.Metadata = ctx.App.Metadata app.ExitErrHandler = ctx.App.ExitErrHandler // set the name and usage app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) if c.HelpName == "" { app.HelpName = c.HelpName } else { app.HelpName = app.Name } app.Usage = c.Usage app.Description = c.Description app.ArgsUsage = c.ArgsUsage // set CommandNotFound app.CommandNotFound = ctx.App.CommandNotFound app.CustomAppHelpTemplate = c.CustomHelpTemplate // set the flags and commands app.Commands = c.Subcommands app.Flags = c.Flags app.HideHelp = c.HideHelp app.Version = ctx.App.Version app.HideVersion = ctx.App.HideVersion app.Compiled = ctx.App.Compiled app.Author = ctx.App.Author app.Email = ctx.App.Email app.Writer = ctx.App.Writer app.ErrWriter = ctx.App.ErrWriter app.UseShortOptionHandling = ctx.App.UseShortOptionHandling app.categories = CommandCategories{} for _, command := range c.Subcommands { app.categories = app.categories.AddCommand(command.Category, command) } sort.Sort(app.categories) // bash completion app.EnableBashCompletion = ctx.App.EnableBashCompletion if c.BashComplete != nil { app.BashComplete = c.BashComplete } // set the actions app.Before = c.Before app.After = c.After if c.Action != nil { app.Action = c.Action } else { app.Action = helpSubcommand.Action } app.OnUsageError = c.OnUsageError for index, cc := range app.Commands { app.Commands[index].commandNamePath = []string{c.Name, cc.Name} } return app.RunAsSubcommand(ctx) } // VisibleFlags returns a slice of the Flags with Hidden=false func (c Command) VisibleFlags() []Flag { return visibleFlags(c.Flags) } cli-1.22.4/command_test.go000066400000000000000000000270421364073550300153660ustar00rootroot00000000000000package cli import ( "bytes" "errors" "flag" "fmt" "io/ioutil" "strings" "testing" ) func TestCommandFlagParsing(t *testing.T) { cases := []struct { testArgs []string skipFlagParsing bool skipArgReorder bool expectedErr error UseShortOptionHandling bool }{ // Test normal "not ignoring flags" flow {[]string{"test-cmd", "blah", "blah", "-break"}, false, false, nil, false}, // Test no arg reorder {[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil, false}, {[]string{"test-cmd", "blah", "blah", "-break", "ls", "-l"}, false, true, nil, true}, {[]string{"test-cmd", "blah", "blah"}, true, false, nil, false}, // Test SkipFlagParsing without any args that look like flags {[]string{"test-cmd", "blah", "-break"}, true, false, nil, false}, // Test SkipFlagParsing with random flag arg {[]string{"test-cmd", "blah", "-help"}, true, false, nil, false}, // Test SkipFlagParsing with "special" help flag arg {[]string{"test-cmd", "blah"}, false, false, nil, true}, // Test UseShortOptionHandling } for _, c := range cases { app := NewApp() app.Writer = ioutil.Discard set := flag.NewFlagSet("test", 0) _ = set.Parse(c.testArgs) context := NewContext(app, set, nil) command := Command{ Name: "test-cmd", Aliases: []string{"tc"}, Usage: "this is for testing", Description: "testing", Action: func(_ *Context) error { return nil }, SkipFlagParsing: c.skipFlagParsing, SkipArgReorder: c.skipArgReorder, UseShortOptionHandling: c.UseShortOptionHandling, } err := command.Run(context) expect(t, err, c.expectedErr) expect(t, []string(context.Args()), c.testArgs) } } func TestParseAndRunShortOpts(t *testing.T) { cases := []struct { testArgs []string expectedErr error expectedArgs []string }{ {[]string{"foo", "test", "-a"}, nil, []string{}}, {[]string{"foo", "test", "-c", "arg1", "arg2"}, nil, []string{"arg1", "arg2"}}, {[]string{"foo", "test", "-f"}, nil, []string{}}, {[]string{"foo", "test", "-ac", "--fgh"}, nil, []string{}}, {[]string{"foo", "test", "-af"}, nil, []string{}}, {[]string{"foo", "test", "-cf"}, nil, []string{}}, {[]string{"foo", "test", "-acf"}, nil, []string{}}, {[]string{"foo", "test", "--acf"}, errors.New("flag provided but not defined: -acf"), nil}, {[]string{"foo", "test", "-invalid"}, errors.New("flag provided but not defined: -invalid"), nil}, {[]string{"foo", "test", "-acf", "-invalid"}, errors.New("flag provided but not defined: -invalid"), nil}, {[]string{"foo", "test", "--invalid"}, errors.New("flag provided but not defined: -invalid"), nil}, {[]string{"foo", "test", "-acf", "--invalid"}, errors.New("flag provided but not defined: -invalid"), nil}, {[]string{"foo", "test", "-acf", "arg1", "-invalid"}, nil, []string{"arg1", "-invalid"}}, {[]string{"foo", "test", "-acf", "arg1", "--invalid"}, nil, []string{"arg1", "--invalid"}}, {[]string{"foo", "test", "-acfi", "not-arg", "arg1", "-invalid"}, nil, []string{"arg1", "-invalid"}}, {[]string{"foo", "test", "-i", "ivalue"}, nil, []string{}}, {[]string{"foo", "test", "-i", "ivalue", "arg1"}, nil, []string{"arg1"}}, {[]string{"foo", "test", "-i"}, errors.New("flag needs an argument: -i"), nil}, } for _, c := range cases { var args []string cmd := Command{ Name: "test", Usage: "this is for testing", Description: "testing", Action: func(c *Context) error { args = c.Args() return nil }, SkipArgReorder: true, UseShortOptionHandling: true, Flags: []Flag{ BoolFlag{Name: "abc, a"}, BoolFlag{Name: "cde, c"}, BoolFlag{Name: "fgh, f"}, StringFlag{Name: "ijk, i"}, }, } app := NewApp() app.Commands = []Command{cmd} err := app.Run(c.testArgs) expect(t, err, c.expectedErr) expect(t, args, c.expectedArgs) } } func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { app := NewApp() app.Commands = []Command{ { Name: "bar", Before: func(c *Context) error { return fmt.Errorf("before error") }, After: func(c *Context) error { return fmt.Errorf("after error") }, }, } err := app.Run([]string{"foo", "bar"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.Contains(err.Error(), "before error") { t.Errorf("expected text of error from Before method, but got none in \"%v\"", err) } if !strings.Contains(err.Error(), "after error") { t.Errorf("expected text of error from After method, but got none in \"%v\"", err) } } func TestCommand_Run_BeforeSavesMetadata(t *testing.T) { var receivedMsgFromAction string var receivedMsgFromAfter string app := NewApp() app.Commands = []Command{ { Name: "bar", Before: func(c *Context) error { c.App.Metadata["msg"] = "hello world" return nil }, Action: func(c *Context) error { msg, ok := c.App.Metadata["msg"] if !ok { return errors.New("msg not found") } receivedMsgFromAction = msg.(string) return nil }, After: func(c *Context) error { msg, ok := c.App.Metadata["msg"] if !ok { return errors.New("msg not found") } receivedMsgFromAfter = msg.(string) return nil }, }, } err := app.Run([]string{"foo", "bar"}) if err != nil { t.Fatalf("expected no error from Run, got %s", err) } expectedMsg := "hello world" if receivedMsgFromAction != expectedMsg { t.Fatalf("expected msg from Action to match. Given: %q\nExpected: %q", receivedMsgFromAction, expectedMsg) } if receivedMsgFromAfter != expectedMsg { t.Fatalf("expected msg from After to match. Given: %q\nExpected: %q", receivedMsgFromAction, expectedMsg) } } func TestCommand_OnUsageError_hasCommandContext(t *testing.T) { app := NewApp() app.Commands = []Command{ { Name: "bar", Flags: []Flag{ IntFlag{Name: "flag"}, }, OnUsageError: func(c *Context, err error, _ bool) error { return fmt.Errorf("intercepted in %s: %s", c.Command.Name, err.Error()) }, }, } err := app.Run([]string{"foo", "bar", "--flag=wrong"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.HasPrefix(err.Error(), "intercepted in bar") { t.Errorf("Expect an intercepted error, but got \"%v\"", err) } } func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) { app := NewApp() app.Commands = []Command{ { Name: "bar", Flags: []Flag{ IntFlag{Name: "flag"}, }, OnUsageError: func(c *Context, err error, _ bool) error { if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") { t.Errorf("Expect an invalid value error, but got \"%v\"", err) } return errors.New("intercepted: " + err.Error()) }, }, } err := app.Run([]string{"foo", "bar", "--flag=wrong"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.HasPrefix(err.Error(), "intercepted: invalid value") { t.Errorf("Expect an intercepted error, but got \"%v\"", err) } } func TestCommand_OnUsageError_WithSubcommand(t *testing.T) { app := NewApp() app.Commands = []Command{ { Name: "bar", Subcommands: []Command{ { Name: "baz", }, }, Flags: []Flag{ IntFlag{Name: "flag"}, }, OnUsageError: func(c *Context, err error, _ bool) error { if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") { t.Errorf("Expect an invalid value error, but got \"%v\"", err) } return errors.New("intercepted: " + err.Error()) }, }, } err := app.Run([]string{"foo", "bar", "--flag=wrong"}) if err == nil { t.Fatalf("expected to receive error from Run, got none") } if !strings.HasPrefix(err.Error(), "intercepted: invalid value") { t.Errorf("Expect an intercepted error, but got \"%v\"", err) } } func TestCommand_Run_SubcommandsCanUseErrWriter(t *testing.T) { app := NewApp() app.ErrWriter = ioutil.Discard app.Commands = []Command{ { Name: "bar", Usage: "this is for testing", Subcommands: []Command{ { Name: "baz", Usage: "this is for testing", Action: func(c *Context) error { if c.App.ErrWriter != ioutil.Discard { return fmt.Errorf("ErrWriter not passed") } return nil }, }, }, }, } err := app.Run([]string{"foo", "bar", "baz"}) if err != nil { t.Fatal(err) } } func TestCommandFlagReordering(t *testing.T) { cases := []struct { testArgs []string expectedValue string expectedArgs []string expectedErr error }{ {[]string{"some-exec", "some-command", "some-arg", "--flag", "foo"}, "foo", []string{"some-arg"}, nil}, {[]string{"some-exec", "some-command", "some-arg", "--flag=foo"}, "foo", []string{"some-arg"}, nil}, {[]string{"some-exec", "some-command", "--flag=foo", "some-arg"}, "foo", []string{"some-arg"}, nil}, } for _, c := range cases { value := "" var args []string app := &App{ Commands: []Command{ { Name: "some-command", Flags: []Flag{ StringFlag{Name: "flag"}, }, Action: func(c *Context) { fmt.Printf("%+v\n", c.String("flag")) value = c.String("flag") args = c.Args() }, }, }, } err := app.Run(c.testArgs) expect(t, err, c.expectedErr) expect(t, value, c.expectedValue) expect(t, args, c.expectedArgs) } } func TestCommandSkipFlagParsing(t *testing.T) { cases := []struct { testArgs []string expectedArgs []string expectedErr error }{ {[]string{"some-exec", "some-command", "some-arg", "--flag", "foo"}, []string{"some-arg", "--flag", "foo"}, nil}, {[]string{"some-exec", "some-command", "some-arg", "--flag=foo"}, []string{"some-arg", "--flag=foo"}, nil}, } for _, c := range cases { var args []string app := &App{ Commands: []Command{ { SkipFlagParsing: true, Name: "some-command", Flags: []Flag{ StringFlag{Name: "flag"}, }, Action: func(c *Context) { fmt.Printf("%+v\n", c.String("flag")) args = c.Args() }, }, }, } err := app.Run(c.testArgs) expect(t, err, c.expectedErr) expect(t, args, c.expectedArgs) } } func TestCommand_Run_CustomShellCompleteAcceptsMalformedFlags(t *testing.T) { cases := []struct { testArgs []string expectedOut string }{ {testArgs: []string{"--undefined"}, expectedOut: "found 0 args | flag 'number' set: false"}, {testArgs: []string{"--number"}, expectedOut: "found 0 args | flag 'number' set: false"}, {testArgs: []string{"--number", "fourty-two"}, expectedOut: "found 0 args | flag 'number' set: false"}, {testArgs: []string{"--number", "42"}, expectedOut: "found 0 args | flag 'number' set: true"}, {testArgs: []string{"--number", "42", "newArg"}, expectedOut: "found 1 args | flag 'number' set: true"}, {testArgs: []string{"--undefined", "--number", "42", "newArg"}, expectedOut: "found 1 args | flag 'number' set: true"}, } for _, c := range cases { var outputBuffer bytes.Buffer app := &App{ Writer: &outputBuffer, EnableBashCompletion: true, Commands: []Command{ { Name: "bar", Usage: "this is for testing", Flags: []Flag{ &IntFlag{ Name: "number", Usage: "A number to parse", }, }, BashComplete: func(c *Context) { fmt.Fprintf(c.App.Writer, "found %d args | flag 'number' set: %t", c.NArg(), c.IsSet("number")) }, }, }, } osArgs := []string{"foo", "bar"} osArgs = append(osArgs, c.testArgs...) osArgs = append(osArgs, "--generate-bash-completion") err := app.Run(osArgs) stdout := outputBuffer.String() expect(t, err, nil) expect(t, stdout, c.expectedOut) } } cli-1.22.4/context.go000066400000000000000000000176451364073550300144050ustar00rootroot00000000000000package cli import ( "errors" "flag" "fmt" "os" "reflect" "strings" "syscall" ) // Context is a type that is passed through to // each Handler action in a cli application. Context // can be used to retrieve context-specific Args and // parsed command-line options. type Context struct { App *App Command Command shellComplete bool flagSet *flag.FlagSet setFlags map[string]bool parentContext *Context } // NewContext creates a new context. For use in when invoking an App or Command action. func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { c := &Context{App: app, flagSet: set, parentContext: parentCtx} if parentCtx != nil { c.shellComplete = parentCtx.shellComplete } return c } // NumFlags returns the number of flags set func (c *Context) NumFlags() int { return c.flagSet.NFlag() } // Set sets a context flag to a value. func (c *Context) Set(name, value string) error { c.setFlags = nil return c.flagSet.Set(name, value) } // GlobalSet sets a context flag to a value on the global flagset func (c *Context) GlobalSet(name, value string) error { globalContext(c).setFlags = nil return globalContext(c).flagSet.Set(name, value) } // IsSet determines if the flag was actually set func (c *Context) IsSet(name string) bool { if c.setFlags == nil { c.setFlags = make(map[string]bool) c.flagSet.Visit(func(f *flag.Flag) { c.setFlags[f.Name] = true }) c.flagSet.VisitAll(func(f *flag.Flag) { if _, ok := c.setFlags[f.Name]; ok { return } c.setFlags[f.Name] = false }) // XXX hack to support IsSet for flags with EnvVar // // There isn't an easy way to do this with the current implementation since // whether a flag was set via an environment variable is very difficult to // determine here. Instead, we intend to introduce a backwards incompatible // change in version 2 to add `IsSet` to the Flag interface to push the // responsibility closer to where the information required to determine // whether a flag is set by non-standard means such as environment // variables is available. // // See https://github.com/urfave/cli/issues/294 for additional discussion flags := c.Command.Flags if c.Command.Name == "" { // cannot == Command{} since it contains slice types if c.App != nil { flags = c.App.Flags } } for _, f := range flags { eachName(f.GetName(), func(name string) { if isSet, ok := c.setFlags[name]; isSet || !ok { // Check if a flag is set if isSet { // If the flag is set, also set its other aliases eachName(f.GetName(), func(name string) { c.setFlags[name] = true }) } return } val := reflect.ValueOf(f) if val.Kind() == reflect.Ptr { val = val.Elem() } filePathValue := val.FieldByName("FilePath") if filePathValue.IsValid() { eachName(filePathValue.String(), func(filePath string) { if _, err := os.Stat(filePath); err == nil { c.setFlags[name] = true return } }) } envVarValue := val.FieldByName("EnvVar") if envVarValue.IsValid() { eachName(envVarValue.String(), func(envVar string) { envVar = strings.TrimSpace(envVar) if _, ok := syscall.Getenv(envVar); ok { c.setFlags[name] = true return } }) } }) } } return c.setFlags[name] } // GlobalIsSet determines if the global flag was actually set func (c *Context) GlobalIsSet(name string) bool { ctx := c if ctx.parentContext != nil { ctx = ctx.parentContext } for ; ctx != nil; ctx = ctx.parentContext { if ctx.IsSet(name) { return true } } return false } // FlagNames returns a slice of flag names used in this context. func (c *Context) FlagNames() (names []string) { for _, f := range c.Command.Flags { name := strings.Split(f.GetName(), ",")[0] if name == "help" { continue } names = append(names, name) } return } // GlobalFlagNames returns a slice of global flag names used by the app. func (c *Context) GlobalFlagNames() (names []string) { for _, f := range c.App.Flags { name := strings.Split(f.GetName(), ",")[0] if name == "help" || name == "version" { continue } names = append(names, name) } return } // Parent returns the parent context, if any func (c *Context) Parent() *Context { return c.parentContext } // value returns the value of the flag coressponding to `name` func (c *Context) value(name string) interface{} { return c.flagSet.Lookup(name).Value.(flag.Getter).Get() } // Args contains apps console arguments type Args []string // Args returns the command line arguments associated with the context. func (c *Context) Args() Args { args := Args(c.flagSet.Args()) return args } // NArg returns the number of the command line arguments. func (c *Context) NArg() int { return len(c.Args()) } // Get returns the nth argument, or else a blank string func (a Args) Get(n int) string { if len(a) > n { return a[n] } return "" } // First returns the first argument, or else a blank string func (a Args) First() string { return a.Get(0) } // Tail returns the rest of the arguments (not the first one) // or else an empty string slice func (a Args) Tail() []string { if len(a) >= 2 { return []string(a)[1:] } return []string{} } // Present checks if there are any arguments present func (a Args) Present() bool { return len(a) != 0 } // Swap swaps arguments at the given indexes func (a Args) Swap(from, to int) error { if from >= len(a) || to >= len(a) { return errors.New("index out of range") } a[from], a[to] = a[to], a[from] return nil } func globalContext(ctx *Context) *Context { if ctx == nil { return nil } for { if ctx.parentContext == nil { return ctx } ctx = ctx.parentContext } } func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { if ctx.parentContext != nil { ctx = ctx.parentContext } for ; ctx != nil; ctx = ctx.parentContext { if f := ctx.flagSet.Lookup(name); f != nil { return ctx.flagSet } } return nil } func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { switch ff.Value.(type) { case *StringSlice: default: _ = set.Set(name, ff.Value.String()) } } func normalizeFlags(flags []Flag, set *flag.FlagSet) error { visited := make(map[string]bool) set.Visit(func(f *flag.Flag) { visited[f.Name] = true }) for _, f := range flags { parts := strings.Split(f.GetName(), ",") if len(parts) == 1 { continue } var ff *flag.Flag for _, name := range parts { name = strings.Trim(name, " ") if visited[name] { if ff != nil { return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) } ff = set.Lookup(name) } } if ff == nil { continue } for _, name := range parts { name = strings.Trim(name, " ") if !visited[name] { copyFlag(name, ff, set) } } } return nil } type requiredFlagsErr interface { error getMissingFlags() []string } type errRequiredFlags struct { missingFlags []string } func (e *errRequiredFlags) Error() string { numberOfMissingFlags := len(e.missingFlags) if numberOfMissingFlags == 1 { return fmt.Sprintf("Required flag %q not set", e.missingFlags[0]) } joinedMissingFlags := strings.Join(e.missingFlags, ", ") return fmt.Sprintf("Required flags %q not set", joinedMissingFlags) } func (e *errRequiredFlags) getMissingFlags() []string { return e.missingFlags } func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr { var missingFlags []string for _, f := range flags { if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() { var flagPresent bool var flagName string for _, key := range strings.Split(f.GetName(), ",") { if len(key) > 1 { flagName = key } if context.IsSet(strings.TrimSpace(key)) { flagPresent = true } } if !flagPresent && flagName != "" { missingFlags = append(missingFlags, flagName) } } } if len(missingFlags) != 0 { return &errRequiredFlags{missingFlags: missingFlags} } return nil } cli-1.22.4/context_test.go000066400000000000000000000417731364073550300154430ustar00rootroot00000000000000package cli import ( "flag" "os" "strings" "testing" "time" ) func TestNewContext(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int("myflag", 12, "doc") set.Int64("myflagInt64", int64(12), "doc") set.Uint("myflagUint", uint(93), "doc") set.Uint64("myflagUint64", uint64(93), "doc") set.Float64("myflag64", float64(17), "doc") globalSet := flag.NewFlagSet("test", 0) globalSet.Int("myflag", 42, "doc") globalSet.Int64("myflagInt64", int64(42), "doc") globalSet.Uint("myflagUint", uint(33), "doc") globalSet.Uint64("myflagUint64", uint64(33), "doc") globalSet.Float64("myflag64", float64(47), "doc") globalCtx := NewContext(nil, globalSet, nil) command := Command{Name: "mycommand"} c := NewContext(nil, set, globalCtx) c.Command = command expect(t, c.Int("myflag"), 12) expect(t, c.Int64("myflagInt64"), int64(12)) expect(t, c.Uint("myflagUint"), uint(93)) expect(t, c.Uint64("myflagUint64"), uint64(93)) expect(t, c.Float64("myflag64"), float64(17)) expect(t, c.GlobalInt("myflag"), 42) expect(t, c.GlobalInt64("myflagInt64"), int64(42)) expect(t, c.GlobalUint("myflagUint"), uint(33)) expect(t, c.GlobalUint64("myflagUint64"), uint64(33)) expect(t, c.GlobalFloat64("myflag64"), float64(47)) expect(t, c.Command.Name, "mycommand") } func TestContext_Int(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int("myflag", 12, "doc") c := NewContext(nil, set, nil) expect(t, c.Int("myflag"), 12) } func TestContext_Int64(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int64("myflagInt64", 12, "doc") c := NewContext(nil, set, nil) expect(t, c.Int64("myflagInt64"), int64(12)) } func TestContext_Uint(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Uint("myflagUint", uint(13), "doc") c := NewContext(nil, set, nil) expect(t, c.Uint("myflagUint"), uint(13)) } func TestContext_Uint64(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Uint64("myflagUint64", uint64(9), "doc") c := NewContext(nil, set, nil) expect(t, c.Uint64("myflagUint64"), uint64(9)) } func TestContext_GlobalInt(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int("myflag", 12, "doc") c := NewContext(nil, set, nil) expect(t, c.GlobalInt("myflag"), 12) expect(t, c.GlobalInt("nope"), 0) } func TestContext_GlobalInt64(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int64("myflagInt64", 12, "doc") c := NewContext(nil, set, nil) expect(t, c.GlobalInt64("myflagInt64"), int64(12)) expect(t, c.GlobalInt64("nope"), int64(0)) } func TestContext_Float64(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Float64("myflag", float64(17), "doc") c := NewContext(nil, set, nil) expect(t, c.Float64("myflag"), float64(17)) } func TestContext_GlobalFloat64(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Float64("myflag", float64(17), "doc") c := NewContext(nil, set, nil) expect(t, c.GlobalFloat64("myflag"), float64(17)) expect(t, c.GlobalFloat64("nope"), float64(0)) } func TestContext_Duration(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Duration("myflag", 12*time.Second, "doc") c := NewContext(nil, set, nil) expect(t, c.Duration("myflag"), 12*time.Second) } func TestContext_String(t *testing.T) { set := flag.NewFlagSet("test", 0) set.String("myflag", "hello world", "doc") c := NewContext(nil, set, nil) expect(t, c.String("myflag"), "hello world") } func TestContext_Bool(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") c := NewContext(nil, set, nil) expect(t, c.Bool("myflag"), false) } func TestContext_BoolT(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", true, "doc") c := NewContext(nil, set, nil) expect(t, c.BoolT("myflag"), true) } func TestContext_GlobalBool(t *testing.T) { set := flag.NewFlagSet("test", 0) globalSet := flag.NewFlagSet("test-global", 0) globalSet.Bool("myflag", false, "doc") globalCtx := NewContext(nil, globalSet, nil) c := NewContext(nil, set, globalCtx) expect(t, c.GlobalBool("myflag"), false) expect(t, c.GlobalBool("nope"), false) } func TestContext_GlobalBoolT(t *testing.T) { set := flag.NewFlagSet("test", 0) globalSet := flag.NewFlagSet("test-global", 0) globalSet.Bool("myflag", true, "doc") globalCtx := NewContext(nil, globalSet, nil) c := NewContext(nil, set, globalCtx) expect(t, c.GlobalBoolT("myflag"), true) expect(t, c.GlobalBoolT("nope"), false) } func TestContext_Args(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") c := NewContext(nil, set, nil) _ = set.Parse([]string{"--myflag", "bat", "baz"}) expect(t, len(c.Args()), 2) expect(t, c.Bool("myflag"), true) } func TestContext_NArg(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") c := NewContext(nil, set, nil) _ = set.Parse([]string{"--myflag", "bat", "baz"}) expect(t, c.NArg(), 2) } func TestContext_IsSet(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") set.String("otherflag", "hello world", "doc") globalSet := flag.NewFlagSet("test", 0) globalSet.Bool("myflagGlobal", true, "doc") globalCtx := NewContext(nil, globalSet, nil) c := NewContext(nil, set, globalCtx) _ = set.Parse([]string{"--myflag", "bat", "baz"}) _ = globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) expect(t, c.IsSet("myflag"), true) expect(t, c.IsSet("otherflag"), false) expect(t, c.IsSet("bogusflag"), false) expect(t, c.IsSet("myflagGlobal"), false) } func TestContext_IsSet_ShortAndFull_FlagNames(t *testing.T) { var ( numberIsSet, nIsSet bool tempIsSet, tIsSet bool usernameIsSet, uIsSet bool debugIsSet, dIsSet bool ) a := App { Flags: []Flag{ IntFlag{Name: "number, n"}, Float64Flag{Name: "temp, t"}, StringFlag{Name: "username, u"}, BoolFlag{Name: "debug, d"}, }, Action: func(ctx *Context) error { numberIsSet = ctx.IsSet("number") nIsSet = ctx.IsSet("n") tempIsSet = ctx.IsSet("temp") tIsSet = ctx.IsSet("t") usernameIsSet = ctx.IsSet("username") uIsSet = ctx.IsSet("u") debugIsSet = ctx.IsSet("debug") dIsSet = ctx.IsSet("d") return nil }, } tests := []struct { args[]string }{ {args: []string{"", "--number", "5", "--temp", "5.2", "--username", "ajitem", "--debug"}}, {args: []string{"", "-n", "5", "-t", "5.2", "-u", "ajitem", "-d"}}, } for _, tt := range tests { _ = a.Run(tt.args) expect(t, numberIsSet == nIsSet, true) expect(t, tempIsSet == tIsSet, true) expect(t, usernameIsSet == uIsSet, true) expect(t, debugIsSet == dIsSet, true) } } // XXX Corresponds to hack in context.IsSet for flags with EnvVar field // Should be moved to `flag_test` in v2 func TestContext_IsSet_fromEnv(t *testing.T) { var ( timeoutIsSet, tIsSet bool noEnvVarIsSet, nIsSet bool passwordIsSet, pIsSet bool unparsableIsSet, uIsSet bool ) clearenv() _ = os.Setenv("APP_TIMEOUT_SECONDS", "15.5") _ = os.Setenv("APP_PASSWORD", "") a := App{ Flags: []Flag{ Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, StringFlag{Name: "password, p", EnvVar: "APP_PASSWORD"}, Float64Flag{Name: "unparsable, u", EnvVar: "APP_UNPARSABLE"}, Float64Flag{Name: "no-env-var, n"}, }, Action: func(ctx *Context) error { timeoutIsSet = ctx.IsSet("timeout") tIsSet = ctx.IsSet("t") passwordIsSet = ctx.IsSet("password") pIsSet = ctx.IsSet("p") unparsableIsSet = ctx.IsSet("unparsable") uIsSet = ctx.IsSet("u") noEnvVarIsSet = ctx.IsSet("no-env-var") nIsSet = ctx.IsSet("n") return nil }, } _ = a.Run([]string{"run"}) expect(t, timeoutIsSet, true) expect(t, tIsSet, true) expect(t, passwordIsSet, true) expect(t, pIsSet, true) expect(t, noEnvVarIsSet, false) expect(t, nIsSet, false) _ = os.Setenv("APP_UNPARSABLE", "foobar") _ = a.Run([]string{"run"}) expect(t, unparsableIsSet, false) expect(t, uIsSet, false) } func TestContext_GlobalIsSet(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") set.String("otherflag", "hello world", "doc") globalSet := flag.NewFlagSet("test", 0) globalSet.Bool("myflagGlobal", true, "doc") globalSet.Bool("myflagGlobalUnset", true, "doc") globalCtx := NewContext(nil, globalSet, nil) c := NewContext(nil, set, globalCtx) _ = set.Parse([]string{"--myflag", "bat", "baz"}) _ = globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) expect(t, c.GlobalIsSet("myflag"), false) expect(t, c.GlobalIsSet("otherflag"), false) expect(t, c.GlobalIsSet("bogusflag"), false) expect(t, c.GlobalIsSet("myflagGlobal"), true) expect(t, c.GlobalIsSet("myflagGlobalUnset"), false) expect(t, c.GlobalIsSet("bogusGlobal"), false) } // XXX Corresponds to hack in context.IsSet for flags with EnvVar field // Should be moved to `flag_test` in v2 func TestContext_GlobalIsSet_fromEnv(t *testing.T) { var ( timeoutIsSet, tIsSet bool noEnvVarIsSet, nIsSet bool passwordIsSet, pIsSet bool passwordValue string unparsableIsSet, uIsSet bool overrideIsSet, oIsSet bool overrideValue string ) clearenv() _ = os.Setenv("APP_TIMEOUT_SECONDS", "15.5") _ = os.Setenv("APP_PASSWORD", "badpass") _ = os.Setenv("APP_OVERRIDE", "overridden") a := App{ Flags: []Flag{ Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, StringFlag{Name: "password, p", EnvVar: "APP_PASSWORD"}, Float64Flag{Name: "no-env-var, n"}, Float64Flag{Name: "unparsable, u", EnvVar: "APP_UNPARSABLE"}, StringFlag{Name: "overrides-default, o", Value: "default", EnvVar: "APP_OVERRIDE"}, }, Commands: []Command{ { Name: "hello", Action: func(ctx *Context) error { timeoutIsSet = ctx.GlobalIsSet("timeout") tIsSet = ctx.GlobalIsSet("t") passwordIsSet = ctx.GlobalIsSet("password") pIsSet = ctx.GlobalIsSet("p") passwordValue = ctx.GlobalString("password") unparsableIsSet = ctx.GlobalIsSet("unparsable") uIsSet = ctx.GlobalIsSet("u") noEnvVarIsSet = ctx.GlobalIsSet("no-env-var") nIsSet = ctx.GlobalIsSet("n") overrideIsSet = ctx.GlobalIsSet("overrides-default") oIsSet = ctx.GlobalIsSet("o") overrideValue = ctx.GlobalString("overrides-default") return nil }, }, }, } if err := a.Run([]string{"run", "hello"}); err != nil { t.Logf("error running Run(): %+v", err) } expect(t, timeoutIsSet, true) expect(t, tIsSet, true) expect(t, passwordIsSet, true) expect(t, pIsSet, true) expect(t, passwordValue, "badpass") expect(t, unparsableIsSet, false) expect(t, noEnvVarIsSet, false) expect(t, nIsSet, false) expect(t, overrideIsSet, true) expect(t, oIsSet, true) expect(t, overrideValue, "overridden") _ = os.Setenv("APP_UNPARSABLE", "foobar") if err := a.Run([]string{"run"}); err != nil { t.Logf("error running Run(): %+v", err) } expect(t, unparsableIsSet, false) expect(t, uIsSet, false) } func TestContext_NumFlags(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") set.String("otherflag", "hello world", "doc") globalSet := flag.NewFlagSet("test", 0) globalSet.Bool("myflagGlobal", true, "doc") globalCtx := NewContext(nil, globalSet, nil) c := NewContext(nil, set, globalCtx) _ = set.Parse([]string{"--myflag", "--otherflag=foo"}) _ = globalSet.Parse([]string{"--myflagGlobal"}) expect(t, c.NumFlags(), 2) } func TestContext_GlobalFlag(t *testing.T) { var globalFlag string var globalFlagSet bool app := NewApp() app.Flags = []Flag{ StringFlag{Name: "global, g", Usage: "global"}, } app.Action = func(c *Context) error { globalFlag = c.GlobalString("global") globalFlagSet = c.GlobalIsSet("global") return nil } _ = app.Run([]string{"command", "-g", "foo"}) expect(t, globalFlag, "foo") expect(t, globalFlagSet, true) } func TestContext_GlobalFlagsInSubcommands(t *testing.T) { subcommandRun := false parentFlag := false app := NewApp() app.Flags = []Flag{ BoolFlag{Name: "debug, d", Usage: "Enable debugging"}, } app.Commands = []Command{ { Name: "foo", Flags: []Flag{ BoolFlag{Name: "parent, p", Usage: "Parent flag"}, }, Subcommands: []Command{ { Name: "bar", Action: func(c *Context) error { if c.GlobalBool("debug") { subcommandRun = true } if c.GlobalBool("parent") { parentFlag = true } return nil }, }, }, }, } _ = app.Run([]string{"command", "-d", "foo", "-p", "bar"}) expect(t, subcommandRun, true) expect(t, parentFlag, true) } func TestContext_Set(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int("int", 5, "an int") c := NewContext(nil, set, nil) expect(t, c.IsSet("int"), false) _ = c.Set("int", "1") expect(t, c.Int("int"), 1) expect(t, c.IsSet("int"), true) } func TestContext_GlobalSet(t *testing.T) { gSet := flag.NewFlagSet("test", 0) gSet.Int("int", 5, "an int") set := flag.NewFlagSet("sub", 0) set.Int("int", 3, "an int") pc := NewContext(nil, gSet, nil) c := NewContext(nil, set, pc) _ = c.Set("int", "1") expect(t, c.Int("int"), 1) expect(t, c.GlobalInt("int"), 5) expect(t, c.GlobalIsSet("int"), false) _ = c.GlobalSet("int", "1") expect(t, c.Int("int"), 1) expect(t, c.GlobalInt("int"), 1) expect(t, c.GlobalIsSet("int"), true) } func TestCheckRequiredFlags(t *testing.T) { tdata := []struct { testCase string parseInput []string envVarInput [2]string flags []Flag expectedAnError bool expectedErrorContents []string }{ { testCase: "empty", }, { testCase: "optional", flags: []Flag{ StringFlag{Name: "optionalFlag"}, }, }, { testCase: "required", flags: []Flag{ StringFlag{Name: "requiredFlag", Required: true}, }, expectedAnError: true, expectedErrorContents: []string{"requiredFlag"}, }, { testCase: "required_and_present", flags: []Flag{ StringFlag{Name: "requiredFlag", Required: true}, }, parseInput: []string{"--requiredFlag", "myinput"}, }, { testCase: "required_and_present_via_env_var", flags: []Flag{ StringFlag{Name: "requiredFlag", Required: true, EnvVar: "REQUIRED_FLAG"}, }, envVarInput: [2]string{"REQUIRED_FLAG", "true"}, }, { testCase: "required_and_optional", flags: []Flag{ StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optionalFlag"}, }, expectedAnError: true, }, { testCase: "required_and_optional_and_optional_present", flags: []Flag{ StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optionalFlag"}, }, parseInput: []string{"--optionalFlag", "myinput"}, expectedAnError: true, }, { testCase: "required_and_optional_and_optional_present_via_env_var", flags: []Flag{ StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optionalFlag", EnvVar: "OPTIONAL_FLAG"}, }, envVarInput: [2]string{"OPTIONAL_FLAG", "true"}, expectedAnError: true, }, { testCase: "required_and_optional_and_required_present", flags: []Flag{ StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "optionalFlag"}, }, parseInput: []string{"--requiredFlag", "myinput"}, }, { testCase: "two_required", flags: []Flag{ StringFlag{Name: "requiredFlagOne", Required: true}, StringFlag{Name: "requiredFlagTwo", Required: true}, }, expectedAnError: true, expectedErrorContents: []string{"requiredFlagOne", "requiredFlagTwo"}, }, { testCase: "two_required_and_one_present", flags: []Flag{ StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "requiredFlagTwo", Required: true}, }, parseInput: []string{"--requiredFlag", "myinput"}, expectedAnError: true, }, { testCase: "two_required_and_both_present", flags: []Flag{ StringFlag{Name: "requiredFlag", Required: true}, StringFlag{Name: "requiredFlagTwo", Required: true}, }, parseInput: []string{"--requiredFlag", "myinput", "--requiredFlagTwo", "myinput"}, }, { testCase: "required_flag_with_short_name", flags: []Flag{ StringSliceFlag{Name: "names, N", Required: true}, }, parseInput: []string{"-N", "asd", "-N", "qwe"}, }, { testCase: "required_flag_with_multiple_short_names", flags: []Flag{ StringSliceFlag{Name: "names, N, n", Required: true}, }, parseInput: []string{"-n", "asd", "-n", "qwe"}, }, } for _, test := range tdata { t.Run(test.testCase, func(t *testing.T) { // setup set := flag.NewFlagSet("test", 0) for _, flags := range test.flags { flags.Apply(set) } _ = set.Parse(test.parseInput) if test.envVarInput[0] != "" { os.Clearenv() _ = os.Setenv(test.envVarInput[0], test.envVarInput[1]) } ctx := &Context{} context := NewContext(ctx.App, set, ctx) context.Command.Flags = test.flags // logic under test err := checkRequiredFlags(test.flags, context) // assertions if test.expectedAnError && err == nil { t.Errorf("expected an error, but there was none") } if !test.expectedAnError && err != nil { t.Errorf("did not expected an error, but there was one: %s", err) } for _, errString := range test.expectedErrorContents { if !strings.Contains(err.Error(), errString) { t.Errorf("expected error %q to contain %q, but it didn't!", err.Error(), errString) } } }) } } cli-1.22.4/docs.go000066400000000000000000000062201364073550300136340ustar00rootroot00000000000000package cli import ( "bytes" "fmt" "io" "sort" "strings" "text/template" "github.com/cpuguy83/go-md2man/v2/md2man" ) // ToMarkdown creates a markdown string for the `*App` // The function errors if either parsing or writing of the string fails. func (a *App) ToMarkdown() (string, error) { var w bytes.Buffer if err := a.writeDocTemplate(&w); err != nil { return "", err } return w.String(), nil } // ToMan creates a man page string for the `*App` // The function errors if either parsing or writing of the string fails. func (a *App) ToMan() (string, error) { var w bytes.Buffer if err := a.writeDocTemplate(&w); err != nil { return "", err } man := md2man.Render(w.Bytes()) return string(man), nil } type cliTemplate struct { App *App Commands []string GlobalArgs []string SynopsisArgs []string } func (a *App) writeDocTemplate(w io.Writer) error { const name = "cli" t, err := template.New(name).Parse(MarkdownDocTemplate) if err != nil { return err } return t.ExecuteTemplate(w, name, &cliTemplate{ App: a, Commands: prepareCommands(a.Commands, 0), GlobalArgs: prepareArgsWithValues(a.Flags), SynopsisArgs: prepareArgsSynopsis(a.Flags), }) } func prepareCommands(commands []Command, level int) []string { coms := []string{} for i := range commands { command := &commands[i] if command.Hidden { continue } usage := "" if command.Usage != "" { usage = command.Usage } prepared := fmt.Sprintf("%s %s\n\n%s\n", strings.Repeat("#", level+2), strings.Join(command.Names(), ", "), usage, ) flags := prepareArgsWithValues(command.Flags) if len(flags) > 0 { prepared += fmt.Sprintf("\n%s", strings.Join(flags, "\n")) } coms = append(coms, prepared) // recursevly iterate subcommands if len(command.Subcommands) > 0 { coms = append( coms, prepareCommands(command.Subcommands, level+1)..., ) } } return coms } func prepareArgsWithValues(flags []Flag) []string { return prepareFlags(flags, ", ", "**", "**", `""`, true) } func prepareArgsSynopsis(flags []Flag) []string { return prepareFlags(flags, "|", "[", "]", "[value]", false) } func prepareFlags( flags []Flag, sep, opener, closer, value string, addDetails bool, ) []string { args := []string{} for _, f := range flags { flag, ok := f.(DocGenerationFlag) if !ok { continue } modifiedArg := opener for _, s := range strings.Split(flag.GetName(), ",") { trimmed := strings.TrimSpace(s) if len(modifiedArg) > len(opener) { modifiedArg += sep } if len(trimmed) > 1 { modifiedArg += fmt.Sprintf("--%s", trimmed) } else { modifiedArg += fmt.Sprintf("-%s", trimmed) } } modifiedArg += closer if flag.TakesValue() { modifiedArg += fmt.Sprintf("=%s", value) } if addDetails { modifiedArg += flagDetails(flag) } args = append(args, modifiedArg+"\n") } sort.Strings(args) return args } // flagDetails returns a string containing the flags metadata func flagDetails(flag DocGenerationFlag) string { description := flag.GetUsage() value := flag.GetValue() if value != "" { description += " (default: " + value + ")" } return ": " + description } cli-1.22.4/docs/000077500000000000000000000000001364073550300133055ustar00rootroot00000000000000cli-1.22.4/docs/CONTRIBUTING.md000066400000000000000000000015431364073550300155410ustar00rootroot00000000000000## Contributing Use @urfave/cli to ping the maintainers. Feel free to put up a pull request to fix a bug or maybe add a feature. We will give it a code review and make sure that it does not break backwards compatibility. If collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch. If you have contributed something significant to the project, we will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you feel like you have contributed to the project but have not yet been added as a collaborator, we probably forgot to add you :sweat_smile:. Please open an issue! cli-1.22.4/docs/v1/000077500000000000000000000000001364073550300136335ustar00rootroot00000000000000cli-1.22.4/docs/v1/CHANGELOG.md000066400000000000000000000527731364073550300154620ustar00rootroot00000000000000# Change Log **ATTN**: This project uses [semantic versioning](http://semver.org/). ## [Unreleased] ## [1.22.1] - 2019-09-11 ### Fixed * Hide output of hidden commands on man pages in [urfave/cli/pull/889](https://github.com/urfave/cli/pull/889) via [@crosbymichael](https://github.com/crosbymichael) * Don't generate fish completion for hidden commands [urfave/cli/pull/891](https://github.com/urfave/891) via [@saschagrunert](https://github.com/saschagrunert) * Using short flag names for required flags throws an error in [urfave/cli/pull/890](https://github.com/urfave/cli/pull/890) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) ### Changed * Remove flag code generation logic, legacy python test runner in [urfave/cli/pull/883](https://github.com/urfave/cli/pull/883) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) * Enable Go Modules support, drop support for `Go 1.10` add support for `Go 1.13` in [urfave/cli/pull/885](https://github.com/urfave/cli/pull/885) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) ## [1.22.0] - 2019-09-07 ### Fixed * Fix Subcommands not falling back to `app.ExitEventHandler` in [urfave/cli/pull/856](https://github.com/urfave/cli/pull/856) via [@FaranIdo](https://github.com/FaranIdo) ### Changed * Clarify that altsrc supports both TOML and JSON in [urfave/cli/pull/774](https://github.com/urfave/cli/pull/774) via [@whereswaldon](https://github.com/whereswaldon) * Made the exit code example more clear in [urfave/cli/pull/823](https://github.com/urfave/cli/pull/823) via [@xordspar0](https://github.com/xordspar0) * Removed the use of python for internal flag generation in [urfave/cli/pull/836](https://github.com/urfave/cli/pull/836) via [@asahasrabuddhe](https://github.com/asahasrabuddhe) * Changed the supported go versions to `1.10`, `1.11`, `1.12` in [urfave/cli/pull/843](https://github.com/urfave/cli/pull/843) via [@lafriks](https://github.com/lafriks) * Changed the v1 releases section in the readme in [urfave/cli/pull/862](https://github.com/urfave/cli/pull/862) via [@russoj88](https://github.com/russoj88) * Cleaned up go modules in [urfave/cli/pull/874](https://github.com/urfave/cli/pull/874) via [@saschagrunert](https://github.com/saschagrunert) ### Added * Added `UseShortOptionHandling` for combining short flags in [urfave/cli/pull/735](https://github.com/urfave/cli/pull/735) via [@rliebz](https://github.com/rliebz) * Added support for flags bash completion in [urfave/cli/pull/808](https://github.com/urfave/cli/pull/808) via [@yogeshlonkar](https://github.com/yogeshlonkar) * Added the `TakesFile` indicator to flag in [urfave/cli/pull/851](https://github.com/urfave/cli/pull/851) via [@saschagrunert](https://github.com/saschagrunert) * Added fish shell completion support in [urfave/cli/pull/848](https://github.com/urfave/cli/pull/848) via [@saschagrunert](https://github.com/saschagrunert) ## [1.21.0] - 2019-08-02 ### Fixed * Fix using "slice" flag types with `EnvVar` in [urfave/cli/pull/687](https://github.com/urfave/cli/pull/687) via [@joshuarubin](https://github.com/joshuarubin) * Fix regression of `SkipFlagParsing` behavior in [urfave/cli/pull/697](https://github.com/urfave/cli/pull/697) via [@jszwedko](https://github.com/jszwedko) * Fix handling `ShortOptions` and `SkipArgReorder` in [urfave/cli/pull/686](https://github.com/urfave/cli/pull/686) via [@baude](https://github.com/baude) * Fix args reordering when bool flags are present in [urfave/cli/pull/712](https://github.com/urfave/cli/pull/712) via [@windler](https://github.com/windler) * Fix parsing of short options in [urfave/cli/pull/758](https://github.com/urfave/cli/pull/758) via [@vrothberg](https://github.com/vrothberg) * Fix unaligned indents for the command help messages in [urfave/cli/pull/806](https://github.com/urfave/cli/pull/806) via [@mingrammer](https://github.com/mingrammer) ### Changed * Cleaned up help output in [urfave/cli/pull/664](https://github.com/urfave/cli/pull/664) via [@maguro](https://github.com/maguro) * Remove redundant nil checks in [urfave/cli/pull/773](https://github.com/urfave/cli/pull/773) via [@teresy](https://github.com/teresy) * Case is now considered when sorting strings in [urfave/cli/pull/676](https://github.com/urfave/cli/pull/676) via [@rliebz](https://github.com/rliebz) ### Added * Added _"required flags"_ support in [urfave/cli/pull/819](https://github.com/urfave/cli/pull/819) via [@lynncyrin](https://github.com/lynncyrin/) * Backport JSON `InputSource` to v1 in [urfave/cli/pull/598](https://github.com/urfave/cli/pull/598) via [@jszwedko](https://github.com/jszwedko) * Allow more customization of flag help strings in [urfave/cli/pull/661](https://github.com/urfave/cli/pull/661) via [@rliebz](https://github.com/rliebz) * Allow custom `ExitError` handler function in [urfave/cli/pull/628](https://github.com/urfave/cli/pull/628) via [@phinnaeus](https://github.com/phinnaeus) * Allow loading a variable from a file in [urfave/cli/pull/675](https://github.com/urfave/cli/pull/675) via [@jmccann](https://github.com/jmccann) * Allow combining short bool names in [urfave/cli/pull/684](https://github.com/urfave/cli/pull/684) via [@baude](https://github.com/baude) * Added test coverage to context in [urfave/cli/pull/788](https://github.com/urfave/cli/pull/788) via [@benzvan](https://github.com/benzvan) * Added go module support in [urfave/cli/pull/831](https://github.com/urfave/cli/pull/831) via [@saschagrunert](https://github.com/saschagrunert) ## [1.20.0] - 2017-08-10 ### Fixed * `HandleExitCoder` is now correctly iterates over all errors in a `MultiError`. The exit code is the exit code of the last error or `1` if there are no `ExitCoder`s in the `MultiError`. * Fixed YAML file loading on Windows (previously would fail validate the file path) * Subcommand `Usage`, `Description`, `ArgsUsage`, `OnUsageError` correctly propogated * `ErrWriter` is now passed downwards through command structure to avoid the need to redefine it * Pass `Command` context into `OnUsageError` rather than parent context so that all fields are avaiable * Errors occuring in `Before` funcs are no longer double printed * Use `UsageText` in the help templates for commands and subcommands if defined; otherwise build the usage as before (was previously ignoring this field) * `IsSet` and `GlobalIsSet` now correctly return whether a flag is set if a program calls `Set` or `GlobalSet` directly after flag parsing (would previously only return `true` if the flag was set during parsing) ### Changed * No longer exit the program on command/subcommand error if the error raised is not an `OsExiter`. This exiting behavior was introduced in 1.19.0, but was determined to be a regression in functionality. See [the PR](https://github.com/urfave/cli/pull/595) for discussion. ### Added * `CommandsByName` type was added to make it easy to sort `Command`s by name, alphabetically * `altsrc` now handles loading of string and int arrays from TOML * Support for definition of custom help templates for `App` via `CustomAppHelpTemplate` * Support for arbitrary key/value fields on `App` to be used with `CustomAppHelpTemplate` via `ExtraInfo` * `HelpFlag`, `VersionFlag`, and `BashCompletionFlag` changed to explictly be `cli.Flag`s allowing for the use of custom flags satisfying the `cli.Flag` interface to be used. ## [1.19.1] - 2016-11-21 ### Fixed - Fixes regression introduced in 1.19.0 where using an `ActionFunc` as the `Action` for a command would cause it to error rather than calling the function. Should not have a affected declarative cases using `func(c *cli.Context) err)`. - Shell completion now handles the case where the user specifies `--generate-bash-completion` immediately after a flag that takes an argument. Previously it call the application with `--generate-bash-completion` as the flag value. ## [1.19.0] - 2016-11-19 ### Added - `FlagsByName` was added to make it easy to sort flags (e.g. `sort.Sort(cli.FlagsByName(app.Flags))`) - A `Description` field was added to `App` for a more detailed description of the application (similar to the existing `Description` field on `Command`) - Flag type code generation via `go generate` - Write to stderr and exit 1 if action returns non-nil error - Added support for TOML to the `altsrc` loader - `SkipArgReorder` was added to allow users to skip the argument reordering. This is useful if you want to consider all "flags" after an argument as arguments rather than flags (the default behavior of the stdlib `flag` library). This is backported functionality from the [removal of the flag reordering](https://github.com/urfave/cli/pull/398) in the unreleased version 2 - For formatted errors (those implementing `ErrorFormatter`), the errors will be formatted during output. Compatible with `pkg/errors`. ### Changed - Raise minimum tested/supported Go version to 1.2+ ### Fixed - Consider empty environment variables as set (previously environment variables with the equivalent of `""` would be skipped rather than their value used). - Return an error if the value in a given environment variable cannot be parsed as the flag type. Previously these errors were silently swallowed. - Print full error when an invalid flag is specified (which includes the invalid flag) - `App.Writer` defaults to `stdout` when `nil` - If no action is specified on a command or app, the help is now printed instead of `panic`ing - `App.Metadata` is initialized automatically now (previously was `nil` unless initialized) - Correctly show help message if `-h` is provided to a subcommand - `context.(Global)IsSet` now respects environment variables. Previously it would return `false` if a flag was specified in the environment rather than as an argument - Removed deprecation warnings to STDERR to avoid them leaking to the end-user - `altsrc`s import paths were updated to use `gopkg.in/urfave/cli.v1`. This fixes issues that occurred when `gopkg.in/urfave/cli.v1` was imported as well as `altsrc` where Go would complain that the types didn't match ## [1.18.1] - 2016-08-28 ### Fixed - Removed deprecation warnings to STDERR to avoid them leaking to the end-user (backported) ## [1.18.0] - 2016-06-27 ### Added - `./runtests` test runner with coverage tracking by default - testing on OS X - testing on Windows - `UintFlag`, `Uint64Flag`, and `Int64Flag` types and supporting code ### Changed - Use spaces for alignment in help/usage output instead of tabs, making the output alignment consistent regardless of tab width ### Fixed - Printing of command aliases in help text - Printing of visible flags for both struct and struct pointer flags - Display the `help` subcommand when using `CommandCategories` - No longer swallows `panic`s that occur within the `Action`s themselves when detecting the signature of the `Action` field ## [1.17.1] - 2016-08-28 ### Fixed - Removed deprecation warnings to STDERR to avoid them leaking to the end-user ## [1.17.0] - 2016-05-09 ### Added - Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc` - `context.GlobalBoolT` was added as an analogue to `context.GlobalBool` - Support for hiding commands by setting `Hidden: true` -- this will hide the commands in help output ### Changed - `Float64Flag`, `IntFlag`, and `DurationFlag` default values are no longer quoted in help text output. - All flag types now include `(default: {value})` strings following usage when a default value can be (reasonably) detected. - `IntSliceFlag` and `StringSliceFlag` usage strings are now more consistent with non-slice flag types - Apps now exit with a code of 3 if an unknown subcommand is specified (previously they printed "No help topic for...", but still exited 0. This makes it easier to script around apps built using `cli` since they can trust that a 0 exit code indicated a successful execution. - cleanups based on [Go Report Card feedback](https://goreportcard.com/report/github.com/urfave/cli) ## [1.16.1] - 2016-08-28 ### Fixed - Removed deprecation warnings to STDERR to avoid them leaking to the end-user ## [1.16.0] - 2016-05-02 ### Added - `Hidden` field on all flag struct types to omit from generated help text ### Changed - `BashCompletionFlag` (`--enable-bash-completion`) is now omitted from generated help text via the `Hidden` field ### Fixed - handling of error values in `HandleAction` and `HandleExitCoder` ## [1.15.0] - 2016-04-30 ### Added - This file! - Support for placeholders in flag usage strings - `App.Metadata` map for arbitrary data/state management - `Set` and `GlobalSet` methods on `*cli.Context` for altering values after parsing. - Support for nested lookup of dot-delimited keys in structures loaded from YAML. ### Changed - The `App.Action` and `Command.Action` now prefer a return signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. If a non-nil `error` is returned, there may be two outcomes: - If the error fulfills `cli.ExitCoder`, then `os.Exit` will be called automatically - Else the error is bubbled up and returned from `App.Run` - Specifying an `Action` with the legacy return signature of `func(*cli.Context)` will produce a deprecation message to stderr - Specifying an `Action` that is not a `func` type will produce a non-zero exit from `App.Run` - Specifying an `Action` func that has an invalid (input) signature will produce a non-zero exit from `App.Run` ### Deprecated - `cli.App.RunAndExitOnError`, which should now be done by returning an error that fulfills `cli.ExitCoder` to `cli.App.Run`. - the legacy signature for `cli.App.Action` of `func(*cli.Context)`, which should now have a return signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. ### Fixed - Added missing `*cli.Context.GlobalFloat64` method ## [1.14.0] - 2016-04-03 (backfilled 2016-04-25) ### Added - Codebeat badge - Support for categorization via `CategorizedHelp` and `Categories` on app. ### Changed - Use `filepath.Base` instead of `path.Base` in `Name` and `HelpName`. ### Fixed - Ensure version is not shown in help text when `HideVersion` set. ## [1.13.0] - 2016-03-06 (backfilled 2016-04-25) ### Added - YAML file input support. - `NArg` method on context. ## [1.12.0] - 2016-02-17 (backfilled 2016-04-25) ### Added - Custom usage error handling. - Custom text support in `USAGE` section of help output. - Improved help messages for empty strings. - AppVeyor CI configuration. ### Changed - Removed `panic` from default help printer func. - De-duping and optimizations. ### Fixed - Correctly handle `Before`/`After` at command level when no subcommands. - Case of literal `-` argument causing flag reordering. - Environment variable hints on Windows. - Docs updates. ## [1.11.1] - 2015-12-21 (backfilled 2016-04-25) ### Changed - Use `path.Base` in `Name` and `HelpName` - Export `GetName` on flag types. ### Fixed - Flag parsing when skipping is enabled. - Test output cleanup. - Move completion check to account for empty input case. ## [1.11.0] - 2015-11-15 (backfilled 2016-04-25) ### Added - Destination scan support for flags. - Testing against `tip` in Travis CI config. ### Changed - Go version in Travis CI config. ### Fixed - Removed redundant tests. - Use correct example naming in tests. ## [1.10.2] - 2015-10-29 (backfilled 2016-04-25) ### Fixed - Remove unused var in bash completion. ## [1.10.1] - 2015-10-21 (backfilled 2016-04-25) ### Added - Coverage and reference logos in README. ### Fixed - Use specified values in help and version parsing. - Only display app version and help message once. ## [1.10.0] - 2015-10-06 (backfilled 2016-04-25) ### Added - More tests for existing functionality. - `ArgsUsage` at app and command level for help text flexibility. ### Fixed - Honor `HideHelp` and `HideVersion` in `App.Run`. - Remove juvenile word from README. ## [1.9.0] - 2015-09-08 (backfilled 2016-04-25) ### Added - `FullName` on command with accompanying help output update. - Set default `$PROG` in bash completion. ### Changed - Docs formatting. ### Fixed - Removed self-referential imports in tests. ## [1.8.0] - 2015-06-30 (backfilled 2016-04-25) ### Added - Support for `Copyright` at app level. - `Parent` func at context level to walk up context lineage. ### Fixed - Global flag processing at top level. ## [1.7.1] - 2015-06-11 (backfilled 2016-04-25) ### Added - Aggregate errors from `Before`/`After` funcs. - Doc comments on flag structs. - Include non-global flags when checking version and help. - Travis CI config updates. ### Fixed - Ensure slice type flags have non-nil values. - Collect global flags from the full command hierarchy. - Docs prose. ## [1.7.0] - 2015-05-03 (backfilled 2016-04-25) ### Changed - `HelpPrinter` signature includes output writer. ### Fixed - Specify go 1.1+ in docs. - Set `Writer` when running command as app. ## [1.6.0] - 2015-03-23 (backfilled 2016-04-25) ### Added - Multiple author support. - `NumFlags` at context level. - `Aliases` at command level. ### Deprecated - `ShortName` at command level. ### Fixed - Subcommand help output. - Backward compatible support for deprecated `Author` and `Email` fields. - Docs regarding `Names`/`Aliases`. ## [1.5.0] - 2015-02-20 (backfilled 2016-04-25) ### Added - `After` hook func support at app and command level. ### Fixed - Use parsed context when running command as subcommand. - Docs prose. ## [1.4.1] - 2015-01-09 (backfilled 2016-04-25) ### Added - Support for hiding `-h / --help` flags, but not `help` subcommand. - Stop flag parsing after `--`. ### Fixed - Help text for generic flags to specify single value. - Use double quotes in output for defaults. - Use `ParseInt` instead of `ParseUint` for int environment var values. - Use `0` as base when parsing int environment var values. ## [1.4.0] - 2014-12-12 (backfilled 2016-04-25) ### Added - Support for environment variable lookup "cascade". - Support for `Stdout` on app for output redirection. ### Fixed - Print command help instead of app help in `ShowCommandHelp`. ## [1.3.1] - 2014-11-13 (backfilled 2016-04-25) ### Added - Docs and example code updates. ### Changed - Default `-v / --version` flag made optional. ## [1.3.0] - 2014-08-10 (backfilled 2016-04-25) ### Added - `FlagNames` at context level. - Exposed `VersionPrinter` var for more control over version output. - Zsh completion hook. - `AUTHOR` section in default app help template. - Contribution guidelines. - `DurationFlag` type. ## [1.2.0] - 2014-08-02 ### Added - Support for environment variable defaults on flags plus tests. ## [1.1.0] - 2014-07-15 ### Added - Bash completion. - Optional hiding of built-in help command. - Optional skipping of flag parsing at command level. - `Author`, `Email`, and `Compiled` metadata on app. - `Before` hook func support at app and command level. - `CommandNotFound` func support at app level. - Command reference available on context. - `GenericFlag` type. - `Float64Flag` type. - `BoolTFlag` type. - `IsSet` flag helper on context. - More flag lookup funcs at context level. - More tests & docs. ### Changed - Help template updates to account for presence/absence of flags. - Separated subcommand help template. - Exposed `HelpPrinter` var for more control over help output. ## [1.0.0] - 2013-11-01 ### Added - `help` flag in default app flag set and each command flag set. - Custom handling of argument parsing errors. - Command lookup by name at app level. - `StringSliceFlag` type and supporting `StringSlice` type. - `IntSliceFlag` type and supporting `IntSlice` type. - Slice type flag lookups by name at context level. - Export of app and command help functions. - More tests & docs. ## 0.1.0 - 2013-07-22 ### Added - Initial implementation. [Unreleased]: https://github.com/urfave/cli/compare/v1.22.1...HEAD [1.22.1]: https://github.com/urfave/cli/compare/v1.22.0...v1.22.1 [1.22.0]: https://github.com/urfave/cli/compare/v1.21.0...v1.22.0 [1.21.0]: https://github.com/urfave/cli/compare/v1.20.0...v1.21.0 [1.20.0]: https://github.com/urfave/cli/compare/v1.19.1...v1.20.0 [1.19.1]: https://github.com/urfave/cli/compare/v1.19.0...v1.19.1 [1.19.0]: https://github.com/urfave/cli/compare/v1.18.0...v1.19.0 [1.18.0]: https://github.com/urfave/cli/compare/v1.17.0...v1.18.0 [1.17.0]: https://github.com/urfave/cli/compare/v1.16.0...v1.17.0 [1.16.0]: https://github.com/urfave/cli/compare/v1.15.0...v1.16.0 [1.15.0]: https://github.com/urfave/cli/compare/v1.14.0...v1.15.0 [1.14.0]: https://github.com/urfave/cli/compare/v1.13.0...v1.14.0 [1.13.0]: https://github.com/urfave/cli/compare/v1.12.0...v1.13.0 [1.12.0]: https://github.com/urfave/cli/compare/v1.11.1...v1.12.0 [1.11.1]: https://github.com/urfave/cli/compare/v1.11.0...v1.11.1 [1.11.0]: https://github.com/urfave/cli/compare/v1.10.2...v1.11.0 [1.10.2]: https://github.com/urfave/cli/compare/v1.10.1...v1.10.2 [1.10.1]: https://github.com/urfave/cli/compare/v1.10.0...v1.10.1 [1.10.0]: https://github.com/urfave/cli/compare/v1.9.0...v1.10.0 [1.9.0]: https://github.com/urfave/cli/compare/v1.8.0...v1.9.0 [1.8.0]: https://github.com/urfave/cli/compare/v1.7.1...v1.8.0 [1.7.1]: https://github.com/urfave/cli/compare/v1.7.0...v1.7.1 [1.7.0]: https://github.com/urfave/cli/compare/v1.6.0...v1.7.0 [1.6.0]: https://github.com/urfave/cli/compare/v1.5.0...v1.6.0 [1.5.0]: https://github.com/urfave/cli/compare/v1.4.1...v1.5.0 [1.4.1]: https://github.com/urfave/cli/compare/v1.4.0...v1.4.1 [1.4.0]: https://github.com/urfave/cli/compare/v1.3.1...v1.4.0 [1.3.1]: https://github.com/urfave/cli/compare/v1.3.0...v1.3.1 [1.3.0]: https://github.com/urfave/cli/compare/v1.2.0...v1.3.0 [1.2.0]: https://github.com/urfave/cli/compare/v1.1.0...v1.2.0 [1.1.0]: https://github.com/urfave/cli/compare/v1.0.0...v1.1.0 [1.0.0]: https://github.com/urfave/cli/compare/v0.1.0...v1.0.0 cli-1.22.4/docs/v1/manual.md000066400000000000000000000757221364073550300154470ustar00rootroot00000000000000cli v1 manual === - [Getting Started](#getting-started) - [Examples](#examples) * [Arguments](#arguments) * [Flags](#flags) + [Placeholder Values](#placeholder-values) + [Alternate Names](#alternate-names) + [Ordering](#ordering) + [Values from the Environment](#values-from-the-environment) + [Values from files](#values-from-files) + [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others) + [Precedence](#precedence) * [Subcommands](#subcommands) * [Subcommands categories](#subcommands-categories) * [Exit code](#exit-code) * [Combining short options](#combining-short-options) * [Bash Completion](#bash-completion) + [Enabling](#enabling) + [Distribution](#distribution) + [Customization](#customization) * [Generated Help Text](#generated-help-text) + [Customization](#customization-1) * [Version Flag](#version-flag) + [Customization](#customization-2) + [Full API Example](#full-api-example) ## Getting Started One of the philosophies behind cli is that an API should be playful and full of discovery. So a cli app can be as little as one line of code in `main()`. ``` go package main import ( "log" "os" "github.com/urfave/cli" ) func main() { err := cli.NewApp().Run(os.Args) if err != nil { log.Fatal(err) } } ``` This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation: ``` go package main import ( "fmt" "log" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Name = "boom" app.Usage = "make an explosive entrance" app.Action = func(c *cli.Context) error { fmt.Println("boom! I say!") return nil } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below. ## Examples Being a programmer can be a lonely job. Thankfully by the power of automation that is not the case! Let's create a greeter app to fend off our demons of loneliness! Start by creating a directory named `greet`, and within it, add a file, `greet.go` with the following code in it: ``` go package main import ( "fmt" "log" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Name = "greet" app.Usage = "fight the loneliness!" app.Action = func(c *cli.Context) error { fmt.Println("Hello friend!") return nil } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` Install our command to the `$GOPATH/bin` directory: ``` $ go install ``` Finally run our new command: ``` $ greet Hello friend! ``` cli also generates neat help text: ``` $ greet help NAME: greet - fight the loneliness! USAGE: main [global options] command [command options] [arguments...] COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --help, -h show help ``` ### Arguments You can lookup arguments by calling the `Args` function on `cli.Context`, e.g.: ``` go package main import ( "fmt" "log" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Action = func(c *cli.Context) error { fmt.Printf("Hello %q", c.Args().Get(0)) return nil } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` ### Flags Setting and querying flags is simple. ``` go package main import ( "fmt" "log" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Flags = []cli.Flag { cli.StringFlag{ Name: "lang", Value: "english", Usage: "language for the greeting", }, } app.Action = func(c *cli.Context) error { name := "Nefertiti" if c.NArg() > 0 { name = c.Args().Get(0) } if c.String("lang") == "spanish" { fmt.Println("Hola", name) } else { fmt.Println("Hello", name) } return nil } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` You can also set a destination variable for a flag, to which the content will be scanned. ``` go package main import ( "log" "os" "fmt" "github.com/urfave/cli" ) func main() { var language string app := cli.NewApp() app.Flags = []cli.Flag { cli.StringFlag{ Name: "lang", Value: "english", Usage: "language for the greeting", Destination: &language, }, } app.Action = func(c *cli.Context) error { name := "someone" if c.NArg() > 0 { name = c.Args()[0] } if language == "spanish" { fmt.Println("Hola", name) } else { fmt.Println("Hello", name) } return nil } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` See full list of flags at http://godoc.org/github.com/urfave/cli #### Placeholder Values Sometimes it's useful to specify a flag's value within the usage string itself. Such placeholders are indicated with back quotes. For example this: ```go package main import ( "log" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Flags = []cli.Flag{ cli.StringFlag{ Name: "config, c", Usage: "Load configuration from `FILE`", }, } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` Will result in help output like: ``` --config FILE, -c FILE Load configuration from FILE ``` Note that only the first placeholder is used. Subsequent back-quoted words will be left as-is. #### Alternate Names You can set alternate (or short) names for flags by providing a comma-delimited list for the `Name`. e.g. ``` go package main import ( "log" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Flags = []cli.Flag { cli.StringFlag{ Name: "lang, l", Value: "english", Usage: "language for the greeting", }, } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error. #### Ordering Flags for the application and commands are shown in the order they are defined. However, it's possible to sort them from outside this library by using `FlagsByName` or `CommandsByName` with `sort`. For example this: ``` go package main import ( "log" "os" "sort" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Flags = []cli.Flag { cli.StringFlag{ Name: "lang, l", Value: "english", Usage: "Language for the greeting", }, cli.StringFlag{ Name: "config, c", Usage: "Load configuration from `FILE`", }, } app.Commands = []cli.Command{ { Name: "complete", Aliases: []string{"c"}, Usage: "complete a task on the list", Action: func(c *cli.Context) error { return nil }, }, { Name: "add", Aliases: []string{"a"}, Usage: "add a task to the list", Action: func(c *cli.Context) error { return nil }, }, } sort.Sort(cli.FlagsByName(app.Flags)) sort.Sort(cli.CommandsByName(app.Commands)) err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` Will result in help output like: ``` --config FILE, -c FILE Load configuration from FILE --lang value, -l value Language for the greeting (default: "english") ``` #### Values from the Environment You can also have the default value set from the environment via `EnvVar`. e.g. ``` go package main import ( "log" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Flags = []cli.Flag { cli.StringFlag{ Name: "lang, l", Value: "english", Usage: "language for the greeting", EnvVar: "APP_LANG", }, } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` The `EnvVar` may also be given as a comma-delimited "cascade", where the first environment variable that resolves is used as the default. ``` go package main import ( "log" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Flags = []cli.Flag { cli.StringFlag{ Name: "lang, l", Value: "english", Usage: "language for the greeting", EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG", }, } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` #### Values from files You can also have the default value set from file via `FilePath`. e.g. ``` go package main import ( "log" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Flags = []cli.Flag { cli.StringFlag{ Name: "password, p", Usage: "password for the mysql database", FilePath: "/etc/mysql/password", }, } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` Note that default values set from file (e.g. `FilePath`) take precedence over default values set from the environment (e.g. `EnvVar`). #### Values from alternate input sources (YAML, TOML, and others) There is a separate package altsrc that adds support for getting flag values from other file input sources. Currently supported input source formats: * YAML * JSON * TOML In order to get values for a flag from an alternate input source the following code would be added to wrap an existing cli.Flag like below: ``` go altsrc.NewIntFlag(cli.IntFlag{Name: "test"}) ``` Initialization must also occur for these flags. Below is an example initializing getting data from a yaml file below. ``` go command.Before = altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) ``` The code above will use the "load" string as a flag name to get the file name of a yaml file from the cli.Context. It will then use that file name to initialize the yaml input source for any flags that are defined on that command. As a note the "load" flag used would also have to be defined on the command flags in order for this code snippet to work. Currently only YAML, JSON, and TOML files are supported but developers can add support for other input sources by implementing the altsrc.InputSourceContext for their given sources. Here is a more complete sample of a command using YAML support: ``` go package notmain import ( "fmt" "log" "os" "github.com/urfave/cli" "github.com/urfave/cli/altsrc" ) func main() { app := cli.NewApp() flags := []cli.Flag{ altsrc.NewIntFlag(cli.IntFlag{Name: "test"}), cli.StringFlag{Name: "load"}, } app.Action = func(c *cli.Context) error { fmt.Println("yaml ist rad") return nil } app.Before = altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load")) app.Flags = flags err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` #### Precedence The precedence for flag value sources is as follows (highest to lowest): 0. Command line flag value from user 0. Environment variable (if specified) 0. Configuration file (if specified) 0. Default defined on the flag ### Subcommands Subcommands can be defined for a more git-like command line app. ```go package main import ( "fmt" "log" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Commands = []cli.Command{ { Name: "add", Aliases: []string{"a"}, Usage: "add a task to the list", Action: func(c *cli.Context) error { fmt.Println("added task: ", c.Args().First()) return nil }, }, { Name: "complete", Aliases: []string{"c"}, Usage: "complete a task on the list", Action: func(c *cli.Context) error { fmt.Println("completed task: ", c.Args().First()) return nil }, }, { Name: "template", Aliases: []string{"t"}, Usage: "options for task templates", Subcommands: []cli.Command{ { Name: "add", Usage: "add a new template", Action: func(c *cli.Context) error { fmt.Println("new task template: ", c.Args().First()) return nil }, }, { Name: "remove", Usage: "remove an existing template", Action: func(c *cli.Context) error { fmt.Println("removed task template: ", c.Args().First()) return nil }, }, }, }, } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` ### Subcommands categories For additional organization in apps that have many subcommands, you can associate a category for each command to group them together in the help output. E.g. ```go package main import ( "log" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Commands = []cli.Command{ { Name: "noop", }, { Name: "add", Category: "Template actions", }, { Name: "remove", Category: "Template actions", }, } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` Will include: ``` COMMANDS: noop Template actions: add remove ``` ### Exit code Calling `App.Run` will not automatically call `os.Exit`, which means that by default the exit code will "fall through" to being `0`. An explicit exit code may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a `cli.MultiError` that includes an error that fulfills `cli.ExitCoder`, e.g.: ``` go package main import ( "log" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.Flags = []cli.Flag{ cli.BoolFlag{ Name: "ginger-crouton", Usage: "Add ginger croutons to the soup", }, } app.Action = func(ctx *cli.Context) error { if !ctx.Bool("ginger-crouton") { return cli.NewExitError("Ginger croutons are not in the soup", 86) } return nil } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` ### Combining short options Traditional use of options using their shortnames look like this: ``` $ cmd -s -o -m "Some message" ``` Suppose you want users to be able to combine options with their shortnames. This can be done using the `UseShortOptionHandling` bool in your app configuration, or for individual commands by attaching it to the command configuration. For example: ``` go package main import ( "fmt" "log" "os" "github.com/urfave/cli" ) func main() { app := cli.NewApp() app.UseShortOptionHandling = true app.Commands = []cli.Command{ { Name: "short", Usage: "complete a task on the list", Flags: []cli.Flag{ cli.BoolFlag{Name: "serve, s"}, cli.BoolFlag{Name: "option, o"}, cli.StringFlag{Name: "message, m"}, }, Action: func(c *cli.Context) error { fmt.Println("serve:", c.Bool("serve")) fmt.Println("option:", c.Bool("option")) fmt.Println("message:", c.String("message")) return nil }, }, } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` If your program has any number of bool flags such as `serve` and `option`, and optionally one non-bool flag `message`, with the short options of `-s`, `-o`, and `-m` respectively, setting `UseShortOptionHandling` will also support the following syntax: ``` $ cmd -som "Some message" ``` If you enable `UseShortOptionHandling`, then you must not use any flags that have a single leading `-` or this will result in failures. For example, `-option` can no longer be used. Flags with two leading dashes (such as `--options`) are still valid. ### Bash Completion You can enable completion commands by setting the `EnableBashCompletion` flag on the `App` object. By default, this setting will only auto-complete to show an app's subcommands, but you can write your own completion methods for the App or its subcommands. ``` go package main import ( "fmt" "log" "os" "github.com/urfave/cli" ) func main() { tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"} app := cli.NewApp() app.EnableBashCompletion = true app.Commands = []cli.Command{ { Name: "complete", Aliases: []string{"c"}, Usage: "complete a task on the list", Action: func(c *cli.Context) error { fmt.Println("completed task: ", c.Args().First()) return nil }, BashComplete: func(c *cli.Context) { // This will complete if no args are passed if c.NArg() > 0 { return } for _, t := range tasks { fmt.Println(t) } }, }, } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` #### Enabling Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while setting the `PROG` variable to the name of your program: `PROG=myprogram source /.../cli/autocomplete/bash_autocomplete` #### Distribution Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename it to the name of the program you wish to add autocomplete support for (or automatically install it there if you are distributing a package). Don't forget to source the file to make it active in the current shell. ``` sudo cp src/bash_autocomplete /etc/bash_completion.d/ source /etc/bash_completion.d/ ``` Alternatively, you can just document that users should source the generic `autocomplete/bash_autocomplete` in their bash configuration with `$PROG` set to the name of their program (as above). #### Customization The default bash completion flag (`--generate-bash-completion`) is defined as `cli.BashCompletionFlag`, and may be redefined if desired, e.g.: ``` go package main import ( "log" "os" "github.com/urfave/cli" ) func main() { cli.BashCompletionFlag = cli.BoolFlag{ Name: "compgen", Hidden: true, } app := cli.NewApp() app.EnableBashCompletion = true app.Commands = []cli.Command{ { Name: "wat", }, } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` ### Generated Help Text The default help flag (`-h/--help`) is defined as `cli.HelpFlag` and is checked by the cli internals in order to print generated help text for the app, command, or subcommand, and break execution. #### Customization All of the help text generation may be customized, and at multiple levels. The templates are exposed as variables `AppHelpTemplate`, `CommandHelpTemplate`, and `SubcommandHelpTemplate` which may be reassigned or augmented, and full override is possible by assigning a compatible func to the `cli.HelpPrinter` variable, e.g.: ``` go package main import ( "fmt" "log" "io" "os" "github.com/urfave/cli" ) func main() { // EXAMPLE: Append to an existing template cli.AppHelpTemplate = fmt.Sprintf(`%s WEBSITE: http://awesometown.example.com SUPPORT: support@awesometown.example.com `, cli.AppHelpTemplate) // EXAMPLE: Override a template cli.AppHelpTemplate = `NAME: {{.Name}} - {{.Usage}} USAGE: {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} {{if len .Authors}} AUTHOR: {{range .Authors}}{{ . }}{{end}} {{end}}{{if .Commands}} COMMANDS: {{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}} GLOBAL OPTIONS: {{range .VisibleFlags}}{{.}} {{end}}{{end}}{{if .Copyright }} COPYRIGHT: {{.Copyright}} {{end}}{{if .Version}} VERSION: {{.Version}} {{end}} ` // EXAMPLE: Replace the `HelpPrinter` func cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) { fmt.Println("Ha HA. I pwnd the help!!1") } err := cli.NewApp().Run(os.Args) if err != nil { log.Fatal(err) } } ``` The default flag may be customized to something other than `-h/--help` by setting `cli.HelpFlag`, e.g.: ``` go package main import ( "log" "os" "github.com/urfave/cli" ) func main() { cli.HelpFlag = cli.BoolFlag{ Name: "halp, haaaaalp", Usage: "HALP", EnvVar: "SHOW_HALP,HALPPLZ", } err := cli.NewApp().Run(os.Args) if err != nil { log.Fatal(err) } } ``` ### Version Flag The default version flag (`-v/--version`) is defined as `cli.VersionFlag`, which is checked by the cli internals in order to print the `App.Version` via `cli.VersionPrinter` and break execution. #### Customization The default flag may be customized to something other than `-v/--version` by setting `cli.VersionFlag`, e.g.: ``` go package main import ( "log" "os" "github.com/urfave/cli" ) func main() { cli.VersionFlag = cli.BoolFlag{ Name: "print-version, V", Usage: "print only the version", } app := cli.NewApp() app.Name = "partay" app.Version = "19.99.0" err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` Alternatively, the version printer at `cli.VersionPrinter` may be overridden, e.g.: ``` go package main import ( "fmt" "log" "os" "github.com/urfave/cli" ) var ( Revision = "fafafaf" ) func main() { cli.VersionPrinter = func(c *cli.Context) { fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision) } app := cli.NewApp() app.Name = "partay" app.Version = "19.99.0" err := app.Run(os.Args) if err != nil { log.Fatal(err) } } ``` #### Full API Example **Notice**: This is a contrived (functioning) example meant strictly for API demonstration purposes. Use of one's imagination is encouraged. ``` go package main import ( "errors" "flag" "fmt" "io" "io/ioutil" "os" "time" "github.com/urfave/cli" ) func init() { cli.AppHelpTemplate += "\nCUSTOMIZED: you bet ur muffins\n" cli.CommandHelpTemplate += "\nYMMV\n" cli.SubcommandHelpTemplate += "\nor something\n" cli.HelpFlag = cli.BoolFlag{Name: "halp"} cli.BashCompletionFlag = cli.BoolFlag{Name: "compgen", Hidden: true} cli.VersionFlag = cli.BoolFlag{Name: "print-version, V"} cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) { fmt.Fprintf(w, "best of luck to you\n") } cli.VersionPrinter = func(c *cli.Context) { fmt.Fprintf(c.App.Writer, "version=%s\n", c.App.Version) } cli.OsExiter = func(c int) { fmt.Fprintf(cli.ErrWriter, "refusing to exit %d\n", c) } cli.ErrWriter = ioutil.Discard cli.FlagStringer = func(fl cli.Flag) string { return fmt.Sprintf("\t\t%s", fl.GetName()) } } type hexWriter struct{} func (w *hexWriter) Write(p []byte) (int, error) { for _, b := range p { fmt.Printf("%x", b) } fmt.Printf("\n") return len(p), nil } type genericType struct{ s string } func (g *genericType) Set(value string) error { g.s = value return nil } func (g *genericType) String() string { return g.s } func main() { app := cli.NewApp() app.Name = "kənˈtrīv" app.Version = "19.99.0" app.Compiled = time.Now() app.Authors = []cli.Author{ cli.Author{ Name: "Example Human", Email: "human@example.com", }, } app.Copyright = "(c) 1999 Serious Enterprise" app.HelpName = "contrive" app.Usage = "demonstrate available API" app.UsageText = "contrive - demonstrating the available API" app.ArgsUsage = "[args and such]" app.Commands = []cli.Command{ cli.Command{ Name: "doo", Aliases: []string{"do"}, Category: "motion", Usage: "do the doo", UsageText: "doo - does the dooing", Description: "no really, there is a lot of dooing to be done", ArgsUsage: "[arrgh]", Flags: []cli.Flag{ cli.BoolFlag{Name: "forever, forevvarr"}, }, Subcommands: cli.Commands{ cli.Command{ Name: "wop", Action: wopAction, }, }, SkipFlagParsing: false, HideHelp: false, Hidden: false, HelpName: "doo!", BashComplete: func(c *cli.Context) { fmt.Fprintf(c.App.Writer, "--better\n") }, Before: func(c *cli.Context) error { fmt.Fprintf(c.App.Writer, "brace for impact\n") return nil }, After: func(c *cli.Context) error { fmt.Fprintf(c.App.Writer, "did we lose anyone?\n") return nil }, Action: func(c *cli.Context) error { c.Command.FullName() c.Command.HasName("wop") c.Command.Names() c.Command.VisibleFlags() fmt.Fprintf(c.App.Writer, "dodododododoodododddooooododododooo\n") if c.Bool("forever") { c.Command.Run(c) } return nil }, OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error { fmt.Fprintf(c.App.Writer, "for shame\n") return err }, }, } app.Flags = []cli.Flag{ cli.BoolFlag{Name: "fancy"}, cli.BoolTFlag{Name: "fancier"}, cli.DurationFlag{Name: "howlong, H", Value: time.Second * 3}, cli.Float64Flag{Name: "howmuch"}, cli.GenericFlag{Name: "wat", Value: &genericType{}}, cli.Int64Flag{Name: "longdistance"}, cli.Int64SliceFlag{Name: "intervals"}, cli.IntFlag{Name: "distance"}, cli.IntSliceFlag{Name: "times"}, cli.StringFlag{Name: "dance-move, d"}, cli.StringSliceFlag{Name: "names, N"}, cli.UintFlag{Name: "age"}, cli.Uint64Flag{Name: "bigage"}, } app.EnableBashCompletion = true app.UseShortOptionHandling = true app.HideHelp = false app.HideVersion = false app.BashComplete = func(c *cli.Context) { fmt.Fprintf(c.App.Writer, "lipstick\nkiss\nme\nlipstick\nringo\n") } app.Before = func(c *cli.Context) error { fmt.Fprintf(c.App.Writer, "HEEEERE GOES\n") return nil } app.After = func(c *cli.Context) error { fmt.Fprintf(c.App.Writer, "Phew!\n") return nil } app.CommandNotFound = func(c *cli.Context, command string) { fmt.Fprintf(c.App.Writer, "Thar be no %q here.\n", command) } app.OnUsageError = func(c *cli.Context, err error, isSubcommand bool) error { if isSubcommand { return err } fmt.Fprintf(c.App.Writer, "WRONG: %#v\n", err) return nil } app.Action = func(c *cli.Context) error { cli.DefaultAppComplete(c) cli.HandleExitCoder(errors.New("not an exit coder, though")) cli.ShowAppHelp(c) cli.ShowCommandCompletions(c, "nope") cli.ShowCommandHelp(c, "also-nope") cli.ShowCompletions(c) cli.ShowSubcommandHelp(c) cli.ShowVersion(c) categories := c.App.Categories() categories.AddCommand("sounds", cli.Command{ Name: "bloop", }) for _, category := range c.App.Categories() { fmt.Fprintf(c.App.Writer, "%s\n", category.Name) fmt.Fprintf(c.App.Writer, "%#v\n", category.Commands) fmt.Fprintf(c.App.Writer, "%#v\n", category.VisibleCommands()) } fmt.Printf("%#v\n", c.App.Command("doo")) if c.Bool("infinite") { c.App.Run([]string{"app", "doo", "wop"}) } if c.Bool("forevar") { c.App.RunAsSubcommand(c) } c.App.Setup() fmt.Printf("%#v\n", c.App.VisibleCategories()) fmt.Printf("%#v\n", c.App.VisibleCommands()) fmt.Printf("%#v\n", c.App.VisibleFlags()) fmt.Printf("%#v\n", c.Args().First()) if len(c.Args()) > 0 { fmt.Printf("%#v\n", c.Args()[1]) } fmt.Printf("%#v\n", c.Args().Present()) fmt.Printf("%#v\n", c.Args().Tail()) set := flag.NewFlagSet("contrive", 0) nc := cli.NewContext(c.App, set, c) fmt.Printf("%#v\n", nc.Args()) fmt.Printf("%#v\n", nc.Bool("nope")) fmt.Printf("%#v\n", nc.BoolT("nerp")) fmt.Printf("%#v\n", nc.Duration("howlong")) fmt.Printf("%#v\n", nc.Float64("hay")) fmt.Printf("%#v\n", nc.Generic("bloop")) fmt.Printf("%#v\n", nc.Int64("bonk")) fmt.Printf("%#v\n", nc.Int64Slice("burnks")) fmt.Printf("%#v\n", nc.Int("bips")) fmt.Printf("%#v\n", nc.IntSlice("blups")) fmt.Printf("%#v\n", nc.String("snurt")) fmt.Printf("%#v\n", nc.StringSlice("snurkles")) fmt.Printf("%#v\n", nc.Uint("flub")) fmt.Printf("%#v\n", nc.Uint64("florb")) fmt.Printf("%#v\n", nc.GlobalBool("global-nope")) fmt.Printf("%#v\n", nc.GlobalBoolT("global-nerp")) fmt.Printf("%#v\n", nc.GlobalDuration("global-howlong")) fmt.Printf("%#v\n", nc.GlobalFloat64("global-hay")) fmt.Printf("%#v\n", nc.GlobalGeneric("global-bloop")) fmt.Printf("%#v\n", nc.GlobalInt("global-bips")) fmt.Printf("%#v\n", nc.GlobalIntSlice("global-blups")) fmt.Printf("%#v\n", nc.GlobalString("global-snurt")) fmt.Printf("%#v\n", nc.GlobalStringSlice("global-snurkles")) fmt.Printf("%#v\n", nc.FlagNames()) fmt.Printf("%#v\n", nc.GlobalFlagNames()) fmt.Printf("%#v\n", nc.GlobalIsSet("wat")) fmt.Printf("%#v\n", nc.GlobalSet("wat", "nope")) fmt.Printf("%#v\n", nc.NArg()) fmt.Printf("%#v\n", nc.NumFlags()) fmt.Printf("%#v\n", nc.Parent()) nc.Set("wat", "also-nope") ec := cli.NewExitError("ohwell", 86) fmt.Fprintf(c.App.Writer, "%d", ec.ExitCode()) fmt.Printf("made it!\n") return nil } if os.Getenv("HEXY") != "" { app.Writer = &hexWriter{} app.ErrWriter = &hexWriter{} } app.Metadata = map[string]interface{}{ "layers": "many", "explicable": false, "whatever-values": 19.99, } // ignore error so we don't exit non-zero and break gfmrun README example tests _ = app.Run(os.Args) } func wopAction(c *cli.Context) error { fmt.Fprintf(c.App.Writer, ":wave: over here, eh\n") return nil } ``` cli-1.22.4/docs/v2/000077500000000000000000000000001364073550300136345ustar00rootroot00000000000000cli-1.22.4/docs/v2/.keep000066400000000000000000000000001364073550300145470ustar00rootroot00000000000000cli-1.22.4/docs_test.go000066400000000000000000000044471364073550300147040ustar00rootroot00000000000000package cli import ( "io/ioutil" "testing" ) func testApp() *App { app := NewApp() app.Name = "greet" app.Flags = []Flag{ StringFlag{ Name: "socket, s", Usage: "some 'usage' text", Value: "value", TakesFile: true, }, StringFlag{Name: "flag, fl, f"}, BoolFlag{ Name: "another-flag, b", Usage: "another usage text", }, } app.Commands = []Command{{ Aliases: []string{"c"}, Flags: []Flag{ StringFlag{ Name: "flag, fl, f", TakesFile: true, }, BoolFlag{ Name: "another-flag, b", Usage: "another usage text", }, }, Name: "config", Usage: "another usage test", Subcommands: []Command{{ Aliases: []string{"s", "ss"}, Flags: []Flag{ StringFlag{Name: "sub-flag, sub-fl, s"}, BoolFlag{ Name: "sub-command-flag, s", Usage: "some usage text", }, }, Name: "sub-config", Usage: "another usage test", }}, }, { Aliases: []string{"i", "in"}, Name: "info", Usage: "retrieve generic information", }, { Name: "some-command", }, { Name: "hidden-command", Hidden: true, }} app.UsageText = "app [first_arg] [second_arg]" app.Usage = "Some app" app.Author = "Harrison" app.Email = "harrison@lolwut.com" app.Authors = []Author{{Name: "Oliver Allen", Email: "oliver@toyshop.com"}} return app } func expectFileContent(t *testing.T, file, expected string) { data, err := ioutil.ReadFile(file) expect(t, err, nil) expect(t, string(data), expected) } func TestToMarkdownFull(t *testing.T) { // Given app := testApp() // When res, err := app.ToMarkdown() // Then expect(t, err, nil) expectFileContent(t, "testdata/expected-doc-full.md", res) } func TestToMarkdownNoFlags(t *testing.T) { // Given app := testApp() app.Flags = nil // When res, err := app.ToMarkdown() // Then expect(t, err, nil) expectFileContent(t, "testdata/expected-doc-no-flags.md", res) } func TestToMarkdownNoCommands(t *testing.T) { // Given app := testApp() app.Commands = nil // When res, err := app.ToMarkdown() // Then expect(t, err, nil) expectFileContent(t, "testdata/expected-doc-no-commands.md", res) } func TestToMan(t *testing.T) { // Given app := testApp() // When res, err := app.ToMan() // Then expect(t, err, nil) expectFileContent(t, "testdata/expected-doc-full.man", res) } cli-1.22.4/errors.go000066400000000000000000000052311364073550300142210ustar00rootroot00000000000000package cli import ( "fmt" "io" "os" "strings" ) // OsExiter is the function used when the app exits. If not set defaults to os.Exit. var OsExiter = os.Exit // ErrWriter is used to write errors to the user. This can be anything // implementing the io.Writer interface and defaults to os.Stderr. var ErrWriter io.Writer = os.Stderr // MultiError is an error that wraps multiple errors. type MultiError struct { Errors []error } // NewMultiError creates a new MultiError. Pass in one or more errors. func NewMultiError(err ...error) MultiError { return MultiError{Errors: err} } // Error implements the error interface. func (m MultiError) Error() string { errs := make([]string, len(m.Errors)) for i, err := range m.Errors { errs[i] = err.Error() } return strings.Join(errs, "\n") } type ErrorFormatter interface { Format(s fmt.State, verb rune) } // ExitCoder is the interface checked by `App` and `Command` for a custom exit // code type ExitCoder interface { error ExitCode() int } // ExitError fulfills both the builtin `error` interface and `ExitCoder` type ExitError struct { exitCode int message interface{} } // NewExitError makes a new *ExitError func NewExitError(message interface{}, exitCode int) *ExitError { return &ExitError{ exitCode: exitCode, message: message, } } // Error returns the string message, fulfilling the interface required by // `error` func (ee *ExitError) Error() string { return fmt.Sprintf("%v", ee.message) } // ExitCode returns the exit code, fulfilling the interface required by // `ExitCoder` func (ee *ExitError) ExitCode() int { return ee.exitCode } // HandleExitCoder checks if the error fulfills the ExitCoder interface, and if // so prints the error to stderr (if it is non-empty) and calls OsExiter with the // given exit code. If the given error is a MultiError, then this func is // called on all members of the Errors slice and calls OsExiter with the last exit code. func HandleExitCoder(err error) { if err == nil { return } if exitErr, ok := err.(ExitCoder); ok { if err.Error() != "" { if _, ok := exitErr.(ErrorFormatter); ok { fmt.Fprintf(ErrWriter, "%+v\n", err) } else { fmt.Fprintln(ErrWriter, err) } } OsExiter(exitErr.ExitCode()) return } if multiErr, ok := err.(MultiError); ok { code := handleMultiError(multiErr) OsExiter(code) return } } func handleMultiError(multiErr MultiError) int { code := 1 for _, merr := range multiErr.Errors { if multiErr2, ok := merr.(MultiError); ok { code = handleMultiError(multiErr2) } else { fmt.Fprintln(ErrWriter, merr) if exitErr, ok := merr.(ExitCoder); ok { code = exitErr.ExitCode() } } } return code } cli-1.22.4/errors_test.go000066400000000000000000000044531364073550300152650ustar00rootroot00000000000000package cli import ( "bytes" "errors" "fmt" "testing" ) func TestHandleExitCoder_nil(t *testing.T) { exitCode := 0 called := false OsExiter = func(rc int) { if !called { exitCode = rc called = true } } defer func() { OsExiter = fakeOsExiter }() HandleExitCoder(nil) expect(t, exitCode, 0) expect(t, called, false) } func TestHandleExitCoder_ExitCoder(t *testing.T) { exitCode := 0 called := false OsExiter = func(rc int) { if !called { exitCode = rc called = true } } defer func() { OsExiter = fakeOsExiter }() HandleExitCoder(NewExitError("galactic perimeter breach", 9)) expect(t, exitCode, 9) expect(t, called, true) } func TestHandleExitCoder_MultiErrorWithExitCoder(t *testing.T) { exitCode := 0 called := false OsExiter = func(rc int) { if !called { exitCode = rc called = true } } defer func() { OsExiter = fakeOsExiter }() exitErr := NewExitError("galactic perimeter breach", 9) exitErr2 := NewExitError("last ExitCoder", 11) err := NewMultiError(errors.New("wowsa"), errors.New("egad"), exitErr, exitErr2) HandleExitCoder(err) expect(t, exitCode, 11) expect(t, called, true) } // make a stub to not import pkg/errors type ErrorWithFormat struct { error } func NewErrorWithFormat(m string) *ErrorWithFormat { return &ErrorWithFormat{error: errors.New(m)} } func (f *ErrorWithFormat) Format(s fmt.State, verb rune) { fmt.Fprintf(s, "This the format: %v", f.error) } func TestHandleExitCoder_ErrorWithFormat(t *testing.T) { called := false OsExiter = func(rc int) { if !called { called = true } } ErrWriter = &bytes.Buffer{} defer func() { OsExiter = fakeOsExiter ErrWriter = fakeErrWriter }() err := NewExitError(NewErrorWithFormat("I am formatted"), 1) HandleExitCoder(err) expect(t, called, true) expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: I am formatted\n") } func TestHandleExitCoder_MultiErrorWithFormat(t *testing.T) { called := false OsExiter = func(rc int) { if !called { called = true } } ErrWriter = &bytes.Buffer{} defer func() { OsExiter = fakeOsExiter }() err := NewMultiError(NewErrorWithFormat("err1"), NewErrorWithFormat("err2")) HandleExitCoder(err) expect(t, called, true) expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: err1\nThis the format: err2\n") } cli-1.22.4/fish.go000066400000000000000000000100651364073550300136370ustar00rootroot00000000000000package cli import ( "bytes" "fmt" "io" "strings" "text/template" ) // ToFishCompletion creates a fish completion string for the `*App` // The function errors if either parsing or writing of the string fails. func (a *App) ToFishCompletion() (string, error) { var w bytes.Buffer if err := a.writeFishCompletionTemplate(&w); err != nil { return "", err } return w.String(), nil } type fishCompletionTemplate struct { App *App Completions []string AllCommands []string } func (a *App) writeFishCompletionTemplate(w io.Writer) error { const name = "cli" t, err := template.New(name).Parse(FishCompletionTemplate) if err != nil { return err } allCommands := []string{} // Add global flags completions := a.prepareFishFlags(a.VisibleFlags(), allCommands) // Add help flag if !a.HideHelp { completions = append( completions, a.prepareFishFlags([]Flag{HelpFlag}, allCommands)..., ) } // Add version flag if !a.HideVersion { completions = append( completions, a.prepareFishFlags([]Flag{VersionFlag}, allCommands)..., ) } // Add commands and their flags completions = append( completions, a.prepareFishCommands(a.VisibleCommands(), &allCommands, []string{})..., ) return t.ExecuteTemplate(w, name, &fishCompletionTemplate{ App: a, Completions: completions, AllCommands: allCommands, }) } func (a *App) prepareFishCommands(commands []Command, allCommands *[]string, previousCommands []string) []string { completions := []string{} for i := range commands { command := &commands[i] if command.Hidden { continue } var completion strings.Builder completion.WriteString(fmt.Sprintf( "complete -r -c %s -n '%s' -a '%s'", a.Name, a.fishSubcommandHelper(previousCommands), strings.Join(command.Names(), " "), )) if command.Usage != "" { completion.WriteString(fmt.Sprintf(" -d '%s'", escapeSingleQuotes(command.Usage))) } if !command.HideHelp { completions = append( completions, a.prepareFishFlags([]Flag{HelpFlag}, command.Names())..., ) } *allCommands = append(*allCommands, command.Names()...) completions = append(completions, completion.String()) completions = append( completions, a.prepareFishFlags(command.Flags, command.Names())..., ) // recursevly iterate subcommands if len(command.Subcommands) > 0 { completions = append( completions, a.prepareFishCommands( command.Subcommands, allCommands, command.Names(), )..., ) } } return completions } func (a *App) prepareFishFlags(flags []Flag, previousCommands []string) []string { completions := []string{} for _, f := range flags { flag, ok := f.(DocGenerationFlag) if !ok { continue } completion := &strings.Builder{} completion.WriteString(fmt.Sprintf( "complete -c %s -n '%s'", a.Name, a.fishSubcommandHelper(previousCommands), )) fishAddFileFlag(f, completion) for idx, opt := range strings.Split(flag.GetName(), ",") { if idx == 0 { completion.WriteString(fmt.Sprintf( " -l %s", strings.TrimSpace(opt), )) } else { completion.WriteString(fmt.Sprintf( " -s %s", strings.TrimSpace(opt), )) } } if flag.TakesValue() { completion.WriteString(" -r") } if flag.GetUsage() != "" { completion.WriteString(fmt.Sprintf(" -d '%s'", escapeSingleQuotes(flag.GetUsage()))) } completions = append(completions, completion.String()) } return completions } func fishAddFileFlag(flag Flag, completion *strings.Builder) { switch f := flag.(type) { case GenericFlag: if f.TakesFile { return } case StringFlag: if f.TakesFile { return } case StringSliceFlag: if f.TakesFile { return } } completion.WriteString(" -f") } func (a *App) fishSubcommandHelper(allCommands []string) string { fishHelper := fmt.Sprintf("__fish_%s_no_subcommand", a.Name) if len(allCommands) > 0 { fishHelper = fmt.Sprintf( "__fish_seen_subcommand_from %s", strings.Join(allCommands, " "), ) } return fishHelper } func escapeSingleQuotes(input string) string { return strings.Replace(input, `'`, `\'`, -1) } cli-1.22.4/fish_test.go000066400000000000000000000003661364073550300147010ustar00rootroot00000000000000package cli import ( "testing" ) func TestFishCompletion(t *testing.T) { // Given app := testApp() // When res, err := app.ToFishCompletion() // Then expect(t, err, nil) expectFileContent(t, "testdata/expected-fish-full.fish", res) } cli-1.22.4/flag.go000066400000000000000000000205271364073550300136230ustar00rootroot00000000000000package cli import ( "flag" "fmt" "io/ioutil" "reflect" "runtime" "strconv" "strings" "syscall" ) const defaultPlaceholder = "value" // BashCompletionFlag enables bash-completion for all commands and subcommands var BashCompletionFlag Flag = BoolFlag{ Name: "generate-bash-completion", Hidden: true, } // VersionFlag prints the version for the application var VersionFlag Flag = BoolFlag{ Name: "version, v", Usage: "print the version", } // HelpFlag prints the help for all commands and subcommands // Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand // unless HideHelp is set to true) var HelpFlag Flag = BoolFlag{ Name: "help, h", Usage: "show help", } // FlagStringer converts a flag definition to a string. This is used by help // to display a flag. var FlagStringer FlagStringFunc = stringifyFlag // FlagNamePrefixer converts a full flag name and its placeholder into the help // message flag prefix. This is used by the default FlagStringer. var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames // FlagEnvHinter annotates flag help message with the environment variable // details. This is used by the default FlagStringer. var FlagEnvHinter FlagEnvHintFunc = withEnvHint // FlagFileHinter annotates flag help message with the environment variable // details. This is used by the default FlagStringer. var FlagFileHinter FlagFileHintFunc = withFileHint // FlagsByName is a slice of Flag. type FlagsByName []Flag func (f FlagsByName) Len() int { return len(f) } func (f FlagsByName) Less(i, j int) bool { return lexicographicLess(f[i].GetName(), f[j].GetName()) } func (f FlagsByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } // Flag is a common interface related to parsing flags in cli. // For more advanced flag parsing techniques, it is recommended that // this interface be implemented. type Flag interface { fmt.Stringer // Apply Flag settings to the given flag set Apply(*flag.FlagSet) GetName() string } // RequiredFlag is an interface that allows us to mark flags as required // it allows flags required flags to be backwards compatible with the Flag interface type RequiredFlag interface { Flag IsRequired() bool } // DocGenerationFlag is an interface that allows documentation generation for the flag type DocGenerationFlag interface { Flag // TakesValue returns true if the flag takes a value, otherwise false TakesValue() bool // GetUsage returns the usage string for the flag GetUsage() string // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. GetValue() string } // errorableFlag is an interface that allows us to return errors during apply // it allows flags defined in this library to return errors in a fashion backwards compatible // TODO remove in v2 and modify the existing Flag interface to return errors type errorableFlag interface { Flag ApplyWithError(*flag.FlagSet) error } func flagSet(name string, flags []Flag) (*flag.FlagSet, error) { set := flag.NewFlagSet(name, flag.ContinueOnError) for _, f := range flags { //TODO remove in v2 when errorableFlag is removed if ef, ok := f.(errorableFlag); ok { if err := ef.ApplyWithError(set); err != nil { return nil, err } } else { f.Apply(set) } } set.SetOutput(ioutil.Discard) return set, nil } func eachName(longName string, fn func(string)) { parts := strings.Split(longName, ",") for _, name := range parts { name = strings.Trim(name, " ") fn(name) } } func visibleFlags(fl []Flag) []Flag { var visible []Flag for _, f := range fl { field := flagValue(f).FieldByName("Hidden") if !field.IsValid() || !field.Bool() { visible = append(visible, f) } } return visible } func prefixFor(name string) (prefix string) { if len(name) == 1 { prefix = "-" } else { prefix = "--" } return } // Returns the placeholder, if any, and the unquoted usage string. func unquoteUsage(usage string) (string, string) { for i := 0; i < len(usage); i++ { if usage[i] == '`' { for j := i + 1; j < len(usage); j++ { if usage[j] == '`' { name := usage[i+1 : j] usage = usage[:i] + name + usage[j+1:] return name, usage } } break } } return "", usage } func prefixedNames(fullName, placeholder string) string { var prefixed string parts := strings.Split(fullName, ",") for i, name := range parts { name = strings.Trim(name, " ") prefixed += prefixFor(name) + name if placeholder != "" { prefixed += " " + placeholder } if i < len(parts)-1 { prefixed += ", " } } return prefixed } func withEnvHint(envVar, str string) string { envText := "" if envVar != "" { prefix := "$" suffix := "" sep := ", $" if runtime.GOOS == "windows" { prefix = "%" suffix = "%" sep = "%, %" } envText = " [" + prefix + strings.Join(strings.Split(envVar, ","), sep) + suffix + "]" } return str + envText } func withFileHint(filePath, str string) string { fileText := "" if filePath != "" { fileText = fmt.Sprintf(" [%s]", filePath) } return str + fileText } func flagValue(f Flag) reflect.Value { fv := reflect.ValueOf(f) for fv.Kind() == reflect.Ptr { fv = reflect.Indirect(fv) } return fv } func stringifyFlag(f Flag) string { fv := flagValue(f) switch f.(type) { case IntSliceFlag: return FlagFileHinter( fv.FieldByName("FilePath").String(), FlagEnvHinter( fv.FieldByName("EnvVar").String(), stringifyIntSliceFlag(f.(IntSliceFlag)), ), ) case Int64SliceFlag: return FlagFileHinter( fv.FieldByName("FilePath").String(), FlagEnvHinter( fv.FieldByName("EnvVar").String(), stringifyInt64SliceFlag(f.(Int64SliceFlag)), ), ) case StringSliceFlag: return FlagFileHinter( fv.FieldByName("FilePath").String(), FlagEnvHinter( fv.FieldByName("EnvVar").String(), stringifyStringSliceFlag(f.(StringSliceFlag)), ), ) } placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String()) needsPlaceholder := false defaultValueString := "" if val := fv.FieldByName("Value"); val.IsValid() { needsPlaceholder = true defaultValueString = fmt.Sprintf(" (default: %v)", val.Interface()) if val.Kind() == reflect.String && val.String() != "" { defaultValueString = fmt.Sprintf(" (default: %q)", val.String()) } } if defaultValueString == " (default: )" { defaultValueString = "" } if needsPlaceholder && placeholder == "" { placeholder = defaultPlaceholder } usageWithDefault := strings.TrimSpace(usage + defaultValueString) return FlagFileHinter( fv.FieldByName("FilePath").String(), FlagEnvHinter( fv.FieldByName("EnvVar").String(), FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder)+"\t"+usageWithDefault, ), ) } func stringifyIntSliceFlag(f IntSliceFlag) string { var defaultVals []string if f.Value != nil && len(f.Value.Value()) > 0 { for _, i := range f.Value.Value() { defaultVals = append(defaultVals, strconv.Itoa(i)) } } return stringifySliceFlag(f.Usage, f.Name, defaultVals) } func stringifyInt64SliceFlag(f Int64SliceFlag) string { var defaultVals []string if f.Value != nil && len(f.Value.Value()) > 0 { for _, i := range f.Value.Value() { defaultVals = append(defaultVals, strconv.FormatInt(i, 10)) } } return stringifySliceFlag(f.Usage, f.Name, defaultVals) } func stringifyStringSliceFlag(f StringSliceFlag) string { var defaultVals []string if f.Value != nil && len(f.Value.Value()) > 0 { for _, s := range f.Value.Value() { if len(s) > 0 { defaultVals = append(defaultVals, strconv.Quote(s)) } } } return stringifySliceFlag(f.Usage, f.Name, defaultVals) } func stringifySliceFlag(usage, name string, defaultVals []string) string { placeholder, usage := unquoteUsage(usage) if placeholder == "" { placeholder = defaultPlaceholder } defaultVal := "" if len(defaultVals) > 0 { defaultVal = fmt.Sprintf(" (default: %s)", strings.Join(defaultVals, ", ")) } usageWithDefault := strings.TrimSpace(usage + defaultVal) return FlagNamePrefixer(name, placeholder) + "\t" + usageWithDefault } func flagFromFileEnv(filePath, envName string) (val string, ok bool) { for _, envVar := range strings.Split(envName, ",") { envVar = strings.TrimSpace(envVar) if envVal, ok := syscall.Getenv(envVar); ok { return envVal, true } } for _, fileVar := range strings.Split(filePath, ",") { if data, err := ioutil.ReadFile(fileVar); err == nil { return string(data), true } } return "", false } cli-1.22.4/flag_bool.go000066400000000000000000000045531364073550300146370ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strconv" ) // BoolFlag is a flag with type bool type BoolFlag struct { Name string Usage string EnvVar string FilePath string Required bool Hidden bool Destination *bool } // String returns a readable representation of this value // (for usage defaults) func (f BoolFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f BoolFlag) GetName() string { return f.Name } // IsRequired returns whether or not the flag is required func (f BoolFlag) IsRequired() bool { return f.Required } // TakesValue returns true of the flag takes a value, otherwise false func (f BoolFlag) TakesValue() bool { return false } // GetUsage returns the usage string for the flag func (f BoolFlag) GetUsage() string { return f.Usage } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f BoolFlag) GetValue() string { return "" } // Bool looks up the value of a local BoolFlag, returns // false if not found func (c *Context) Bool(name string) bool { return lookupBool(name, c.flagSet) } // GlobalBool looks up the value of a global BoolFlag, returns // false if not found func (c *Context) GlobalBool(name string) bool { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupBool(name, fs) } return false } // Apply populates the flag given the flag set and environment // Ignores errors func (f BoolFlag) Apply(set *flag.FlagSet) { _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { val := false if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { if envVal == "" { val = false } else { envValBool, err := strconv.ParseBool(envVal) if err != nil { return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) } val = envValBool } } eachName(f.Name, func(name string) { if f.Destination != nil { set.BoolVar(f.Destination, name, val, f.Usage) return } set.Bool(name, val, f.Usage) }) return nil } func lookupBool(name string, set *flag.FlagSet) bool { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseBool(f.Value.String()) if err != nil { return false } return parsed } return false } cli-1.22.4/flag_bool_t.go000066400000000000000000000046261364073550300151630ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strconv" ) // BoolTFlag is a flag with type bool that is true by default type BoolTFlag struct { Name string Usage string EnvVar string FilePath string Required bool Hidden bool Destination *bool } // String returns a readable representation of this value // (for usage defaults) func (f BoolTFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f BoolTFlag) GetName() string { return f.Name } // IsRequired returns whether or not the flag is required func (f BoolTFlag) IsRequired() bool { return f.Required } // TakesValue returns true of the flag takes a value, otherwise false func (f BoolTFlag) TakesValue() bool { return false } // GetUsage returns the usage string for the flag func (f BoolTFlag) GetUsage() string { return f.Usage } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f BoolTFlag) GetValue() string { return "" } // BoolT looks up the value of a local BoolTFlag, returns // false if not found func (c *Context) BoolT(name string) bool { return lookupBoolT(name, c.flagSet) } // GlobalBoolT looks up the value of a global BoolTFlag, returns // false if not found func (c *Context) GlobalBoolT(name string) bool { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupBoolT(name, fs) } return false } // Apply populates the flag given the flag set and environment // Ignores errors func (f BoolTFlag) Apply(set *flag.FlagSet) { _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error { val := true if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { if envVal == "" { val = false } else { envValBool, err := strconv.ParseBool(envVal) if err != nil { return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) } val = envValBool } } eachName(f.Name, func(name string) { if f.Destination != nil { set.BoolVar(f.Destination, name, val, f.Usage) return } set.Bool(name, val, f.Usage) }) return nil } func lookupBoolT(name string, set *flag.FlagSet) bool { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseBool(f.Value.String()) if err != nil { return false } return parsed } return false } cli-1.22.4/flag_duration.go000066400000000000000000000047761364073550300155400ustar00rootroot00000000000000package cli import ( "flag" "fmt" "time" ) // DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration) type DurationFlag struct { Name string Usage string EnvVar string FilePath string Required bool Hidden bool Value time.Duration Destination *time.Duration } // String returns a readable representation of this value // (for usage defaults) func (f DurationFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f DurationFlag) GetName() string { return f.Name } // IsRequired returns whether or not the flag is required func (f DurationFlag) IsRequired() bool { return f.Required } // TakesValue returns true of the flag takes a value, otherwise false func (f DurationFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f DurationFlag) GetUsage() string { return f.Usage } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f DurationFlag) GetValue() string { return f.Value.String() } // Duration looks up the value of a local DurationFlag, returns // 0 if not found func (c *Context) Duration(name string) time.Duration { return lookupDuration(name, c.flagSet) } // GlobalDuration looks up the value of a global DurationFlag, returns // 0 if not found func (c *Context) GlobalDuration(name string) time.Duration { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupDuration(name, fs) } return 0 } // Apply populates the flag given the flag set and environment // Ignores errors func (f DurationFlag) Apply(set *flag.FlagSet) { _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { envValDuration, err := time.ParseDuration(envVal) if err != nil { return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err) } f.Value = envValDuration } eachName(f.Name, func(name string) { if f.Destination != nil { set.DurationVar(f.Destination, name, f.Value, f.Usage) return } set.Duration(name, f.Value, f.Usage) }) return nil } func lookupDuration(name string, set *flag.FlagSet) time.Duration { f := set.Lookup(name) if f != nil { parsed, err := time.ParseDuration(f.Value.String()) if err != nil { return 0 } return parsed } return 0 } cli-1.22.4/flag_float64.go000066400000000000000000000046501364073550300151610ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strconv" ) // Float64Flag is a flag with type float64 type Float64Flag struct { Name string Usage string EnvVar string FilePath string Required bool Hidden bool Value float64 Destination *float64 } // String returns a readable representation of this value // (for usage defaults) func (f Float64Flag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f Float64Flag) GetName() string { return f.Name } // IsRequired returns whether or not the flag is required func (f Float64Flag) IsRequired() bool { return f.Required } // TakesValue returns true of the flag takes a value, otherwise false func (f Float64Flag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f Float64Flag) GetUsage() string { return f.Usage } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f Float64Flag) GetValue() string { return fmt.Sprintf("%f", f.Value) } // Float64 looks up the value of a local Float64Flag, returns // 0 if not found func (c *Context) Float64(name string) float64 { return lookupFloat64(name, c.flagSet) } // GlobalFloat64 looks up the value of a global Float64Flag, returns // 0 if not found func (c *Context) GlobalFloat64(name string) float64 { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupFloat64(name, fs) } return 0 } // Apply populates the flag given the flag set and environment // Ignores errors func (f Float64Flag) Apply(set *flag.FlagSet) { _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { envValFloat, err := strconv.ParseFloat(envVal, 10) if err != nil { return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err) } f.Value = envValFloat } eachName(f.Name, func(name string) { if f.Destination != nil { set.Float64Var(f.Destination, name, f.Value, f.Usage) return } set.Float64(name, f.Value, f.Usage) }) return nil } func lookupFloat64(name string, set *flag.FlagSet) float64 { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseFloat(f.Value.String(), 64) if err != nil { return 0 } return parsed } return 0 } cli-1.22.4/flag_generic.go000066400000000000000000000050501364073550300153110ustar00rootroot00000000000000package cli import ( "flag" "fmt" ) // Generic is a generic parseable type identified by a specific flag type Generic interface { Set(value string) error String() string } // GenericFlag is a flag with type Generic type GenericFlag struct { Name string Usage string EnvVar string FilePath string Required bool Hidden bool TakesFile bool Value Generic } // String returns a readable representation of this value // (for usage defaults) func (f GenericFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f GenericFlag) GetName() string { return f.Name } // IsRequired returns whether or not the flag is required func (f GenericFlag) IsRequired() bool { return f.Required } // TakesValue returns true of the flag takes a value, otherwise false func (f GenericFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f GenericFlag) GetUsage() string { return f.Usage } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f GenericFlag) GetValue() string { if f.Value != nil { return f.Value.String() } return "" } // Apply takes the flagset and calls Set on the generic flag with the value // provided by the user for parsing by the flag // Ignores parsing errors func (f GenericFlag) Apply(set *flag.FlagSet) { _ = f.ApplyWithError(set) } // ApplyWithError takes the flagset and calls Set on the generic flag with the value // provided by the user for parsing by the flag func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error { val := f.Value if fileEnvVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { if err := val.Set(fileEnvVal); err != nil { return fmt.Errorf("could not parse %s as value for flag %s: %s", fileEnvVal, f.Name, err) } } eachName(f.Name, func(name string) { set.Var(f.Value, name, f.Usage) }) return nil } // Generic looks up the value of a local GenericFlag, returns // nil if not found func (c *Context) Generic(name string) interface{} { return lookupGeneric(name, c.flagSet) } // GlobalGeneric looks up the value of a global GenericFlag, returns // nil if not found func (c *Context) GlobalGeneric(name string) interface{} { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupGeneric(name, fs) } return nil } func lookupGeneric(name string, set *flag.FlagSet) interface{} { f := set.Lookup(name) if f != nil { parsed, err := f.Value, error(nil) if err != nil { return nil } return parsed } return nil } cli-1.22.4/flag_int.go000066400000000000000000000044771364073550300145030ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strconv" ) // IntFlag is a flag with type int type IntFlag struct { Name string Usage string EnvVar string FilePath string Required bool Hidden bool Value int Destination *int } // String returns a readable representation of this value // (for usage defaults) func (f IntFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f IntFlag) GetName() string { return f.Name } // IsRequired returns whether or not the flag is required func (f IntFlag) IsRequired() bool { return f.Required } // TakesValue returns true of the flag takes a value, otherwise false func (f IntFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f IntFlag) GetUsage() string { return f.Usage } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f IntFlag) GetValue() string { return fmt.Sprintf("%d", f.Value) } // Apply populates the flag given the flag set and environment // Ignores errors func (f IntFlag) Apply(set *flag.FlagSet) { _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f IntFlag) ApplyWithError(set *flag.FlagSet) error { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { envValInt, err := strconv.ParseInt(envVal, 0, 64) if err != nil { return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) } f.Value = int(envValInt) } eachName(f.Name, func(name string) { if f.Destination != nil { set.IntVar(f.Destination, name, f.Value, f.Usage) return } set.Int(name, f.Value, f.Usage) }) return nil } // Int looks up the value of a local IntFlag, returns // 0 if not found func (c *Context) Int(name string) int { return lookupInt(name, c.flagSet) } // GlobalInt looks up the value of a global IntFlag, returns // 0 if not found func (c *Context) GlobalInt(name string) int { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupInt(name, fs) } return 0 } func lookupInt(name string, set *flag.FlagSet) int { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) if err != nil { return 0 } return int(parsed) } return 0 } cli-1.22.4/flag_int64.go000066400000000000000000000045541364073550300146510ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strconv" ) // Int64Flag is a flag with type int64 type Int64Flag struct { Name string Usage string EnvVar string FilePath string Required bool Hidden bool Value int64 Destination *int64 } // String returns a readable representation of this value // (for usage defaults) func (f Int64Flag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f Int64Flag) GetName() string { return f.Name } // IsRequired returns whether or not the flag is required func (f Int64Flag) IsRequired() bool { return f.Required } // TakesValue returns true of the flag takes a value, otherwise false func (f Int64Flag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f Int64Flag) GetUsage() string { return f.Usage } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f Int64Flag) GetValue() string { return fmt.Sprintf("%d", f.Value) } // Apply populates the flag given the flag set and environment // Ignores errors func (f Int64Flag) Apply(set *flag.FlagSet) { _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { envValInt, err := strconv.ParseInt(envVal, 0, 64) if err != nil { return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err) } f.Value = envValInt } eachName(f.Name, func(name string) { if f.Destination != nil { set.Int64Var(f.Destination, name, f.Value, f.Usage) return } set.Int64(name, f.Value, f.Usage) }) return nil } // Int64 looks up the value of a local Int64Flag, returns // 0 if not found func (c *Context) Int64(name string) int64 { return lookupInt64(name, c.flagSet) } // GlobalInt64 looks up the value of a global Int64Flag, returns // 0 if not found func (c *Context) GlobalInt64(name string) int64 { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupInt64(name, fs) } return 0 } func lookupInt64(name string, set *flag.FlagSet) int64 { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseInt(f.Value.String(), 0, 64) if err != nil { return 0 } return parsed } return 0 } cli-1.22.4/flag_int64_slice.go000066400000000000000000000106371364073550300160270ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strconv" "strings" ) // Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter type Int64Slice []int64 // Set parses the value into an integer and appends it to the list of values func (f *Int64Slice) Set(value string) error { tmp, err := strconv.ParseInt(value, 10, 64) if err != nil { return err } *f = append(*f, tmp) return nil } // String returns a readable representation of this value (for usage defaults) func (f *Int64Slice) String() string { slice := make([]string, len(*f)) for i, v := range *f { slice[i] = strconv.FormatInt(v, 10) } return strings.Join(slice, ",") } // Value returns the slice of ints set by this flag func (f *Int64Slice) Value() []int64 { return *f } // Get returns the slice of ints set by this flag func (f *Int64Slice) Get() interface{} { return *f } // Int64SliceFlag is a flag with type *Int64Slice type Int64SliceFlag struct { Name string Usage string EnvVar string FilePath string Required bool Hidden bool Value *Int64Slice } // String returns a readable representation of this value // (for usage defaults) func (f Int64SliceFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f Int64SliceFlag) GetName() string { return f.Name } // IsRequired returns whether or not the flag is required func (f Int64SliceFlag) IsRequired() bool { return f.Required } // TakesValue returns true of the flag takes a value, otherwise false func (f Int64SliceFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f Int64SliceFlag) GetUsage() string { return f.Usage } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f Int64SliceFlag) GetValue() string { if f.Value != nil { return f.Value.String() } return "" } // Apply populates the flag given the flag set and environment // Ignores errors func (f Int64SliceFlag) Apply(set *flag.FlagSet) { _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { newVal := &Int64Slice{} for _, s := range strings.Split(envVal, ",") { s = strings.TrimSpace(s) if err := newVal.Set(s); err != nil { return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err) } } if f.Value == nil { f.Value = newVal } else { *f.Value = *newVal } } eachName(f.Name, func(name string) { if f.Value == nil { f.Value = &Int64Slice{} } set.Var(f.Value, name, f.Usage) }) return nil } // Int64Slice looks up the value of a local Int64SliceFlag, returns // nil if not found func (c *Context) Int64Slice(name string) []int64 { return lookupInt64Slice(name, c.flagSet) } // GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns // nil if not found func (c *Context) GlobalInt64Slice(name string) []int64 { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupInt64Slice(name, fs) } return nil } func lookupInt64Slice(name string, set *flag.FlagSet) []int64 { f := set.Lookup(name) if f != nil { value, ok := f.Value.(*Int64Slice) if !ok { return nil } // extract the slice from asserted value parsed := value.Value() // extract default value from the flag var defaultVal []int64 for _, v := range strings.Split(f.DefValue, ",") { if v != "" { int64Value, err := strconv.ParseInt(v, 10, 64) if err != nil { panic(err) } defaultVal = append(defaultVal, int64Value) } } // if the current value is not equal to the default value // remove the default values from the flag if !isInt64SliceEqual(parsed, defaultVal) { for _, v := range defaultVal { parsed = removeFromInt64Slice(parsed, v) } } return parsed } return nil } func removeFromInt64Slice(slice []int64, val int64) []int64 { for i, v := range slice { if v == val { return append(slice[:i], slice[i+1:]...) } } return slice } func isInt64SliceEqual(newValue, defaultValue []int64) bool { // If one is nil, the other must also be nil. if (newValue == nil) != (defaultValue == nil) { return false } if len(newValue) != len(defaultValue) { return false } for i, v := range newValue { if v != defaultValue[i] { return false } } return true } cli-1.22.4/flag_int_slice.go000066400000000000000000000104321364073550300156460ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strconv" "strings" ) // IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter type IntSlice []int // Set parses the value into an integer and appends it to the list of values func (f *IntSlice) Set(value string) error { tmp, err := strconv.Atoi(value) if err != nil { return err } *f = append(*f, tmp) return nil } // String returns a readable representation of this value (for usage defaults) func (f *IntSlice) String() string { slice := make([]string, len(*f)) for i, v := range *f { slice[i] = strconv.Itoa(v) } return strings.Join(slice, ",") } // Value returns the slice of ints set by this flag func (f *IntSlice) Value() []int { return *f } // Get returns the slice of ints set by this flag func (f *IntSlice) Get() interface{} { return *f } // IntSliceFlag is a flag with type *IntSlice type IntSliceFlag struct { Name string Usage string EnvVar string FilePath string Required bool Hidden bool Value *IntSlice } // String returns a readable representation of this value // (for usage defaults) func (f IntSliceFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f IntSliceFlag) GetName() string { return f.Name } // IsRequired returns whether or not the flag is required func (f IntSliceFlag) IsRequired() bool { return f.Required } // TakesValue returns true of the flag takes a value, otherwise false func (f IntSliceFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f IntSliceFlag) GetUsage() string { return f.Usage } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f IntSliceFlag) GetValue() string { if f.Value != nil { return f.Value.String() } return "" } // Apply populates the flag given the flag set and environment // Ignores errors func (f IntSliceFlag) Apply(set *flag.FlagSet) { _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { newVal := &IntSlice{} for _, s := range strings.Split(envVal, ",") { s = strings.TrimSpace(s) if err := newVal.Set(s); err != nil { return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err) } } if f.Value == nil { f.Value = newVal } else { *f.Value = *newVal } } eachName(f.Name, func(name string) { if f.Value == nil { f.Value = &IntSlice{} } set.Var(f.Value, name, f.Usage) }) return nil } // IntSlice looks up the value of a local IntSliceFlag, returns // nil if not found func (c *Context) IntSlice(name string) []int { return lookupIntSlice(name, c.flagSet) } // GlobalIntSlice looks up the value of a global IntSliceFlag, returns // nil if not found func (c *Context) GlobalIntSlice(name string) []int { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupIntSlice(name, fs) } return nil } func lookupIntSlice(name string, set *flag.FlagSet) []int { f := set.Lookup(name) if f != nil { value, ok := f.Value.(*IntSlice) if !ok { return nil } // extract the slice from asserted value slice := value.Value() // extract default value from the flag var defaultVal []int for _, v := range strings.Split(f.DefValue, ",") { if v != "" { intValue, err := strconv.Atoi(v) if err != nil { panic(err) } defaultVal = append(defaultVal, intValue) } } // if the current value is not equal to the default value // remove the default values from the flag if !isIntSliceEqual(slice, defaultVal) { for _, v := range defaultVal { slice = removeFromIntSlice(slice, v) } } return slice } return nil } func removeFromIntSlice(slice []int, val int) []int { for i, v := range slice { if v == val { return append(slice[:i], slice[i+1:]...) } } return slice } func isIntSliceEqual(newValue, defaultValue []int) bool { // If one is nil, the other must also be nil. if (newValue == nil) != (defaultValue == nil) { return false } if len(newValue) != len(defaultValue) { return false } for i, v := range newValue { if v != defaultValue[i] { return false } } return true } cli-1.22.4/flag_string.go000066400000000000000000000042741364073550300152120ustar00rootroot00000000000000package cli import "flag" // StringFlag is a flag with type string type StringFlag struct { Name string Usage string EnvVar string FilePath string Required bool Hidden bool TakesFile bool Value string Destination *string } // String returns a readable representation of this value // (for usage defaults) func (f StringFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f StringFlag) GetName() string { return f.Name } // IsRequired returns whether or not the flag is required func (f StringFlag) IsRequired() bool { return f.Required } // TakesValue returns true of the flag takes a value, otherwise false func (f StringFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f StringFlag) GetUsage() string { return f.Usage } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f StringFlag) GetValue() string { return f.Value } // Apply populates the flag given the flag set and environment // Ignores errors func (f StringFlag) Apply(set *flag.FlagSet) { _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f StringFlag) ApplyWithError(set *flag.FlagSet) error { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { f.Value = envVal } eachName(f.Name, func(name string) { if f.Destination != nil { set.StringVar(f.Destination, name, f.Value, f.Usage) return } set.String(name, f.Value, f.Usage) }) return nil } // String looks up the value of a local StringFlag, returns // "" if not found func (c *Context) String(name string) string { return lookupString(name, c.flagSet) } // GlobalString looks up the value of a global StringFlag, returns // "" if not found func (c *Context) GlobalString(name string) string { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupString(name, fs) } return "" } func lookupString(name string, set *flag.FlagSet) string { f := set.Lookup(name) if f != nil { parsed, err := f.Value.String(), error(nil) if err != nil { return "" } return parsed } return "" } cli-1.22.4/flag_string_slice.go000066400000000000000000000102201364073550300163550ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strings" ) // StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter type StringSlice []string // Set appends the string value to the list of values func (f *StringSlice) Set(value string) error { *f = append(*f, value) return nil } // String returns a readable representation of this value (for usage defaults) func (f *StringSlice) String() string { return strings.Join(*f, ",") } // Value returns the slice of strings set by this flag func (f *StringSlice) Value() []string { return *f } // Get returns the slice of strings set by this flag func (f *StringSlice) Get() interface{} { return *f } // StringSliceFlag is a flag with type *StringSlice type StringSliceFlag struct { Name string Usage string EnvVar string FilePath string Required bool Hidden bool TakesFile bool Value *StringSlice } // String returns a readable representation of this value // (for usage defaults) func (f StringSliceFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f StringSliceFlag) GetName() string { return f.Name } // IsRequired returns whether or not the flag is required func (f StringSliceFlag) IsRequired() bool { return f.Required } // TakesValue returns true of the flag takes a value, otherwise false func (f StringSliceFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f StringSliceFlag) GetUsage() string { return f.Usage } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f StringSliceFlag) GetValue() string { if f.Value != nil { return f.Value.String() } return "" } // Apply populates the flag given the flag set and environment // Ignores errors func (f StringSliceFlag) Apply(set *flag.FlagSet) { _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { newVal := &StringSlice{} for _, s := range strings.Split(envVal, ",") { s = strings.TrimSpace(s) if err := newVal.Set(s); err != nil { return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err) } } if f.Value == nil { f.Value = newVal } else { *f.Value = *newVal } } eachName(f.Name, func(name string) { if f.Value == nil { f.Value = &StringSlice{} } set.Var(f.Value, name, f.Usage) }) return nil } // StringSlice looks up the value of a local StringSliceFlag, returns // nil if not found func (c *Context) StringSlice(name string) []string { return lookupStringSlice(name, c.flagSet) } // GlobalStringSlice looks up the value of a global StringSliceFlag, returns // nil if not found func (c *Context) GlobalStringSlice(name string) []string { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupStringSlice(name, fs) } return nil } func lookupStringSlice(name string, set *flag.FlagSet) []string { f := set.Lookup(name) if f != nil { value, ok := f.Value.(*StringSlice) if !ok { return nil } // extract the slice from asserted value slice := value.Value() // extract default value from the flag var defaultVal []string for _, v := range strings.Split(f.DefValue, ",") { defaultVal = append(defaultVal, v) } // if the current value is not equal to the default value // remove the default values from the flag if !isStringSliceEqual(slice, defaultVal) { for _, v := range defaultVal { slice = removeFromStringSlice(slice, v) } } return slice } return nil } func removeFromStringSlice(slice []string, val string) []string { for i, v := range slice { if v == val { return append(slice[:i], slice[i+1:]...) } } return slice } func isStringSliceEqual(newValue, defaultValue []string) bool { // If one is nil, the other must also be nil. if (newValue == nil) != (defaultValue == nil) { return false } if len(newValue) != len(defaultValue) { return false } for i, v := range newValue { if v != defaultValue[i] { return false } } return true } cli-1.22.4/flag_test.go000066400000000000000000001121551364073550300146610ustar00rootroot00000000000000package cli import ( "fmt" "io" "io/ioutil" "os" "reflect" "regexp" "runtime" "strings" "testing" "time" ) var boolFlagTests = []struct { name string expected string }{ {"help", "--help\t"}, {"h", "-h\t"}, } func TestBoolFlagHelpOutput(t *testing.T) { for _, test := range boolFlagTests { flag := BoolFlag{Name: test.name} output := flag.String() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestFlagsFromEnv(t *testing.T) { var flagTests = []struct { input string output interface{} flag Flag errRegexp string }{ {"", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, {"1", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, {"false", false, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, {"foobar", true, BoolFlag{Name: "debug", EnvVar: "DEBUG"}, fmt.Sprintf(`could not parse foobar as bool value for flag debug: .*`)}, {"", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, {"1", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, {"false", false, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, ""}, {"foobar", true, BoolTFlag{Name: "debug", EnvVar: "DEBUG"}, fmt.Sprintf(`could not parse foobar as bool value for flag debug: .*`)}, {"1s", 1 * time.Second, DurationFlag{Name: "time", EnvVar: "TIME"}, ""}, {"foobar", false, DurationFlag{Name: "time", EnvVar: "TIME"}, fmt.Sprintf(`could not parse foobar as duration for flag time: .*`)}, {"1.2", 1.2, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"1", 1.0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"foobar", 0, Float64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as float64 value for flag seconds: .*`)}, {"1", int64(1), Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"1.2", 0, Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as int value for flag seconds: .*`)}, {"foobar", 0, Int64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int value for flag seconds: .*`)}, {"1", 1, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"1.2", 0, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as int value for flag seconds: .*`)}, {"foobar", 0, IntFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int value for flag seconds: .*`)}, {"1,2", IntSlice{1, 2}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"1.2,2", IntSlice{}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2,2 as int slice value for flag seconds: .*`)}, {"foobar", IntSlice{}, IntSliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int slice value for flag seconds: .*`)}, {"1,2", Int64Slice{1, 2}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"1.2,2", Int64Slice{}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2,2 as int64 slice value for flag seconds: .*`)}, {"foobar", Int64Slice{}, Int64SliceFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as int64 slice value for flag seconds: .*`)}, {"foo", "foo", StringFlag{Name: "name", EnvVar: "NAME"}, ""}, {"foo,bar", StringSlice{"foo", "bar"}, StringSliceFlag{Name: "names", EnvVar: "NAMES"}, ""}, {"1", uint(1), UintFlag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"1.2", 0, UintFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as uint value for flag seconds: .*`)}, {"foobar", 0, UintFlag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as uint value for flag seconds: .*`)}, {"1", uint64(1), Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, ""}, {"1.2", 0, Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse 1.2 as uint64 value for flag seconds: .*`)}, {"foobar", 0, Uint64Flag{Name: "seconds", EnvVar: "SECONDS"}, fmt.Sprintf(`could not parse foobar as uint64 value for flag seconds: .*`)}, {"foo,bar", &Parser{"foo", "bar"}, GenericFlag{Name: "names", Value: &Parser{}, EnvVar: "NAMES"}, ""}, } for _, test := range flagTests { os.Clearenv() _ = os.Setenv(reflect.ValueOf(test.flag).FieldByName("EnvVar").String(), test.input) a := App{ Flags: []Flag{test.flag}, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.value(test.flag.GetName()), test.output) { t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.value(test.flag.GetName())) } return nil }, } err := a.Run([]string{"run"}) if test.errRegexp != "" { if err == nil { t.Errorf("expected error to match %s, got none", test.errRegexp) } else { if matched, _ := regexp.MatchString(test.errRegexp, err.Error()); !matched { t.Errorf("expected error to match %s, got error %s", test.errRegexp, err) } } } else { if err != nil && test.errRegexp == "" { t.Errorf("expected no error got %s", err) } } } } var stringFlagTests = []struct { name string usage string value string expected string }{ {"foo", "", "", "--foo value\t"}, {"f", "", "", "-f value\t"}, {"f", "The total `foo` desired", "all", "-f foo\tThe total foo desired (default: \"all\")"}, {"test", "", "Something", "--test value\t(default: \"Something\")"}, {"config,c", "Load configuration from `FILE`", "", "--config FILE, -c FILE\tLoad configuration from FILE"}, {"config,c", "Load configuration from `CONFIG`", "config.json", "--config CONFIG, -c CONFIG\tLoad configuration from CONFIG (default: \"config.json\")"}, } func TestStringFlagHelpOutput(t *testing.T) { for _, test := range stringFlagTests { flag := StringFlag{Name: test.name, Usage: test.usage, Value: test.value} output := flag.String() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_FOO", "derp") for _, test := range stringFlagTests { flag := StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"} output := flag.String() expectedSuffix := " [$APP_FOO]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_FOO%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } } } var prefixStringFlagTests = []struct { name string usage string value string prefixer FlagNamePrefixFunc expected string }{ {"foo", "", "", func(a, b string) string { return fmt.Sprintf("name: %s, ph: %s", a, b) }, "name: foo, ph: value\t"}, {"f", "", "", func(a, b string) string { return fmt.Sprintf("name: %s, ph: %s", a, b) }, "name: f, ph: value\t"}, {"f", "The total `foo` desired", "all", func(a, b string) string { return fmt.Sprintf("name: %s, ph: %s", a, b) }, "name: f, ph: foo\tThe total foo desired (default: \"all\")"}, {"test", "", "Something", func(a, b string) string { return fmt.Sprintf("name: %s, ph: %s", a, b) }, "name: test, ph: value\t(default: \"Something\")"}, {"config,c", "Load configuration from `FILE`", "", func(a, b string) string { return fmt.Sprintf("name: %s, ph: %s", a, b) }, "name: config,c, ph: FILE\tLoad configuration from FILE"}, {"config,c", "Load configuration from `CONFIG`", "config.json", func(a, b string) string { return fmt.Sprintf("name: %s, ph: %s", a, b) }, "name: config,c, ph: CONFIG\tLoad configuration from CONFIG (default: \"config.json\")"}, } func TestFlagNamePrefixer(t *testing.T) { defer func() { FlagNamePrefixer = prefixedNames }() for _, test := range prefixStringFlagTests { FlagNamePrefixer = test.prefixer flag := StringFlag{Name: test.name, Usage: test.usage, Value: test.value} output := flag.String() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } var envHintFlagTests = []struct { name string env string hinter FlagEnvHintFunc expected string }{ {"foo", "", func(a, b string) string { return fmt.Sprintf("env: %s, str: %s", a, b) }, "env: , str: --foo value\t"}, {"f", "", func(a, b string) string { return fmt.Sprintf("env: %s, str: %s", a, b) }, "env: , str: -f value\t"}, {"foo", "ENV_VAR", func(a, b string) string { return fmt.Sprintf("env: %s, str: %s", a, b) }, "env: ENV_VAR, str: --foo value\t"}, {"f", "ENV_VAR", func(a, b string) string { return fmt.Sprintf("env: %s, str: %s", a, b) }, "env: ENV_VAR, str: -f value\t"}, } func TestFlagEnvHinter(t *testing.T) { defer func() { FlagEnvHinter = withEnvHint }() for _, test := range envHintFlagTests { FlagEnvHinter = test.hinter flag := StringFlag{Name: test.name, EnvVar: test.env} output := flag.String() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } var stringSliceFlagTests = []struct { name string value *StringSlice expected string }{ {"foo", func() *StringSlice { s := &StringSlice{} _ = s.Set("") return s }(), "--foo value\t"}, {"f", func() *StringSlice { s := &StringSlice{} _ = s.Set("") return s }(), "-f value\t"}, {"f", func() *StringSlice { s := &StringSlice{} _ = s.Set("Lipstick") return s }(), "-f value\t(default: \"Lipstick\")"}, {"test", func() *StringSlice { s := &StringSlice{} _ = s.Set("Something") return s }(), "--test value\t(default: \"Something\")"}, } func TestStringSliceFlagHelpOutput(t *testing.T) { for _, test := range stringSliceFlagTests { flag := StringSliceFlag{Name: test.name, Value: test.value} output := flag.String() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_QWWX", "11,4") for _, test := range stringSliceFlagTests { flag := StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"} output := flag.String() expectedSuffix := " [$APP_QWWX]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_QWWX%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%q does not end with"+expectedSuffix, output) } } } var intFlagTests = []struct { name string expected string }{ {"hats", "--hats value\t(default: 9)"}, {"H", "-H value\t(default: 9)"}, } func TestIntFlagHelpOutput(t *testing.T) { for _, test := range intFlagTests { flag := IntFlag{Name: test.name, Value: 9} output := flag.String() if output != test.expected { t.Errorf("%s does not match %s", output, test.expected) } } } func TestIntFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_BAR", "2") for _, test := range intFlagTests { flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"} output := flag.String() expectedSuffix := " [$APP_BAR]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_BAR%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } } } var int64FlagTests = []struct { name string expected string }{ {"hats", "--hats value\t(default: 8589934592)"}, {"H", "-H value\t(default: 8589934592)"}, } func TestInt64FlagHelpOutput(t *testing.T) { for _, test := range int64FlagTests { flag := Int64Flag{Name: test.name, Value: 8589934592} output := flag.String() if output != test.expected { t.Errorf("%s does not match %s", output, test.expected) } } } func TestInt64FlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_BAR", "2") for _, test := range int64FlagTests { flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"} output := flag.String() expectedSuffix := " [$APP_BAR]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_BAR%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } } } var uintFlagTests = []struct { name string expected string }{ {"nerfs", "--nerfs value\t(default: 41)"}, {"N", "-N value\t(default: 41)"}, } func TestUintFlagHelpOutput(t *testing.T) { for _, test := range uintFlagTests { flag := UintFlag{Name: test.name, Value: 41} output := flag.String() if output != test.expected { t.Errorf("%s does not match %s", output, test.expected) } } } func TestUintFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_BAR", "2") for _, test := range uintFlagTests { flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"} output := flag.String() expectedSuffix := " [$APP_BAR]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_BAR%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } } } var uint64FlagTests = []struct { name string expected string }{ {"gerfs", "--gerfs value\t(default: 8589934582)"}, {"G", "-G value\t(default: 8589934582)"}, } func TestUint64FlagHelpOutput(t *testing.T) { for _, test := range uint64FlagTests { flag := Uint64Flag{Name: test.name, Value: 8589934582} output := flag.String() if output != test.expected { t.Errorf("%s does not match %s", output, test.expected) } } } func TestUint64FlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_BAR", "2") for _, test := range uint64FlagTests { flag := UintFlag{Name: test.name, EnvVar: "APP_BAR"} output := flag.String() expectedSuffix := " [$APP_BAR]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_BAR%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } } } var durationFlagTests = []struct { name string expected string }{ {"hooting", "--hooting value\t(default: 1s)"}, {"H", "-H value\t(default: 1s)"}, } func TestDurationFlagHelpOutput(t *testing.T) { for _, test := range durationFlagTests { flag := DurationFlag{Name: test.name, Value: 1 * time.Second} output := flag.String() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_BAR", "2h3m6s") for _, test := range durationFlagTests { flag := DurationFlag{Name: test.name, EnvVar: "APP_BAR"} output := flag.String() expectedSuffix := " [$APP_BAR]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_BAR%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } } } var intSliceFlagTests = []struct { name string value *IntSlice expected string }{ {"heads", &IntSlice{}, "--heads value\t"}, {"H", &IntSlice{}, "-H value\t"}, {"H, heads", func() *IntSlice { i := &IntSlice{} _ = i.Set("9") _ = i.Set("3") return i }(), "-H value, --heads value\t(default: 9, 3)"}, } func TestIntSliceFlagHelpOutput(t *testing.T) { for _, test := range intSliceFlagTests { flag := IntSliceFlag{Name: test.name, Value: test.value} output := flag.String() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_SMURF", "42,3") for _, test := range intSliceFlagTests { flag := IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} output := flag.String() expectedSuffix := " [$APP_SMURF]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_SMURF%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%q does not end with"+expectedSuffix, output) } } } var int64SliceFlagTests = []struct { name string value *Int64Slice expected string }{ {"heads", &Int64Slice{}, "--heads value\t"}, {"H", &Int64Slice{}, "-H value\t"}, {"H, heads", func() *Int64Slice { i := &Int64Slice{} _ = i.Set("2") _ = i.Set("17179869184") return i }(), "-H value, --heads value\t(default: 2, 17179869184)"}, } func TestInt64SliceFlagHelpOutput(t *testing.T) { for _, test := range int64SliceFlagTests { flag := Int64SliceFlag{Name: test.name, Value: test.value} output := flag.String() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestInt64SliceFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_SMURF", "42,17179869184") for _, test := range int64SliceFlagTests { flag := Int64SliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} output := flag.String() expectedSuffix := " [$APP_SMURF]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_SMURF%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%q does not end with"+expectedSuffix, output) } } } var float64FlagTests = []struct { name string expected string }{ {"hooting", "--hooting value\t(default: 0.1)"}, {"H", "-H value\t(default: 0.1)"}, } func TestFloat64FlagHelpOutput(t *testing.T) { for _, test := range float64FlagTests { flag := Float64Flag{Name: test.name, Value: 0.1} output := flag.String() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_BAZ", "99.4") for _, test := range float64FlagTests { flag := Float64Flag{Name: test.name, EnvVar: "APP_BAZ"} output := flag.String() expectedSuffix := " [$APP_BAZ]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_BAZ%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } } } var genericFlagTests = []struct { name string value Generic expected string }{ {"toads", &Parser{"abc", "def"}, "--toads value\ttest flag (default: abc,def)"}, {"t", &Parser{"abc", "def"}, "-t value\ttest flag (default: abc,def)"}, } func TestGenericFlagHelpOutput(t *testing.T) { for _, test := range genericFlagTests { flag := GenericFlag{Name: test.name, Value: test.value, Usage: "test flag"} output := flag.String() if output != test.expected { t.Errorf("%q does not match %q", output, test.expected) } } } func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_ZAP", "3") for _, test := range genericFlagTests { flag := GenericFlag{Name: test.name, EnvVar: "APP_ZAP"} output := flag.String() expectedSuffix := " [$APP_ZAP]" if runtime.GOOS == "windows" { expectedSuffix = " [%APP_ZAP%]" } if !strings.HasSuffix(output, expectedSuffix) { t.Errorf("%s does not end with"+expectedSuffix, output) } } } func TestParseMultiString(t *testing.T) { _ = (&App{ Flags: []Flag{ StringFlag{Name: "serve, s"}, }, Action: func(ctx *Context) error { if ctx.String("serve") != "10" { t.Errorf("main name not set") } if ctx.String("s") != "10" { t.Errorf("short name not set") } return nil }, }).Run([]string{"run", "-s", "10"}) } func TestParseDestinationString(t *testing.T) { var dest string _ = (&App{ Flags: []Flag{ StringFlag{ Name: "dest", Destination: &dest, }, }, Action: func(ctx *Context) error { if dest != "10" { t.Errorf("expected destination String 10") } return nil }, }).Run([]string{"run", "--dest", "10"}) } func TestParseMultiStringFromEnv(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_COUNT", "20") _ = (&App{ Flags: []Flag{ StringFlag{Name: "count, c", EnvVar: "APP_COUNT"}, }, Action: func(ctx *Context) error { if ctx.String("count") != "20" { t.Errorf("main name not set") } if ctx.String("c") != "20" { t.Errorf("short name not set") } return nil }, }).Run([]string{"run"}) } func TestParseMultiStringFromEnvCascade(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_COUNT", "20") _ = (&App{ Flags: []Flag{ StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"}, }, Action: func(ctx *Context) error { if ctx.String("count") != "20" { t.Errorf("main name not set") } if ctx.String("c") != "20" { t.Errorf("short name not set") } return nil }, }).Run([]string{"run"}) } func TestParseMultiStringSlice(t *testing.T) { _ = (&App{ Flags: []Flag{ StringSliceFlag{Name: "serve, s", Value: &StringSlice{}}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) { t.Errorf("main name not set") } if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) { t.Errorf("short name not set") } return nil }, }).Run([]string{"run", "-s", "10", "-s", "20"}) } func TestParseMultiStringSliceFromEnv(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_INTERVALS", "20,30,40") _ = (&App{ Flags: []Flag{ StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "APP_INTERVALS"}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { t.Errorf("main name not set from env") } if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) { t.Errorf("short name not set from env") } return nil }, }).Run([]string{"run"}) } func TestParseMultiStringSliceFromEnvCascade(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_INTERVALS", "20,30,40") _ = (&App{ Flags: []Flag{ StringSliceFlag{Name: "intervals, i", Value: &StringSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { t.Errorf("main name not set from env") } if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) { t.Errorf("short name not set from env") } return nil }, }).Run([]string{"run"}) } func TestParseMultiInt(t *testing.T) { _ = (&App{ Flags: []Flag{ IntFlag{Name: "serve, s"}, }, Action: func(ctx *Context) error { if ctx.Int("serve") != 10 { t.Errorf("main name not set") } if ctx.Int("s") != 10 { t.Errorf("short name not set") } return nil }, }).Run([]string{"run", "-s", "10"}) } func TestParseDestinationInt(t *testing.T) { var dest int _ = (&App{ Flags: []Flag{ IntFlag{ Name: "dest", Destination: &dest, }, }, Action: func(ctx *Context) error { if dest != 10 { t.Errorf("expected destination Int 10") } return nil }, }).Run([]string{"run", "--dest", "10"}) } func TestParseMultiIntFromEnv(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_TIMEOUT_SECONDS", "10") _ = (&App{ Flags: []Flag{ IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, }, Action: func(ctx *Context) error { if ctx.Int("timeout") != 10 { t.Errorf("main name not set") } if ctx.Int("t") != 10 { t.Errorf("short name not set") } return nil }, }).Run([]string{"run"}) } func TestParseMultiIntFromEnvCascade(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_TIMEOUT_SECONDS", "10") _ = (&App{ Flags: []Flag{ IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, }, Action: func(ctx *Context) error { if ctx.Int("timeout") != 10 { t.Errorf("main name not set") } if ctx.Int("t") != 10 { t.Errorf("short name not set") } return nil }, }).Run([]string{"run"}) } func TestParseMultiIntSlice(t *testing.T) { _ = (&App{ Flags: []Flag{ IntSliceFlag{Name: "serve, s", Value: &IntSlice{}}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) { t.Errorf("main name not set") } if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) { t.Errorf("short name not set") } return nil }, }).Run([]string{"run", "-s", "10", "-s", "20"}) } func TestParseMultiIntSliceFromEnv(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_INTERVALS", "20,30,40") _ = (&App{ Flags: []Flag{ IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "APP_INTERVALS"}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { t.Errorf("main name not set from env") } if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) { t.Errorf("short name not set from env") } return nil }, }).Run([]string{"run"}) } func TestParseMultiIntSliceFromEnvCascade(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_INTERVALS", "20,30,40") _ = (&App{ Flags: []Flag{ IntSliceFlag{Name: "intervals, i", Value: &IntSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { t.Errorf("main name not set from env") } if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) { t.Errorf("short name not set from env") } return nil }, }).Run([]string{"run"}) } func TestParseMultiInt64Slice(t *testing.T) { _ = (&App{ Flags: []Flag{ Int64SliceFlag{Name: "serve, s", Value: &Int64Slice{}}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.Int64Slice("serve"), []int64{10, 17179869184}) { t.Errorf("main name not set") } if !reflect.DeepEqual(ctx.Int64Slice("s"), []int64{10, 17179869184}) { t.Errorf("short name not set") } return nil }, }).Run([]string{"run", "-s", "10", "-s", "17179869184"}) } func TestParseMultiInt64SliceFromEnv(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_INTERVALS", "20,30,17179869184") _ = (&App{ Flags: []Flag{ Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "APP_INTERVALS"}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) { t.Errorf("main name not set from env") } if !reflect.DeepEqual(ctx.Int64Slice("i"), []int64{20, 30, 17179869184}) { t.Errorf("short name not set from env") } return nil }, }).Run([]string{"run"}) } func TestParseMultiInt64SliceFromEnvCascade(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_INTERVALS", "20,30,17179869184") _ = (&App{ Flags: []Flag{ Int64SliceFlag{Name: "intervals, i", Value: &Int64Slice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.Int64Slice("intervals"), []int64{20, 30, 17179869184}) { t.Errorf("main name not set from env") } if !reflect.DeepEqual(ctx.Int64Slice("i"), []int64{20, 30, 17179869184}) { t.Errorf("short name not set from env") } return nil }, }).Run([]string{"run"}) } func TestParseMultiFloat64(t *testing.T) { _ = (&App{ Flags: []Flag{ Float64Flag{Name: "serve, s"}, }, Action: func(ctx *Context) error { if ctx.Float64("serve") != 10.2 { t.Errorf("main name not set") } if ctx.Float64("s") != 10.2 { t.Errorf("short name not set") } return nil }, }).Run([]string{"run", "-s", "10.2"}) } func TestParseDestinationFloat64(t *testing.T) { var dest float64 _ = (&App{ Flags: []Flag{ Float64Flag{ Name: "dest", Destination: &dest, }, }, Action: func(ctx *Context) error { if dest != 10.2 { t.Errorf("expected destination Float64 10.2") } return nil }, }).Run([]string{"run", "--dest", "10.2"}) } func TestParseMultiFloat64FromEnv(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_TIMEOUT_SECONDS", "15.5") _ = (&App{ Flags: []Flag{ Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, }, Action: func(ctx *Context) error { if ctx.Float64("timeout") != 15.5 { t.Errorf("main name not set") } if ctx.Float64("t") != 15.5 { t.Errorf("short name not set") } return nil }, }).Run([]string{"run"}) } func TestParseMultiFloat64FromEnvCascade(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_TIMEOUT_SECONDS", "15.5") _ = (&App{ Flags: []Flag{ Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, }, Action: func(ctx *Context) error { if ctx.Float64("timeout") != 15.5 { t.Errorf("main name not set") } if ctx.Float64("t") != 15.5 { t.Errorf("short name not set") } return nil }, }).Run([]string{"run"}) } func TestParseMultiBool(t *testing.T) { _ = (&App{ Flags: []Flag{ BoolFlag{Name: "serve, s"}, }, Action: func(ctx *Context) error { if ctx.Bool("serve") != true { t.Errorf("main name not set") } if ctx.Bool("s") != true { t.Errorf("short name not set") } return nil }, }).Run([]string{"run", "--serve"}) } func TestParseBoolShortOptionHandle(t *testing.T) { _ = (&App{ Commands: []Command{ { Name: "foobar", UseShortOptionHandling: true, Action: func(ctx *Context) error { if ctx.Bool("serve") != true { t.Errorf("main name not set") } if ctx.Bool("option") != true { t.Errorf("short name not set") } return nil }, Flags: []Flag{ BoolFlag{Name: "serve, s"}, BoolFlag{Name: "option, o"}, }, }, }, }).Run([]string{"run", "foobar", "-so"}) } func TestParseDestinationBool(t *testing.T) { var dest bool _ = (&App{ Flags: []Flag{ BoolFlag{ Name: "dest", Destination: &dest, }, }, Action: func(ctx *Context) error { if dest != true { t.Errorf("expected destination Bool true") } return nil }, }).Run([]string{"run", "--dest"}) } func TestParseMultiBoolFromEnv(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_DEBUG", "1") _ = (&App{ Flags: []Flag{ BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, }, Action: func(ctx *Context) error { if ctx.Bool("debug") != true { t.Errorf("main name not set from env") } if ctx.Bool("d") != true { t.Errorf("short name not set from env") } return nil }, }).Run([]string{"run"}) } func TestParseMultiBoolFromEnvCascade(t *testing.T) { os.Clearenv() os.Setenv("APP_DEBUG", "1") _ = (&App{ Flags: []Flag{ BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, }, Action: func(ctx *Context) error { if ctx.Bool("debug") != true { t.Errorf("main name not set from env") } if ctx.Bool("d") != true { t.Errorf("short name not set from env") } return nil }, }).Run([]string{"run"}) } func TestParseBoolTFromEnv(t *testing.T) { var boolTFlagTests = []struct { input string output bool }{ {"", false}, {"1", true}, {"false", false}, {"true", true}, } for _, test := range boolTFlagTests { os.Clearenv() _ = os.Setenv("DEBUG", test.input) _ = (&App{ Flags: []Flag{ BoolTFlag{Name: "debug, d", EnvVar: "DEBUG"}, }, Action: func(ctx *Context) error { if ctx.Bool("debug") != test.output { t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("debug")) } if ctx.Bool("d") != test.output { t.Errorf("expected %+v to be parsed as %+v, instead was %+v", test.input, test.output, ctx.Bool("d")) } return nil }, }).Run([]string{"run"}) } } func TestParseMultiBoolT(t *testing.T) { _ = (&App{ Flags: []Flag{ BoolTFlag{Name: "serve, s"}, }, Action: func(ctx *Context) error { if ctx.BoolT("serve") != true { t.Errorf("main name not set") } if ctx.BoolT("s") != true { t.Errorf("short name not set") } return nil }, }).Run([]string{"run", "--serve"}) } func TestParseDestinationBoolT(t *testing.T) { var dest bool _ = (&App{ Flags: []Flag{ BoolTFlag{ Name: "dest", Destination: &dest, }, }, Action: func(ctx *Context) error { if dest != true { t.Errorf("expected destination BoolT true") } return nil }, }).Run([]string{"run", "--dest"}) } func TestParseMultiBoolTFromEnv(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_DEBUG", "0") _ = (&App{ Flags: []Flag{ BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, }, Action: func(ctx *Context) error { if ctx.BoolT("debug") != false { t.Errorf("main name not set from env") } if ctx.BoolT("d") != false { t.Errorf("short name not set from env") } return nil }, }).Run([]string{"run"}) } func TestParseMultiBoolTFromEnvCascade(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_DEBUG", "0") _ = (&App{ Flags: []Flag{ BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, }, Action: func(ctx *Context) error { if ctx.BoolT("debug") != false { t.Errorf("main name not set from env") } if ctx.BoolT("d") != false { t.Errorf("short name not set from env") } return nil }, }).Run([]string{"run"}) } type Parser [2]string func (p *Parser) Set(value string) error { parts := strings.Split(value, ",") if len(parts) != 2 { return fmt.Errorf("invalid format") } (*p)[0] = parts[0] (*p)[1] = parts[1] return nil } func (p *Parser) String() string { return fmt.Sprintf("%s,%s", p[0], p[1]) } func (p *Parser) Get() interface{} { return p } func TestParseGeneric(t *testing.T) { _ = (&App{ Flags: []Flag{ GenericFlag{Name: "serve, s", Value: &Parser{}}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) { t.Errorf("main name not set") } if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) { t.Errorf("short name not set") } return nil }, }).Run([]string{"run", "-s", "10,20"}) } func TestParseGenericFromEnv(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_SERVE", "20,30") _ = (&App{ Flags: []Flag{ GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) { t.Errorf("main name not set from env") } if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) { t.Errorf("short name not set from env") } return nil }, }).Run([]string{"run"}) } func TestParseGenericFromEnvCascade(t *testing.T) { os.Clearenv() _ = os.Setenv("APP_FOO", "99,2000") _ = (&App{ Flags: []Flag{ GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"}, }, Action: func(ctx *Context) error { if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) { t.Errorf("value not set from env") } return nil }, }).Run([]string{"run"}) } func TestFlagFromFile(t *testing.T) { os.Clearenv() os.Setenv("APP_FOO", "123") temp, err := ioutil.TempFile("", "urfave_cli_test") if err != nil { t.Error(err) return } _, _ = io.WriteString(temp, "abc") _ = temp.Close() defer func() { _ = os.Remove(temp.Name()) }() var filePathTests = []struct { path string name string expected string }{ {"file-does-not-exist", "APP_BAR", ""}, {"file-does-not-exist", "APP_FOO", "123"}, {"file-does-not-exist", "APP_FOO,APP_BAR", "123"}, {temp.Name(), "APP_FOO", "123"}, {temp.Name(), "APP_BAR", "abc"}, } for _, filePathTest := range filePathTests { got, _ := flagFromFileEnv(filePathTest.path, filePathTest.name) if want := filePathTest.expected; got != want { t.Errorf("Did not expect %v - Want %v", got, want) } } } func TestSliceFlag_WithDefaults(t *testing.T) { tests := []struct { args []string app *App }{ { args: []string{""}, app: &App{ Flags: []Flag{ StringSliceFlag{Name: "names, n", Value: &StringSlice{"john"}}, IntSliceFlag{Name: "userIds, u", Value: &IntSlice{3}}, Int64SliceFlag{Name: "phoneNumbers, p", Value: &Int64Slice{123456789}}, }, Action: func(ctx *Context) error { expect(t, len(ctx.StringSlice("n")), 1) for _, name := range ctx.StringSlice("names") { expect(t, name == "john", true) } expect(t, len(ctx.IntSlice("u")), 1) for _, userId := range ctx.IntSlice("userIds") { expect(t, userId == 3, true) } expect(t, len(ctx.Int64Slice("p")), 1) for _, phoneNumber := range ctx.Int64Slice("phoneNumbers") { expect(t, phoneNumber == 123456789, true) } return nil }, }, }, { args: []string{"", "-n", "jane", "-n", "bob", "-u", "5", "-u", "10", "-p", "987654321"}, app: &App{ Flags: []Flag{ StringSliceFlag{Name: "names, n", Value: &StringSlice{"john"}}, IntSliceFlag{Name: "userIds, u", Value: &IntSlice{3}}, Int64SliceFlag{Name: "phoneNumbers, p", Value: &Int64Slice{123456789}}, }, Action: func(ctx *Context) error { expect(t, len(ctx.StringSlice("n")), 2) for _, name := range ctx.StringSlice("names") { expect(t, name != "john", true) } expect(t, len(ctx.IntSlice("u")), 2) for _, userId := range ctx.IntSlice("userIds") { expect(t, userId != 3, true) } expect(t, len(ctx.Int64Slice("p")), 1) for _, phoneNumber := range ctx.Int64Slice("phoneNumbers") { expect(t, phoneNumber != 123456789, true) } return nil }, }, }, { args: []string{"", "--names", "john", "--userIds", "3", "--phoneNumbers", "123456789"}, app: &App{ Flags: []Flag{ StringSliceFlag{Name: "names, n", Value: &StringSlice{"john"}}, IntSliceFlag{Name: "userIds, u", Value: &IntSlice{3}}, Int64SliceFlag{Name: "phoneNumbers, p", Value: &Int64Slice{123456789}}, }, Action: func(ctx *Context) error { expect(t, len(ctx.StringSlice("n")), 1) for _, name := range ctx.StringSlice("names") { expect(t, name == "john", true) } expect(t, len(ctx.IntSlice("u")), 1) for _, userId := range ctx.IntSlice("userIds") { expect(t, userId == 3, true) } expect(t, len(ctx.Int64Slice("p")), 1) for _, phoneNumber := range ctx.Int64Slice("phoneNumbers") { expect(t, phoneNumber == 123456789, true) } return nil }, }, }, } for _, tt := range tests { _ = tt.app.Run(tt.args) } } cli-1.22.4/flag_uint.go000066400000000000000000000045401364073550300146570ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strconv" ) // UintFlag is a flag with type uint type UintFlag struct { Name string Usage string EnvVar string FilePath string Required bool Hidden bool Value uint Destination *uint } // String returns a readable representation of this value // (for usage defaults) func (f UintFlag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f UintFlag) GetName() string { return f.Name } // IsRequired returns whether or not the flag is required func (f UintFlag) IsRequired() bool { return f.Required } // TakesValue returns true of the flag takes a value, otherwise false func (f UintFlag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f UintFlag) GetUsage() string { return f.Usage } // Apply populates the flag given the flag set and environment // Ignores errors func (f UintFlag) Apply(set *flag.FlagSet) { _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f UintFlag) ApplyWithError(set *flag.FlagSet) error { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { envValInt, err := strconv.ParseUint(envVal, 0, 64) if err != nil { return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err) } f.Value = uint(envValInt) } eachName(f.Name, func(name string) { if f.Destination != nil { set.UintVar(f.Destination, name, f.Value, f.Usage) return } set.Uint(name, f.Value, f.Usage) }) return nil } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f UintFlag) GetValue() string { return fmt.Sprintf("%d", f.Value) } // Uint looks up the value of a local UintFlag, returns // 0 if not found func (c *Context) Uint(name string) uint { return lookupUint(name, c.flagSet) } // GlobalUint looks up the value of a global UintFlag, returns // 0 if not found func (c *Context) GlobalUint(name string) uint { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupUint(name, fs) } return 0 } func lookupUint(name string, set *flag.FlagSet) uint { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) if err != nil { return 0 } return uint(parsed) } return 0 } cli-1.22.4/flag_uint64.go000066400000000000000000000046141364073550300150330ustar00rootroot00000000000000package cli import ( "flag" "fmt" "strconv" ) // Uint64Flag is a flag with type uint64 type Uint64Flag struct { Name string Usage string EnvVar string FilePath string Required bool Hidden bool Value uint64 Destination *uint64 } // String returns a readable representation of this value // (for usage defaults) func (f Uint64Flag) String() string { return FlagStringer(f) } // GetName returns the name of the flag func (f Uint64Flag) GetName() string { return f.Name } // IsRequired returns whether or not the flag is required func (f Uint64Flag) IsRequired() bool { return f.Required } // TakesValue returns true of the flag takes a value, otherwise false func (f Uint64Flag) TakesValue() bool { return true } // GetUsage returns the usage string for the flag func (f Uint64Flag) GetUsage() string { return f.Usage } // GetValue returns the flags value as string representation and an empty // string if the flag takes no value at all. func (f Uint64Flag) GetValue() string { return fmt.Sprintf("%d", f.Value) } // Apply populates the flag given the flag set and environment // Ignores errors func (f Uint64Flag) Apply(set *flag.FlagSet) { _ = f.ApplyWithError(set) } // ApplyWithError populates the flag given the flag set and environment func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok { envValInt, err := strconv.ParseUint(envVal, 0, 64) if err != nil { return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err) } f.Value = envValInt } eachName(f.Name, func(name string) { if f.Destination != nil { set.Uint64Var(f.Destination, name, f.Value, f.Usage) return } set.Uint64(name, f.Value, f.Usage) }) return nil } // Uint64 looks up the value of a local Uint64Flag, returns // 0 if not found func (c *Context) Uint64(name string) uint64 { return lookupUint64(name, c.flagSet) } // GlobalUint64 looks up the value of a global Uint64Flag, returns // 0 if not found func (c *Context) GlobalUint64(name string) uint64 { if fs := lookupGlobalFlagSet(name, c); fs != nil { return lookupUint64(name, fs) } return 0 } func lookupUint64(name string, set *flag.FlagSet) uint64 { f := set.Lookup(name) if f != nil { parsed, err := strconv.ParseUint(f.Value.String(), 0, 64) if err != nil { return 0 } return parsed } return 0 } cli-1.22.4/funcs.go000066400000000000000000000036671364073550300140360ustar00rootroot00000000000000package cli // BashCompleteFunc is an action to execute when the bash-completion flag is set type BashCompleteFunc func(*Context) // BeforeFunc is an action to execute before any subcommands are run, but after // the context is ready if a non-nil error is returned, no subcommands are run type BeforeFunc func(*Context) error // AfterFunc is an action to execute after any subcommands are run, but after the // subcommand has finished it is run even if Action() panics type AfterFunc func(*Context) error // ActionFunc is the action to execute when no subcommands are specified type ActionFunc func(*Context) error // CommandNotFoundFunc is executed if the proper command cannot be found type CommandNotFoundFunc func(*Context, string) // OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying // customized usage error messages. This function is able to replace the // original error messages. If this function is not set, the "Incorrect usage" // is displayed and the execution is interrupted. type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error // ExitErrHandlerFunc is executed if provided in order to handle ExitError values // returned by Actions and Before/After functions. type ExitErrHandlerFunc func(context *Context, err error) // FlagStringFunc is used by the help generation to display a flag, which is // expected to be a single line. type FlagStringFunc func(Flag) string // FlagNamePrefixFunc is used by the default FlagStringFunc to create prefix // text for a flag's full name. type FlagNamePrefixFunc func(fullName, placeholder string) string // FlagEnvHintFunc is used by the default FlagStringFunc to annotate flag help // with the environment variable details. type FlagEnvHintFunc func(envVar, str string) string // FlagFileHintFunc is used by the default FlagStringFunc to annotate flag help // with the file path details. type FlagFileHintFunc func(filePath, str string) string cli-1.22.4/go.mod000066400000000000000000000002641364073550300134650ustar00rootroot00000000000000module github.com/urfave/cli go 1.11 require ( github.com/BurntSushi/toml v0.3.1 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d gopkg.in/yaml.v2 v2.2.2 ) cli-1.22.4/go.sum000066400000000000000000000024671364073550300135210ustar00rootroot00000000000000github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= cli-1.22.4/help.go000066400000000000000000000217451364073550300136450ustar00rootroot00000000000000package cli import ( "fmt" "io" "os" "strings" "text/tabwriter" "text/template" "unicode/utf8" ) var helpCommand = Command{ Name: "help", Aliases: []string{"h"}, Usage: "Shows a list of commands or help for one command", ArgsUsage: "[command]", Action: func(c *Context) error { args := c.Args() if args.Present() { return ShowCommandHelp(c, args.First()) } _ = ShowAppHelp(c) return nil }, } var helpSubcommand = Command{ Name: "help", Aliases: []string{"h"}, Usage: "Shows a list of commands or help for one command", ArgsUsage: "[command]", Action: func(c *Context) error { args := c.Args() if args.Present() { return ShowCommandHelp(c, args.First()) } return ShowSubcommandHelp(c) }, } // Prints help for the App or Command type helpPrinter func(w io.Writer, templ string, data interface{}) // Prints help for the App or Command with custom template function. type helpPrinterCustom func(w io.Writer, templ string, data interface{}, customFunc map[string]interface{}) // HelpPrinter is a function that writes the help output. If not set explicitly, // this calls HelpPrinterCustom using only the default template functions. // // If custom logic for printing help is required, this function can be // overridden. If the ExtraInfo field is defined on an App, this function // should not be modified, as HelpPrinterCustom will be used directly in order // to capture the extra information. var HelpPrinter helpPrinter = printHelp // HelpPrinterCustom is a function that writes the help output. It is used as // the default implementation of HelpPrinter, and may be called directly if // the ExtraInfo field is set on an App. var HelpPrinterCustom helpPrinterCustom = printHelpCustom // VersionPrinter prints the version for the App var VersionPrinter = printVersion // ShowAppHelpAndExit - Prints the list of subcommands for the app and exits with exit code. func ShowAppHelpAndExit(c *Context, exitCode int) { _ = ShowAppHelp(c) os.Exit(exitCode) } // ShowAppHelp is an action that displays the help. func ShowAppHelp(c *Context) error { template := c.App.CustomAppHelpTemplate if template == "" { template = AppHelpTemplate } if c.App.ExtraInfo == nil { HelpPrinter(c.App.Writer, template, c.App) return nil } customAppData := func() map[string]interface{} { return map[string]interface{}{ "ExtraInfo": c.App.ExtraInfo, } } HelpPrinterCustom(c.App.Writer, template, c.App, customAppData()) return nil } // DefaultAppComplete prints the list of subcommands as the default app completion method func DefaultAppComplete(c *Context) { DefaultCompleteWithFlags(nil)(c) } func printCommandSuggestions(commands []Command, writer io.Writer) { for _, command := range commands { if command.Hidden { continue } if os.Getenv("_CLI_ZSH_AUTOCOMPLETE_HACK") == "1" { for _, name := range command.Names() { _, _ = fmt.Fprintf(writer, "%s:%s\n", name, command.Usage) } } else { for _, name := range command.Names() { _, _ = fmt.Fprintf(writer, "%s\n", name) } } } } func cliArgContains(flagName string) bool { for _, name := range strings.Split(flagName, ",") { name = strings.TrimSpace(name) count := utf8.RuneCountInString(name) if count > 2 { count = 2 } flag := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) for _, a := range os.Args { if a == flag { return true } } } return false } func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { cur := strings.TrimPrefix(lastArg, "-") cur = strings.TrimPrefix(cur, "-") for _, flag := range flags { if bflag, ok := flag.(BoolFlag); ok && bflag.Hidden { continue } for _, name := range strings.Split(flag.GetName(), ",") { name = strings.TrimSpace(name) // this will get total count utf8 letters in flag name count := utf8.RuneCountInString(name) if count > 2 { count = 2 // resuse this count to generate single - or -- in flag completion } // if flag name has more than one utf8 letter and last argument in cli has -- prefix then // skip flag completion for short flags example -v or -x if strings.HasPrefix(lastArg, "--") && count == 1 { continue } // match if last argument matches this flag and it is not repeated if strings.HasPrefix(name, cur) && cur != name && !cliArgContains(flag.GetName()) { flagCompletion := fmt.Sprintf("%s%s", strings.Repeat("-", count), name) _, _ = fmt.Fprintln(writer, flagCompletion) } } } } func DefaultCompleteWithFlags(cmd *Command) func(c *Context) { return func(c *Context) { if len(os.Args) > 2 { lastArg := os.Args[len(os.Args)-2] if strings.HasPrefix(lastArg, "-") { printFlagSuggestions(lastArg, c.App.Flags, c.App.Writer) if cmd != nil { printFlagSuggestions(lastArg, cmd.Flags, c.App.Writer) } return } } if cmd != nil { printCommandSuggestions(cmd.Subcommands, c.App.Writer) } else { printCommandSuggestions(c.App.Commands, c.App.Writer) } } } // ShowCommandHelpAndExit - exits with code after showing help func ShowCommandHelpAndExit(c *Context, command string, code int) { _ = ShowCommandHelp(c, command) os.Exit(code) } // ShowCommandHelp prints help for the given command func ShowCommandHelp(ctx *Context, command string) error { // show the subcommand help for a command with subcommands if command == "" { HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) return nil } for _, c := range ctx.App.Commands { if c.HasName(command) { templ := c.CustomHelpTemplate if templ == "" { templ = CommandHelpTemplate } HelpPrinter(ctx.App.Writer, templ, c) return nil } } if ctx.App.CommandNotFound == nil { return NewExitError(fmt.Sprintf("No help topic for '%v'", command), 3) } ctx.App.CommandNotFound(ctx, command) return nil } // ShowSubcommandHelp prints help for the given subcommand func ShowSubcommandHelp(c *Context) error { return ShowCommandHelp(c, c.Command.Name) } // ShowVersion prints the version number of the App func ShowVersion(c *Context) { VersionPrinter(c) } func printVersion(c *Context) { _, _ = fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) } // ShowCompletions prints the lists of commands within a given context func ShowCompletions(c *Context) { a := c.App if a != nil && a.BashComplete != nil { a.BashComplete(c) } } // ShowCommandCompletions prints the custom completions for a given command func ShowCommandCompletions(ctx *Context, command string) { c := ctx.App.Command(command) if c != nil { if c.BashComplete != nil { c.BashComplete(ctx) } else { DefaultCompleteWithFlags(c)(ctx) } } } // printHelpCustom is the default implementation of HelpPrinterCustom. // // The customFuncs map will be combined with a default template.FuncMap to // allow using arbitrary functions in template rendering. func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs map[string]interface{}) { funcMap := template.FuncMap{ "join": strings.Join, } for key, value := range customFuncs { funcMap[key] = value } w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) err := t.Execute(w, data) if err != nil { // If the writer is closed, t.Execute will fail, and there's nothing // we can do to recover. if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" { _, _ = fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) } return } _ = w.Flush() } func printHelp(out io.Writer, templ string, data interface{}) { HelpPrinterCustom(out, templ, data, nil) } func checkVersion(c *Context) bool { found := false if VersionFlag.GetName() != "" { eachName(VersionFlag.GetName(), func(name string) { if c.GlobalBool(name) || c.Bool(name) { found = true } }) } return found } func checkHelp(c *Context) bool { found := false if HelpFlag.GetName() != "" { eachName(HelpFlag.GetName(), func(name string) { if c.GlobalBool(name) || c.Bool(name) { found = true } }) } return found } func checkCommandHelp(c *Context, name string) bool { if c.Bool("h") || c.Bool("help") { _ = ShowCommandHelp(c, name) return true } return false } func checkSubcommandHelp(c *Context) bool { if c.Bool("h") || c.Bool("help") { _ = ShowSubcommandHelp(c) return true } return false } func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { if !a.EnableBashCompletion { return false, arguments } pos := len(arguments) - 1 lastArg := arguments[pos] if lastArg != "--"+BashCompletionFlag.GetName() { return false, arguments } return true, arguments[:pos] } func checkCompletions(c *Context) bool { if !c.shellComplete { return false } if args := c.Args(); args.Present() { name := args.First() if cmd := c.App.Command(name); cmd != nil { // let the command handle the completion return false } } ShowCompletions(c) return true } func checkCommandCompletions(c *Context, name string) bool { if !c.shellComplete { return false } ShowCommandCompletions(c, name) return true } cli-1.22.4/help_test.go000066400000000000000000000431541364073550300147020ustar00rootroot00000000000000package cli import ( "bytes" "flag" "fmt" "io" "runtime" "strings" "testing" ) func Test_ShowAppHelp_NoAuthor(t *testing.T) { output := new(bytes.Buffer) app := NewApp() app.Writer = output c := NewContext(app, nil, nil) _ = ShowAppHelp(c) if bytes.Contains(output.Bytes(), []byte("AUTHOR(S):")) { t.Errorf("expected\n%snot to include %s", output.String(), "AUTHOR(S):") } } func Test_ShowAppHelp_NoVersion(t *testing.T) { output := new(bytes.Buffer) app := NewApp() app.Writer = output app.Version = "" c := NewContext(app, nil, nil) _ = ShowAppHelp(c) if bytes.Contains(output.Bytes(), []byte("VERSION:")) { t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:") } } func Test_ShowAppHelp_HideVersion(t *testing.T) { output := new(bytes.Buffer) app := NewApp() app.Writer = output app.HideVersion = true c := NewContext(app, nil, nil) _ = ShowAppHelp(c) if bytes.Contains(output.Bytes(), []byte("VERSION:")) { t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:") } } func Test_Help_Custom_Flags(t *testing.T) { oldFlag := HelpFlag defer func() { HelpFlag = oldFlag }() HelpFlag = BoolFlag{ Name: "help, x", Usage: "show help", } app := App{ Flags: []Flag{ BoolFlag{Name: "foo, h"}, }, Action: func(ctx *Context) error { if ctx.Bool("h") != true { t.Errorf("custom help flag not set") } return nil }, } output := new(bytes.Buffer) app.Writer = output _ = app.Run([]string{"test", "-h"}) if output.Len() > 0 { t.Errorf("unexpected output: %s", output.String()) } } func Test_Version_Custom_Flags(t *testing.T) { oldFlag := VersionFlag defer func() { VersionFlag = oldFlag }() VersionFlag = BoolFlag{ Name: "version, V", Usage: "show version", } app := App{ Flags: []Flag{ BoolFlag{Name: "foo, v"}, }, Action: func(ctx *Context) error { if ctx.Bool("v") != true { t.Errorf("custom version flag not set") } return nil }, } output := new(bytes.Buffer) app.Writer = output _ = app.Run([]string{"test", "-v"}) if output.Len() > 0 { t.Errorf("unexpected output: %s", output.String()) } } func Test_helpCommand_Action_ErrorIfNoTopic(t *testing.T) { app := NewApp() set := flag.NewFlagSet("test", 0) _ = set.Parse([]string{"foo"}) c := NewContext(app, set, nil) err := helpCommand.Action.(func(*Context) error)(c) if err == nil { t.Fatalf("expected error from helpCommand.Action(), but got nil") } exitErr, ok := err.(*ExitError) if !ok { t.Fatalf("expected ExitError from helpCommand.Action(), but instead got: %v", err.Error()) } if !strings.HasPrefix(exitErr.Error(), "No help topic for") { t.Fatalf("expected an unknown help topic error, but got: %v", exitErr.Error()) } if exitErr.exitCode != 3 { t.Fatalf("expected exit value = 3, got %d instead", exitErr.exitCode) } } func Test_helpCommand_InHelpOutput(t *testing.T) { app := NewApp() output := &bytes.Buffer{} app.Writer = output _ = app.Run([]string{"test", "--help"}) s := output.String() if strings.Contains(s, "\nCOMMANDS:\nGLOBAL OPTIONS:\n") { t.Fatalf("empty COMMANDS section detected: %q", s) } if !strings.Contains(s, "help, h") { t.Fatalf("missing \"help, h\": %q", s) } } func Test_helpSubcommand_Action_ErrorIfNoTopic(t *testing.T) { app := NewApp() set := flag.NewFlagSet("test", 0) _ = set.Parse([]string{"foo"}) c := NewContext(app, set, nil) err := helpSubcommand.Action.(func(*Context) error)(c) if err == nil { t.Fatalf("expected error from helpCommand.Action(), but got nil") } exitErr, ok := err.(*ExitError) if !ok { t.Fatalf("expected ExitError from helpCommand.Action(), but instead got: %v", err.Error()) } if !strings.HasPrefix(exitErr.Error(), "No help topic for") { t.Fatalf("expected an unknown help topic error, but got: %v", exitErr.Error()) } if exitErr.exitCode != 3 { t.Fatalf("expected exit value = 3, got %d instead", exitErr.exitCode) } } func TestShowAppHelp_CommandAliases(t *testing.T) { app := &App{ Commands: []Command{ { Name: "frobbly", Aliases: []string{"fr", "frob"}, Action: func(ctx *Context) error { return nil }, }, }, } output := &bytes.Buffer{} app.Writer = output _ = app.Run([]string{"foo", "--help"}) if !strings.Contains(output.String(), "frobbly, fr, frob") { t.Errorf("expected output to include all command aliases; got: %q", output.String()) } } func TestShowCommandHelp_HelpPrinter(t *testing.T) { doublecho := func(text string) string { return text + " " + text } tests := []struct { name string template string printer helpPrinter command string wantTemplate string wantOutput string }{ { name: "no-command", template: "", printer: func(w io.Writer, templ string, data interface{}) { fmt.Fprint(w, "yo") }, command: "", wantTemplate: SubcommandHelpTemplate, wantOutput: "yo", }, { name: "standard-command", template: "", printer: func(w io.Writer, templ string, data interface{}) { fmt.Fprint(w, "yo") }, command: "my-command", wantTemplate: CommandHelpTemplate, wantOutput: "yo", }, { name: "custom-template-command", template: "{{doublecho .Name}}", printer: func(w io.Writer, templ string, data interface{}) { // Pass a custom function to ensure it gets used fm := map[string]interface{}{"doublecho": doublecho} HelpPrinterCustom(w, templ, data, fm) }, command: "my-command", wantTemplate: "{{doublecho .Name}}", wantOutput: "my-command my-command", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { defer func(old helpPrinter) { HelpPrinter = old }(HelpPrinter) HelpPrinter = func(w io.Writer, templ string, data interface{}) { if templ != tt.wantTemplate { t.Errorf("want template:\n%s\ngot template:\n%s", tt.wantTemplate, templ) } tt.printer(w, templ, data) } var buf bytes.Buffer app := &App{ Name: "my-app", Writer: &buf, Commands: []Command{ { Name: "my-command", CustomHelpTemplate: tt.template, }, }, } err := app.Run([]string{"my-app", "help", tt.command}) if err != nil { t.Fatal(err) } got := buf.String() if got != tt.wantOutput { t.Errorf("want output %q, got %q", tt.wantOutput, got) } }) } } func TestShowCommandHelp_HelpPrinterCustom(t *testing.T) { doublecho := func(text string) string { return text + " " + text } tests := []struct { name string template string printer helpPrinterCustom command string wantTemplate string wantOutput string }{ { name: "no-command", template: "", printer: func(w io.Writer, templ string, data interface{}, fm map[string]interface{}) { fmt.Fprint(w, "yo") }, command: "", wantTemplate: SubcommandHelpTemplate, wantOutput: "yo", }, { name: "standard-command", template: "", printer: func(w io.Writer, templ string, data interface{}, fm map[string]interface{}) { fmt.Fprint(w, "yo") }, command: "my-command", wantTemplate: CommandHelpTemplate, wantOutput: "yo", }, { name: "custom-template-command", template: "{{doublecho .Name}}", printer: func(w io.Writer, templ string, data interface{}, _ map[string]interface{}) { // Pass a custom function to ensure it gets used fm := map[string]interface{}{"doublecho": doublecho} printHelpCustom(w, templ, data, fm) }, command: "my-command", wantTemplate: "{{doublecho .Name}}", wantOutput: "my-command my-command", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { defer func(old helpPrinterCustom) { HelpPrinterCustom = old }(HelpPrinterCustom) HelpPrinterCustom = func(w io.Writer, templ string, data interface{}, fm map[string]interface{}) { if fm != nil { t.Error("unexpected function map passed") } if templ != tt.wantTemplate { t.Errorf("want template:\n%s\ngot template:\n%s", tt.wantTemplate, templ) } tt.printer(w, templ, data, fm) } var buf bytes.Buffer app := &App{ Name: "my-app", Writer: &buf, Commands: []Command{ { Name: "my-command", CustomHelpTemplate: tt.template, }, }, } err := app.Run([]string{"my-app", "help", tt.command}) if err != nil { t.Fatal(err) } got := buf.String() if got != tt.wantOutput { t.Errorf("want output %q, got %q", tt.wantOutput, got) } }) } } func TestShowCommandHelp_CommandAliases(t *testing.T) { app := &App{ Commands: []Command{ { Name: "frobbly", Aliases: []string{"fr", "frob", "bork"}, Action: func(ctx *Context) error { return nil }, }, }, } output := &bytes.Buffer{} app.Writer = output _ = app.Run([]string{"foo", "help", "fr"}) if !strings.Contains(output.String(), "frobbly") { t.Errorf("expected output to include command name; got: %q", output.String()) } if strings.Contains(output.String(), "bork") { t.Errorf("expected output to exclude command aliases; got: %q", output.String()) } } func TestShowSubcommandHelp_CommandAliases(t *testing.T) { app := &App{ Commands: []Command{ { Name: "frobbly", Aliases: []string{"fr", "frob", "bork"}, Action: func(ctx *Context) error { return nil }, }, }, } output := &bytes.Buffer{} app.Writer = output _ = app.Run([]string{"foo", "help"}) if !strings.Contains(output.String(), "frobbly, fr, frob, bork") { t.Errorf("expected output to include all command aliases; got: %q", output.String()) } } func TestShowCommandHelp_Customtemplate(t *testing.T) { app := &App{ Commands: []Command{ { Name: "frobbly", Action: func(ctx *Context) error { return nil }, HelpName: "foo frobbly", CustomHelpTemplate: `NAME: {{.HelpName}} - {{.Usage}} USAGE: {{.HelpName}} [FLAGS] TARGET [TARGET ...] FLAGS: {{range .VisibleFlags}}{{.}} {{end}} EXAMPLES: 1. Frobbly runs with this param locally. $ {{.HelpName}} wobbly `, }, }, } output := &bytes.Buffer{} app.Writer = output _ = app.Run([]string{"foo", "help", "frobbly"}) if strings.Contains(output.String(), "2. Frobbly runs without this param locally.") { t.Errorf("expected output to exclude \"2. Frobbly runs without this param locally.\"; got: %q", output.String()) } if !strings.Contains(output.String(), "1. Frobbly runs with this param locally.") { t.Errorf("expected output to include \"1. Frobbly runs with this param locally.\"; got: %q", output.String()) } if !strings.Contains(output.String(), "$ foo frobbly wobbly") { t.Errorf("expected output to include \"$ foo frobbly wobbly\"; got: %q", output.String()) } } func TestShowSubcommandHelp_CommandUsageText(t *testing.T) { app := &App{ Commands: []Command{ { Name: "frobbly", UsageText: "this is usage text", }, }, } output := &bytes.Buffer{} app.Writer = output _ = app.Run([]string{"foo", "frobbly", "--help"}) if !strings.Contains(output.String(), "this is usage text") { t.Errorf("expected output to include usage text; got: %q", output.String()) } } func TestShowSubcommandHelp_SubcommandUsageText(t *testing.T) { app := &App{ Commands: []Command{ { Name: "frobbly", Subcommands: []Command{ { Name: "bobbly", UsageText: "this is usage text", }, }, }, }, } output := &bytes.Buffer{} app.Writer = output _ = app.Run([]string{"foo", "frobbly", "bobbly", "--help"}) if !strings.Contains(output.String(), "this is usage text") { t.Errorf("expected output to include usage text; got: %q", output.String()) } } func TestShowAppHelp_HiddenCommand(t *testing.T) { app := &App{ Commands: []Command{ { Name: "frobbly", Action: func(ctx *Context) error { return nil }, }, { Name: "secretfrob", Hidden: true, Action: func(ctx *Context) error { return nil }, }, }, } output := &bytes.Buffer{} app.Writer = output _ = app.Run([]string{"app", "--help"}) if strings.Contains(output.String(), "secretfrob") { t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String()) } if !strings.Contains(output.String(), "frobbly") { t.Errorf("expected output to include \"frobbly\"; got: %q", output.String()) } } func TestShowAppHelp_HelpPrinter(t *testing.T) { doublecho := func(text string) string { return text + " " + text } tests := []struct { name string template string printer helpPrinter wantTemplate string wantOutput string }{ { name: "standard-command", template: "", printer: func(w io.Writer, templ string, data interface{}) { fmt.Fprint(w, "yo") }, wantTemplate: AppHelpTemplate, wantOutput: "yo", }, { name: "custom-template-command", template: "{{doublecho .Name}}", printer: func(w io.Writer, templ string, data interface{}) { // Pass a custom function to ensure it gets used fm := map[string]interface{}{"doublecho": doublecho} printHelpCustom(w, templ, data, fm) }, wantTemplate: "{{doublecho .Name}}", wantOutput: "my-app my-app", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { defer func(old helpPrinter) { HelpPrinter = old }(HelpPrinter) HelpPrinter = func(w io.Writer, templ string, data interface{}) { if templ != tt.wantTemplate { t.Errorf("want template:\n%s\ngot template:\n%s", tt.wantTemplate, templ) } tt.printer(w, templ, data) } var buf bytes.Buffer app := &App{ Name: "my-app", Writer: &buf, CustomAppHelpTemplate: tt.template, } err := app.Run([]string{"my-app", "help"}) if err != nil { t.Fatal(err) } got := buf.String() if got != tt.wantOutput { t.Errorf("want output %q, got %q", tt.wantOutput, got) } }) } } func TestShowAppHelp_HelpPrinterCustom(t *testing.T) { doublecho := func(text string) string { return text + " " + text } tests := []struct { name string template string printer helpPrinterCustom wantTemplate string wantOutput string }{ { name: "standard-command", template: "", printer: func(w io.Writer, templ string, data interface{}, fm map[string]interface{}) { fmt.Fprint(w, "yo") }, wantTemplate: AppHelpTemplate, wantOutput: "yo", }, { name: "custom-template-command", template: "{{doublecho .Name}}", printer: func(w io.Writer, templ string, data interface{}, _ map[string]interface{}) { // Pass a custom function to ensure it gets used fm := map[string]interface{}{"doublecho": doublecho} printHelpCustom(w, templ, data, fm) }, wantTemplate: "{{doublecho .Name}}", wantOutput: "my-app my-app", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { defer func(old helpPrinterCustom) { HelpPrinterCustom = old }(HelpPrinterCustom) HelpPrinterCustom = func(w io.Writer, templ string, data interface{}, fm map[string]interface{}) { if fm != nil { t.Error("unexpected function map passed") } if templ != tt.wantTemplate { t.Errorf("want template:\n%s\ngot template:\n%s", tt.wantTemplate, templ) } tt.printer(w, templ, data, fm) } var buf bytes.Buffer app := &App{ Name: "my-app", Writer: &buf, CustomAppHelpTemplate: tt.template, } err := app.Run([]string{"my-app", "help"}) if err != nil { t.Fatal(err) } got := buf.String() if got != tt.wantOutput { t.Errorf("want output %q, got %q", tt.wantOutput, got) } }) } } func TestShowAppHelp_CustomAppTemplate(t *testing.T) { app := &App{ Commands: []Command{ { Name: "frobbly", Action: func(ctx *Context) error { return nil }, }, { Name: "secretfrob", Hidden: true, Action: func(ctx *Context) error { return nil }, }, }, ExtraInfo: func() map[string]string { platform := fmt.Sprintf("OS: %s | Arch: %s", runtime.GOOS, runtime.GOARCH) goruntime := fmt.Sprintf("Version: %s | CPUs: %d", runtime.Version(), runtime.NumCPU()) return map[string]string{ "PLATFORM": platform, "RUNTIME": goruntime, } }, CustomAppHelpTemplate: `NAME: {{.Name}} - {{.Usage}} USAGE: {{.Name}} {{if .VisibleFlags}}[FLAGS] {{end}}COMMAND{{if .VisibleFlags}} [COMMAND FLAGS | -h]{{end}} [ARGUMENTS...] COMMANDS: {{range .VisibleCommands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} {{end}}{{if .VisibleFlags}} GLOBAL FLAGS: {{range .VisibleFlags}}{{.}} {{end}}{{end}} VERSION: 2.0.0 {{"\n"}}{{range $key, $value := ExtraInfo}} {{$key}}: {{$value}} {{end}}`, } output := &bytes.Buffer{} app.Writer = output _ = app.Run([]string{"app", "--help"}) if strings.Contains(output.String(), "secretfrob") { t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String()) } if !strings.Contains(output.String(), "frobbly") { t.Errorf("expected output to include \"frobbly\"; got: %q", output.String()) } if !strings.Contains(output.String(), "PLATFORM:") || !strings.Contains(output.String(), "OS:") || !strings.Contains(output.String(), "Arch:") { t.Errorf("expected output to include \"PLATFORM:, OS: and Arch:\"; got: %q", output.String()) } if !strings.Contains(output.String(), "RUNTIME:") || !strings.Contains(output.String(), "Version:") || !strings.Contains(output.String(), "CPUs:") { t.Errorf("expected output to include \"RUNTIME:, Version: and CPUs:\"; got: %q", output.String()) } if !strings.Contains(output.String(), "VERSION:") || !strings.Contains(output.String(), "2.0.0") { t.Errorf("expected output to include \"VERSION:, 2.0.0\"; got: %q", output.String()) } } cli-1.22.4/helpers_test.go000066400000000000000000000011221364073550300154010ustar00rootroot00000000000000package cli import ( "os" "reflect" "runtime" "strings" "testing" ) var ( wd, _ = os.Getwd() ) func expect(t *testing.T, a interface{}, b interface{}) { _, fn, line, _ := runtime.Caller(1) fn = strings.Replace(fn, wd+"/", "", -1) if !reflect.DeepEqual(a, b) { t.Errorf("(%s:%d) Expected %v (type %v) - Got %v (type %v)", fn, line, b, reflect.TypeOf(b), a, reflect.TypeOf(a)) } } func refute(t *testing.T, a interface{}, b interface{}) { if reflect.DeepEqual(a, b) { t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) } } cli-1.22.4/helpers_unix_test.go000066400000000000000000000001761364073550300164540ustar00rootroot00000000000000// +build darwin dragonfly freebsd linux netbsd openbsd solaris package cli import "os" func clearenv() { os.Clearenv() } cli-1.22.4/helpers_windows_test.go000066400000000000000000000005711364073550300171620ustar00rootroot00000000000000package cli import ( "os" "syscall" ) // os.Clearenv() doesn't actually unset variables on Windows // See: https://github.com/golang/go/issues/17902 func clearenv() { for _, s := range os.Environ() { for j := 1; j < len(s); j++ { if s[j] == '=' { keyp, _ := syscall.UTF16PtrFromString(s[0:j]) syscall.SetEnvironmentVariable(keyp, nil) break } } } } cli-1.22.4/parse.go000066400000000000000000000046361364073550300140270ustar00rootroot00000000000000package cli import ( "flag" "strings" ) type iterativeParser interface { newFlagSet() (*flag.FlagSet, error) useShortOptionHandling() bool } // To enable short-option handling (e.g., "-it" vs "-i -t") we have to // iteratively catch parsing errors. This way we achieve LR parsing without // transforming any arguments. Otherwise, there is no way we can discriminate // combined short options from common arguments that should be left untouched. // Pass `shellComplete` to continue parsing options on failure during shell // completion when, the user-supplied options may be incomplete. func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComplete bool) error { for { err := set.Parse(args) if !ip.useShortOptionHandling() || err == nil { if shellComplete { return nil } return err } errStr := err.Error() trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: -") if errStr == trimmed { return err } // regenerate the initial args with the split short opts argsWereSplit := false for i, arg := range args { // skip args that are not part of the error message if name := strings.TrimLeft(arg, "-"); name != trimmed { continue } // if we can't split, the error was accurate shortOpts := splitShortOptions(set, arg) if len(shortOpts) == 1 { return err } // swap current argument with the split version args = append(args[:i], append(shortOpts, args[i+1:]...)...) argsWereSplit = true break } // This should be an impossible to reach code path, but in case the arg // splitting failed to happen, this will prevent infinite loops if !argsWereSplit { return err } // Since custom parsing failed, replace the flag set before retrying newSet, err := ip.newFlagSet() if err != nil { return err } *set = *newSet } } func splitShortOptions(set *flag.FlagSet, arg string) []string { shortFlagsExist := func(s string) bool { for _, c := range s[1:] { if f := set.Lookup(string(c)); f == nil { return false } } return true } if !isSplittable(arg) || !shortFlagsExist(arg) { return []string{arg} } separated := make([]string, 0, len(arg)-1) for _, flagChar := range arg[1:] { separated = append(separated, "-"+string(flagChar)) } return separated } func isSplittable(flagArg string) bool { return strings.HasPrefix(flagArg, "-") && !strings.HasPrefix(flagArg, "--") && len(flagArg) > 2 } cli-1.22.4/sort.go000066400000000000000000000010101364073550300136630ustar00rootroot00000000000000package cli import "unicode" // lexicographicLess compares strings alphabetically considering case. func lexicographicLess(i, j string) bool { iRunes := []rune(i) jRunes := []rune(j) lenShared := len(iRunes) if lenShared > len(jRunes) { lenShared = len(jRunes) } for index := 0; index < lenShared; index++ { ir := iRunes[index] jr := jRunes[index] if lir, ljr := unicode.ToLower(ir), unicode.ToLower(jr); lir != ljr { return lir < ljr } if ir != jr { return ir < jr } } return i < j } cli-1.22.4/sort_test.go000066400000000000000000000011131364073550300147260ustar00rootroot00000000000000package cli import "testing" var lexicographicLessTests = []struct { i string j string expected bool }{ {"", "a", true}, {"a", "", false}, {"a", "a", false}, {"a", "A", false}, {"A", "a", true}, {"aa", "a", false}, {"a", "aa", true}, {"a", "b", true}, {"a", "B", true}, {"A", "b", true}, {"A", "B", true}, } func TestLexicographicLess(t *testing.T) { for _, test := range lexicographicLessTests { actual := lexicographicLess(test.i, test.j) if test.expected != actual { t.Errorf(`expected string "%s" to come before "%s"`, test.i, test.j) } } } cli-1.22.4/template.go000066400000000000000000000072601364073550300145240ustar00rootroot00000000000000package cli // AppHelpTemplate is the text template for the Default help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. var AppHelpTemplate = `NAME: {{.Name}}{{if .Usage}} - {{.Usage}}{{end}} USAGE: {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} VERSION: {{.Version}}{{end}}{{end}}{{if .Description}} DESCRIPTION: {{.Description}}{{end}}{{if len .Authors}} AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: {{range $index, $author := .Authors}}{{if $index}} {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}} COMMANDS:{{range .VisibleCategories}}{{if .Name}} {{.Name}}:{{range .VisibleCommands}} {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} GLOBAL OPTIONS: {{range $index, $option := .VisibleFlags}}{{if $index}} {{end}}{{$option}}{{end}}{{end}}{{if .Copyright}} COPYRIGHT: {{.Copyright}}{{end}} ` // CommandHelpTemplate is the text template for the command help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. var CommandHelpTemplate = `NAME: {{.HelpName}} - {{.Usage}} USAGE: {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}} CATEGORY: {{.Category}}{{end}}{{if .Description}} DESCRIPTION: {{.Description}}{{end}}{{if .VisibleFlags}} OPTIONS: {{range .VisibleFlags}}{{.}} {{end}}{{end}} ` // SubcommandHelpTemplate is the text template for the subcommand help topic. // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. var SubcommandHelpTemplate = `NAME: {{.HelpName}} - {{if .Description}}{{.Description}}{{else}}{{.Usage}}{{end}} USAGE: {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}} COMMANDS:{{range .VisibleCategories}}{{if .Name}} {{.Name}}:{{range .VisibleCommands}} {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{range .VisibleCommands}} {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}} OPTIONS: {{range .VisibleFlags}}{{.}} {{end}}{{end}} ` var MarkdownDocTemplate = `% {{ .App.Name }}(8) {{ .App.Description }} % {{ .App.Author }} # NAME {{ .App.Name }}{{ if .App.Usage }} - {{ .App.Usage }}{{ end }} # SYNOPSIS {{ .App.Name }} {{ if .SynopsisArgs }} ` + "```" + ` {{ range $v := .SynopsisArgs }}{{ $v }}{{ end }}` + "```" + ` {{ end }}{{ if .App.UsageText }} # DESCRIPTION {{ .App.UsageText }} {{ end }} **Usage**: ` + "```" + ` {{ .App.Name }} [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] ` + "```" + ` {{ if .GlobalArgs }} # GLOBAL OPTIONS {{ range $v := .GlobalArgs }} {{ $v }}{{ end }} {{ end }}{{ if .Commands }} # COMMANDS {{ range $v := .Commands }} {{ $v }}{{ end }}{{ end }}` var FishCompletionTemplate = `# {{ .App.Name }} fish shell completion function __fish_{{ .App.Name }}_no_subcommand --description 'Test if there has been any subcommand yet' for i in (commandline -opc) if contains -- $i{{ range $v := .AllCommands }} {{ $v }}{{ end }} return 1 end end return 0 end {{ range $v := .Completions }}{{ $v }} {{ end }}` cli-1.22.4/testdata/000077500000000000000000000000001364073550300141665ustar00rootroot00000000000000cli-1.22.4/testdata/expected-doc-full.man000066400000000000000000000015671364073550300202000ustar00rootroot00000000000000.nh .TH greet(8) .SH Harrison .SH NAME .PP greet \- Some app .SH SYNOPSIS .PP greet .PP .RS .nf [\-\-another\-flag|\-b] [\-\-flag|\-\-fl|\-f]=[value] [\-\-socket|\-s]=[value] .fi .RE .SH DESCRIPTION .PP app [first\_arg] [second\_arg] .PP \fBUsage\fP: .PP .RS .nf greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] .fi .RE .SH GLOBAL OPTIONS .PP \fB\-\-another\-flag, \-b\fP: another usage text .PP \fB\-\-flag, \-\-fl, \-f\fP="": .PP \fB\-\-socket, \-s\fP="": some 'usage' text (default: value) .SH COMMANDS .SH config, c .PP another usage test .PP \fB\-\-another\-flag, \-b\fP: another usage text .PP \fB\-\-flag, \-\-fl, \-f\fP="": .SS sub\-config, s, ss .PP another usage test .PP \fB\-\-sub\-command\-flag, \-s\fP: some usage text .PP \fB\-\-sub\-flag, \-\-sub\-fl, \-s\fP="": .SH info, i, in .PP retrieve generic information .SH some\-commandcli-1.22.4/testdata/expected-doc-full.md000066400000000000000000000013271364073550300200170ustar00rootroot00000000000000% greet(8) % Harrison # NAME greet - Some app # SYNOPSIS greet ``` [--another-flag|-b] [--flag|--fl|-f]=[value] [--socket|-s]=[value] ``` # DESCRIPTION app [first_arg] [second_arg] **Usage**: ``` greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] ``` # GLOBAL OPTIONS **--another-flag, -b**: another usage text **--flag, --fl, -f**="": **--socket, -s**="": some 'usage' text (default: value) # COMMANDS ## config, c another usage test **--another-flag, -b**: another usage text **--flag, --fl, -f**="": ### sub-config, s, ss another usage test **--sub-command-flag, -s**: some usage text **--sub-flag, --sub-fl, -s**="": ## info, i, in retrieve generic information ## some-command cli-1.22.4/testdata/expected-doc-no-commands.md000066400000000000000000000006461364073550300212730ustar00rootroot00000000000000% greet(8) % Harrison # NAME greet - Some app # SYNOPSIS greet ``` [--another-flag|-b] [--flag|--fl|-f]=[value] [--socket|-s]=[value] ``` # DESCRIPTION app [first_arg] [second_arg] **Usage**: ``` greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] ``` # GLOBAL OPTIONS **--another-flag, -b**: another usage text **--flag, --fl, -f**="": **--socket, -s**="": some 'usage' text (default: value) cli-1.22.4/testdata/expected-doc-no-flags.md000066400000000000000000000007701364073550300205640ustar00rootroot00000000000000% greet(8) % Harrison # NAME greet - Some app # SYNOPSIS greet # DESCRIPTION app [first_arg] [second_arg] **Usage**: ``` greet [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] ``` # COMMANDS ## config, c another usage test **--another-flag, -b**: another usage text **--flag, --fl, -f**="": ### sub-config, s, ss another usage test **--sub-command-flag, -s**: some usage text **--sub-flag, --sub-fl, -s**="": ## info, i, in retrieve generic information ## some-command cli-1.22.4/testdata/expected-fish-full.fish000066400000000000000000000035331364073550300205350ustar00rootroot00000000000000# greet fish shell completion function __fish_greet_no_subcommand --description 'Test if there has been any subcommand yet' for i in (commandline -opc) if contains -- $i config c sub-config s ss info i in some-command return 1 end end return 0 end complete -c greet -n '__fish_greet_no_subcommand' -l socket -s s -r -d 'some \'usage\' text' complete -c greet -n '__fish_greet_no_subcommand' -f -l flag -s fl -s f -r complete -c greet -n '__fish_greet_no_subcommand' -f -l another-flag -s b -d 'another usage text' complete -c greet -n '__fish_greet_no_subcommand' -f -l help -s h -d 'show help' complete -c greet -n '__fish_greet_no_subcommand' -f -l version -s v -d 'print the version' complete -c greet -n '__fish_seen_subcommand_from config c' -f -l help -s h -d 'show help' complete -r -c greet -n '__fish_greet_no_subcommand' -a 'config c' -d 'another usage test' complete -c greet -n '__fish_seen_subcommand_from config c' -l flag -s fl -s f -r complete -c greet -n '__fish_seen_subcommand_from config c' -f -l another-flag -s b -d 'another usage text' complete -c greet -n '__fish_seen_subcommand_from sub-config s ss' -f -l help -s h -d 'show help' complete -r -c greet -n '__fish_seen_subcommand_from config c' -a 'sub-config s ss' -d 'another usage test' complete -c greet -n '__fish_seen_subcommand_from sub-config s ss' -f -l sub-flag -s sub-fl -s s -r complete -c greet -n '__fish_seen_subcommand_from sub-config s ss' -f -l sub-command-flag -s s -d 'some usage text' complete -c greet -n '__fish_seen_subcommand_from info i in' -f -l help -s h -d 'show help' complete -r -c greet -n '__fish_greet_no_subcommand' -a 'info i in' -d 'retrieve generic information' complete -c greet -n '__fish_seen_subcommand_from some-command' -f -l help -s h -d 'show help' complete -r -c greet -n '__fish_greet_no_subcommand' -a 'some-command'