diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 169d3f1..89cd822 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -46,19 +46,20 @@ jobs: chmod +x $GOPATH/bin/gfmrun npm install markdown-toc - - name: Run Tests (v1) - if: contains(github.base_ref, 'v1') + - name: Run Tests run: | go run build.go vet go run build.go test + + - name: Run Tests (v1) + if: contains(github.base_ref, 'v1') + run: | 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 diff --git a/context.go b/context.go index 5b59e75..c0c526f 100644 --- a/context.go +++ b/context.go @@ -28,6 +28,9 @@ func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { if parentCtx != nil { c.Context = parentCtx.Context c.shellComplete = parentCtx.shellComplete + if parentCtx.flagSet == nil { + parentCtx.flagSet = &flag.FlagSet{} + } } c.Command = &Command{} diff --git a/context_test.go b/context_test.go index 3cefcca..ccf8846 100644 --- a/context_test.go +++ b/context_test.go @@ -344,6 +344,81 @@ func TestContextPropagation(t *testing.T) { } } +func TestContextAttributeAccessing(t *testing.T) { + tdata := []struct { + testCase string + setBoolInput string + ctxBoolInput string + newContextInput *Context + }{ + { + testCase: "empty", + setBoolInput: "", + ctxBoolInput: "", + newContextInput: nil, + }, + { + testCase: "empty_with_background_context", + setBoolInput: "", + ctxBoolInput: "", + newContextInput: &Context{Context: context.Background()}, + }, + { + testCase: "empty_set_bool_and_present_ctx_bool", + setBoolInput: "", + ctxBoolInput: "ctx-bool", + newContextInput: nil, + }, + { + testCase: "present_set_bool_and_present_ctx_bool_with_background_context", + setBoolInput: "", + ctxBoolInput: "ctx-bool", + newContextInput: &Context{Context: context.Background()}, + }, + { + testCase: "present_set_bool_and_present_ctx_bool", + setBoolInput: "ctx-bool", + ctxBoolInput: "ctx-bool", + newContextInput: nil, + }, + { + testCase: "present_set_bool_and_present_ctx_bool_with_background_context", + setBoolInput: "ctx-bool", + ctxBoolInput: "ctx-bool", + newContextInput: &Context{Context: context.Background()}, + }, + { + testCase: "present_set_bool_and_different_ctx_bool", + setBoolInput: "ctx-bool", + ctxBoolInput: "not-ctx-bool", + newContextInput: nil, + }, + { + testCase: "present_set_bool_and_different_ctx_bool_with_background_context", + setBoolInput: "ctx-bool", + ctxBoolInput: "not-ctx-bool", + newContextInput: &Context{Context: context.Background()}, + }, + } + + for _, test := range tdata { + t.Run(test.testCase, func(t *testing.T) { + // setup + set := flag.NewFlagSet("some-flag-set-name", 0) + set.Bool(test.setBoolInput, false, "usage documentation") + ctx := NewContext(nil, set, test.newContextInput) + + // logic under test + value := ctx.Bool(test.ctxBoolInput) + + // assertions + if value != false { + t.Errorf("expected \"value\" to be false, but it was not") + } + }) + } +} + func TestCheckRequiredFlags(t *testing.T) { tdata := []struct { testCase string diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ceb381f..383507d 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,6 +6,12 @@ View [unreleased 2.X] series changes. +## [2.1.1] - 2019-12-24 + +### Fixed + +* Fixed a `Context` regression introduced in `v2.1.0` in [urfave/cli/pull/1014](https://github.com/urfave/cli/pull/1014) via [@lynncyrin](https://github.com/lynncyrin) + ## [2.1.0] - 2019-12-24 These release notes were written for the git hash [ae84df4cef4a2a6f1a0cb1d41ea0f3af8755e5a8](https://github.com/urfave/cli/tree/ae84df4cef4a2a6f1a0cb1d41ea0f3af8755e5a8) @@ -541,7 +547,8 @@ signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`. ### Added - Initial implementation. -[unreleased 2.X]: https://github.com/urfave/cli/compare/v2.1.0...HEAD +[unreleased 2.X]: https://github.com/urfave/cli/compare/v2.1.1...HEAD +[2.1.1]: https://github.com/urfave/cli/compare/v2.1.0...v2.1.1 [2.1.0]: https://github.com/urfave/cli/compare/v2.0.0...v2.1.0 [2.0.0]: https://github.com/urfave/cli/compare/v1.22.2...v2.0.0 diff --git a/docs/v2/manual.md b/docs/v2/manual.md index 48179e0..2a9bdc4 100644 --- a/docs/v2/manual.md +++ b/docs/v2/manual.md @@ -13,6 +13,7 @@ cli v2 manual + [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) + + [Required Flags](#required-flags) + [Default Values for help output](#default-values-for-help-output) + [Precedence](#precedence) * [Subcommands](#subcommands) @@ -641,6 +642,63 @@ func main() { } ``` +#### Required Flags + +You can make a flag required by setting the `Required` field to `true`. If a user +does not provide a required flag, they will be shown an error message. + +Take for example this app that reqiures the `lang` flag: + + +```go +package main + +import ( + "log" + "os" + "strings" + + "github.com/urfave/cli" +) + +func main() { + app := cli.NewApp() + + app.Flags = []cli.Flag { + cli.StringFlag{ + Name: "lang", + Value: "english", + Usage: "language for the greeting", + Required: true, + }, + } + + app.Action = func(c *cli.Context) error { + var output string + if c.String("lang") == "spanish" { + output = "Hola" + } else { + output = "Hello" + } + fmt.Println(output) + return nil + } + + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } +} +``` + +If the app is run without the `lang` flag, the user will see the following message + +``` +Required flag "lang" not set +``` + #### Default Values for help output Sometimes it's useful to specify a flag's default help-text value within the flag declaration. This can be useful if the default value for a flag is a computed value. The default value can be set via the `DefaultText` struct field. @@ -1447,13 +1505,15 @@ func main() { cli.ShowVersion(c) 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) - } + // // uncomment when https://github.com/urfave/cli/pull/1014 is released + // if c.Bool("infinite") { + // c.App.Run([]string{"app", "doo", "wop"}) + // } + + // // uncomment when https://github.com/urfave/cli/pull/1014 is released + // 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()) @@ -1469,28 +1529,29 @@ func main() { 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.Bool("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.FlagNames()) - fmt.Printf("%#v\n", nc.IsSet("wat")) - fmt.Printf("%#v\n", nc.Set("wat", "nope")) - fmt.Printf("%#v\n", nc.NArg()) - fmt.Printf("%#v\n", nc.NumFlags()) - fmt.Printf("%#v\n", nc.Lineage()[1]) - + // // uncomment when https://github.com/urfave/cli/pull/1014 is released + // fmt.Printf("%#v\n", nc.Args()) + // fmt.Printf("%#v\n", nc.Bool("nope")) + // fmt.Printf("%#v\n", !nc.Bool("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")) + + // // uncomment when https://github.com/urfave/cli/pull/1014 is released + // fmt.Printf("%#v\n", nc.FlagNames()) + // fmt.Printf("%#v\n", nc.IsSet("wat")) + // fmt.Printf("%#v\n", nc.Set("wat", "nope")) + // fmt.Printf("%#v\n", nc.NArg()) + // fmt.Printf("%#v\n", nc.NumFlags()) + // fmt.Printf("%#v\n", nc.Lineage()[1]) nc.Set("wat", "also-nope") ec := cli.Exit("ohwell", 86)