Merge pull request #675 from jmccann/continue3

Ability to load variable from file - UPDATED
main
Jesse Szwedko 7 years ago committed by GitHub
commit 44cb242eeb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -32,6 +32,7 @@ applications in an expressive way.
+ [Alternate Names](#alternate-names) + [Alternate Names](#alternate-names)
+ [Ordering](#ordering) + [Ordering](#ordering)
+ [Values from the Environment](#values-from-the-environment) + [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) + [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others)
+ [Precedence](#precedence) + [Precedence](#precedence)
* [Subcommands](#subcommands) * [Subcommands](#subcommands)
@ -587,6 +588,41 @@ func main() {
} }
``` ```
#### Values from files
You can also have the default value set from file via `FilePath`. e.g.
<!-- {
"args": ["&#45;&#45;help"],
"output": "password for the mysql database"
} -->
``` go
package main
import (
"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",
},
}
app.Run(os.Args)
}
```
Note that default values set from file (e.g. `FilePath`) take precedence over
default values set from the enviornment (e.g. `EnvVar`).
#### Values from alternate input sources (YAML, TOML, and others) #### Values from alternate input sources (YAML, TOML, and others)
There is a separate package altsrc that adds support for getting flag values There is a separate package altsrc that adds support for getting flag values

@ -3,6 +3,7 @@ package cli
import ( import (
"errors" "errors"
"flag" "flag"
"os"
"reflect" "reflect"
"strings" "strings"
"syscall" "syscall"
@ -93,18 +94,26 @@ func (c *Context) IsSet(name string) bool {
val = val.Elem() val = val.Elem()
} }
envVarValue := val.FieldByName("EnvVar") filePathValue := val.FieldByName("FilePath")
if !envVarValue.IsValid() { if filePathValue.IsValid() {
return eachName(filePathValue.String(), func(filePath string) {
if _, err := os.Stat(filePath); err == nil {
c.setFlags[name] = true
return
}
})
} }
eachName(envVarValue.String(), func(envVar string) { envVarValue := val.FieldByName("EnvVar")
envVar = strings.TrimSpace(envVar) if envVarValue.IsValid() {
if _, ok := syscall.Getenv(envVar); ok { eachName(envVarValue.String(), func(envVar string) {
c.setFlags[name] = true envVar = strings.TrimSpace(envVar)
return if _, ok := syscall.Getenv(envVar); ok {
} c.setFlags[name] = true
}) return
}
})
}
}) })
} }
} }

@ -3,6 +3,7 @@ package cli
import ( import (
"flag" "flag"
"fmt" "fmt"
"io/ioutil"
"reflect" "reflect"
"runtime" "runtime"
"strconv" "strconv"
@ -45,6 +46,10 @@ var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames
// details. This is used by the default FlagStringer. // details. This is used by the default FlagStringer.
var FlagEnvHinter FlagEnvHintFunc = withEnvHint 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. // FlagsByName is a slice of Flag.
type FlagsByName []Flag type FlagsByName []Flag
@ -120,15 +125,9 @@ func (f GenericFlag) Apply(set *flag.FlagSet) {
// provided by the user for parsing by the flag // provided by the user for parsing by the flag
func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error { func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error {
val := f.Value val := f.Value
if f.EnvVar != "" { if fileEnvVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
for _, envVar := range strings.Split(f.EnvVar, ",") { if err := val.Set(fileEnvVal); err != nil {
envVar = strings.TrimSpace(envVar) return fmt.Errorf("could not parse %s as value for flag %s: %s", fileEnvVal, f.Name, err)
if envVal, ok := syscall.Getenv(envVar); ok {
if err := val.Set(envVal); err != nil {
return fmt.Errorf("could not parse %s as value for flag %s: %s", envVal, f.Name, err)
}
break
}
} }
} }
@ -171,21 +170,15 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) {
// ApplyWithError populates the flag given the flag set and environment // ApplyWithError populates the flag given the flag set and environment
func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error { func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error {
if f.EnvVar != "" { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
for _, envVar := range strings.Split(f.EnvVar, ",") { newVal := &StringSlice{}
envVar = strings.TrimSpace(envVar) for _, s := range strings.Split(envVal, ",") {
if envVal, ok := syscall.Getenv(envVar); ok { s = strings.TrimSpace(s)
newVal := &StringSlice{} if err := newVal.Set(s); err != nil {
for _, s := range strings.Split(envVal, ",") { return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err)
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)
}
}
f.Value = newVal
break
} }
} }
f.Value = newVal
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -234,21 +227,15 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) {
// ApplyWithError populates the flag given the flag set and environment // ApplyWithError populates the flag given the flag set and environment
func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error { func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error {
if f.EnvVar != "" { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
for _, envVar := range strings.Split(f.EnvVar, ",") { newVal := &IntSlice{}
envVar = strings.TrimSpace(envVar) for _, s := range strings.Split(envVal, ",") {
if envVal, ok := syscall.Getenv(envVar); ok { s = strings.TrimSpace(s)
newVal := &IntSlice{} if err := newVal.Set(s); err != nil {
for _, s := range strings.Split(envVal, ",") { return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err)
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)
}
}
f.Value = newVal
break
} }
} }
f.Value = newVal
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -297,21 +284,15 @@ func (f Int64SliceFlag) Apply(set *flag.FlagSet) {
// ApplyWithError populates the flag given the flag set and environment // ApplyWithError populates the flag given the flag set and environment
func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error { func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
if f.EnvVar != "" { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
for _, envVar := range strings.Split(f.EnvVar, ",") { newVal := &Int64Slice{}
envVar = strings.TrimSpace(envVar) for _, s := range strings.Split(envVal, ",") {
if envVal, ok := syscall.Getenv(envVar); ok { s = strings.TrimSpace(s)
newVal := &Int64Slice{} if err := newVal.Set(s); err != nil {
for _, s := range strings.Split(envVal, ",") { return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err)
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)
}
}
f.Value = newVal
break
} }
} }
f.Value = newVal
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -332,23 +313,15 @@ func (f BoolFlag) Apply(set *flag.FlagSet) {
// ApplyWithError populates the flag given the flag set and environment // ApplyWithError populates the flag given the flag set and environment
func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error { func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error {
val := false val := false
if f.EnvVar != "" { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
for _, envVar := range strings.Split(f.EnvVar, ",") { if envVal == "" {
envVar = strings.TrimSpace(envVar) val = false
if envVal, ok := syscall.Getenv(envVar); ok { } else {
if envVal == "" { envValBool, err := strconv.ParseBool(envVal)
val = false if err != nil {
break return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
}
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
break
} }
val = envValBool
} }
} }
@ -372,23 +345,16 @@ func (f BoolTFlag) Apply(set *flag.FlagSet) {
// ApplyWithError populates the flag given the flag set and environment // ApplyWithError populates the flag given the flag set and environment
func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error { func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error {
val := true val := true
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal, ok := syscall.Getenv(envVar); ok {
if envVal == "" {
val = false
break
}
envValBool, err := strconv.ParseBool(envVal) if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
if err != nil { if envVal == "" {
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err) val = false
} } else {
envValBool, err := strconv.ParseBool(envVal)
val = envValBool if err != nil {
break return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
} }
val = envValBool
} }
} }
@ -411,14 +377,8 @@ func (f StringFlag) Apply(set *flag.FlagSet) {
// ApplyWithError populates the flag given the flag set and environment // ApplyWithError populates the flag given the flag set and environment
func (f StringFlag) ApplyWithError(set *flag.FlagSet) error { func (f StringFlag) ApplyWithError(set *flag.FlagSet) error {
if f.EnvVar != "" { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
for _, envVar := range strings.Split(f.EnvVar, ",") { f.Value = envVal
envVar = strings.TrimSpace(envVar)
if envVal, ok := syscall.Getenv(envVar); ok {
f.Value = envVal
break
}
}
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -440,18 +400,12 @@ func (f IntFlag) Apply(set *flag.FlagSet) {
// ApplyWithError populates the flag given the flag set and environment // ApplyWithError populates the flag given the flag set and environment
func (f IntFlag) ApplyWithError(set *flag.FlagSet) error { func (f IntFlag) ApplyWithError(set *flag.FlagSet) error {
if f.EnvVar != "" { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
for _, envVar := range strings.Split(f.EnvVar, ",") { envValInt, err := strconv.ParseInt(envVal, 0, 64)
envVar = strings.TrimSpace(envVar) if err != nil {
if envVal, ok := syscall.Getenv(envVar); ok { return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err)
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)
break
}
} }
f.Value = int(envValInt)
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -473,19 +427,13 @@ func (f Int64Flag) Apply(set *flag.FlagSet) {
// ApplyWithError populates the flag given the flag set and environment // ApplyWithError populates the flag given the flag set and environment
func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error { func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error {
if f.EnvVar != "" { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
for _, envVar := range strings.Split(f.EnvVar, ",") { envValInt, err := strconv.ParseInt(envVal, 0, 64)
envVar = strings.TrimSpace(envVar) if err != nil {
if envVal, ok := syscall.Getenv(envVar); ok { return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err)
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
break
}
} }
f.Value = envValInt
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -507,19 +455,13 @@ func (f UintFlag) Apply(set *flag.FlagSet) {
// ApplyWithError populates the flag given the flag set and environment // ApplyWithError populates the flag given the flag set and environment
func (f UintFlag) ApplyWithError(set *flag.FlagSet) error { func (f UintFlag) ApplyWithError(set *flag.FlagSet) error {
if f.EnvVar != "" { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
for _, envVar := range strings.Split(f.EnvVar, ",") { envValInt, err := strconv.ParseUint(envVal, 0, 64)
envVar = strings.TrimSpace(envVar) if err != nil {
if envVal, ok := syscall.Getenv(envVar); ok { return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err)
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)
break
}
} }
f.Value = uint(envValInt)
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -541,19 +483,13 @@ func (f Uint64Flag) Apply(set *flag.FlagSet) {
// ApplyWithError populates the flag given the flag set and environment // ApplyWithError populates the flag given the flag set and environment
func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error { func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error {
if f.EnvVar != "" { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
for _, envVar := range strings.Split(f.EnvVar, ",") { envValInt, err := strconv.ParseUint(envVal, 0, 64)
envVar = strings.TrimSpace(envVar) if err != nil {
if envVal, ok := syscall.Getenv(envVar); ok { return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err)
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 = uint64(envValInt)
break
}
} }
f.Value = uint64(envValInt)
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -575,19 +511,13 @@ func (f DurationFlag) Apply(set *flag.FlagSet) {
// ApplyWithError populates the flag given the flag set and environment // ApplyWithError populates the flag given the flag set and environment
func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error { func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error {
if f.EnvVar != "" { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
for _, envVar := range strings.Split(f.EnvVar, ",") { envValDuration, err := time.ParseDuration(envVal)
envVar = strings.TrimSpace(envVar) if err != nil {
if envVal, ok := syscall.Getenv(envVar); ok { return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err)
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
break
}
} }
f.Value = envValDuration
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -609,19 +539,13 @@ func (f Float64Flag) Apply(set *flag.FlagSet) {
// ApplyWithError populates the flag given the flag set and environment // ApplyWithError populates the flag given the flag set and environment
func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error { func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error {
if f.EnvVar != "" { if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
for _, envVar := range strings.Split(f.EnvVar, ",") { envValFloat, err := strconv.ParseFloat(envVal, 10)
envVar = strings.TrimSpace(envVar) if err != nil {
if envVal, ok := syscall.Getenv(envVar); ok { return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err)
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 = float64(envValFloat)
break
}
} }
f.Value = float64(envValFloat)
} }
eachName(f.Name, func(name string) { eachName(f.Name, func(name string) {
@ -705,6 +629,14 @@ func withEnvHint(envVar, str string) string {
return str + envText 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 { func flagValue(f Flag) reflect.Value {
fv := reflect.ValueOf(f) fv := reflect.ValueOf(f)
for fv.Kind() == reflect.Ptr { for fv.Kind() == reflect.Ptr {
@ -718,14 +650,29 @@ func stringifyFlag(f Flag) string {
switch f.(type) { switch f.(type) {
case IntSliceFlag: case IntSliceFlag:
return FlagEnvHinter(fv.FieldByName("EnvVar").String(), return FlagFileHinter(
stringifyIntSliceFlag(f.(IntSliceFlag))) fv.FieldByName("FilePath").String(),
FlagEnvHinter(
fv.FieldByName("EnvVar").String(),
stringifyIntSliceFlag(f.(IntSliceFlag)),
),
)
case Int64SliceFlag: case Int64SliceFlag:
return FlagEnvHinter(fv.FieldByName("EnvVar").String(), return FlagFileHinter(
stringifyInt64SliceFlag(f.(Int64SliceFlag))) fv.FieldByName("FilePath").String(),
FlagEnvHinter(
fv.FieldByName("EnvVar").String(),
stringifyInt64SliceFlag(f.(Int64SliceFlag)),
),
)
case StringSliceFlag: case StringSliceFlag:
return FlagEnvHinter(fv.FieldByName("EnvVar").String(), return FlagFileHinter(
stringifyStringSliceFlag(f.(StringSliceFlag))) fv.FieldByName("FilePath").String(),
FlagEnvHinter(
fv.FieldByName("EnvVar").String(),
stringifyStringSliceFlag(f.(StringSliceFlag)),
),
)
} }
placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String()) placeholder, usage := unquoteUsage(fv.FieldByName("Usage").String())
@ -752,8 +699,13 @@ func stringifyFlag(f Flag) string {
usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString)) usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultValueString))
return FlagEnvHinter(fv.FieldByName("EnvVar").String(), return FlagFileHinter(
fmt.Sprintf("%s\t%s", FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder), usageWithDefault)) fv.FieldByName("FilePath").String(),
FlagEnvHinter(
fv.FieldByName("EnvVar").String(),
fmt.Sprintf("%s\t%s", FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder), usageWithDefault),
),
)
} }
func stringifyIntSliceFlag(f IntSliceFlag) string { func stringifyIntSliceFlag(f IntSliceFlag) string {
@ -805,3 +757,18 @@ func stringifySliceFlag(usage, name string, defaultVals []string) string {
usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal)) usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal))
return fmt.Sprintf("%s\t%s", FlagNamePrefixer(name, placeholder), usageWithDefault) return fmt.Sprintf("%s\t%s", FlagNamePrefixer(name, placeholder), 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
}

@ -13,6 +13,7 @@ type BoolFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Destination *bool Destination *bool
} }
@ -60,6 +61,7 @@ type BoolTFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Destination *bool Destination *bool
} }
@ -107,6 +109,7 @@ type DurationFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Value time.Duration Value time.Duration
Destination *time.Duration Destination *time.Duration
@ -155,6 +158,7 @@ type Float64Flag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Value float64 Value float64
Destination *float64 Destination *float64
@ -200,11 +204,12 @@ func lookupFloat64(name string, set *flag.FlagSet) float64 {
// GenericFlag is a flag with type Generic // GenericFlag is a flag with type Generic
type GenericFlag struct { type GenericFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
Hidden bool FilePath string
Value Generic Hidden bool
Value Generic
} }
// String returns a readable representation of this value // String returns a readable representation of this value
@ -250,6 +255,7 @@ type Int64Flag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Value int64 Value int64
Destination *int64 Destination *int64
@ -298,6 +304,7 @@ type IntFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Value int Value int
Destination *int Destination *int
@ -343,11 +350,12 @@ func lookupInt(name string, set *flag.FlagSet) int {
// IntSliceFlag is a flag with type *IntSlice // IntSliceFlag is a flag with type *IntSlice
type IntSliceFlag struct { type IntSliceFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
Hidden bool FilePath string
Value *IntSlice Hidden bool
Value *IntSlice
} }
// String returns a readable representation of this value // String returns a readable representation of this value
@ -390,11 +398,12 @@ func lookupIntSlice(name string, set *flag.FlagSet) []int {
// Int64SliceFlag is a flag with type *Int64Slice // Int64SliceFlag is a flag with type *Int64Slice
type Int64SliceFlag struct { type Int64SliceFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
Hidden bool FilePath string
Value *Int64Slice Hidden bool
Value *Int64Slice
} }
// String returns a readable representation of this value // String returns a readable representation of this value
@ -440,6 +449,7 @@ type StringFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Value string Value string
Destination *string Destination *string
@ -485,11 +495,12 @@ func lookupString(name string, set *flag.FlagSet) string {
// StringSliceFlag is a flag with type *StringSlice // StringSliceFlag is a flag with type *StringSlice
type StringSliceFlag struct { type StringSliceFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
Hidden bool FilePath string
Value *StringSlice Hidden bool
Value *StringSlice
} }
// String returns a readable representation of this value // String returns a readable representation of this value
@ -535,6 +546,7 @@ type Uint64Flag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Value uint64 Value uint64
Destination *uint64 Destination *uint64
@ -583,6 +595,7 @@ type UintFlag struct {
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
Value uint Value uint
Destination *uint Destination *uint

@ -2,6 +2,8 @@ package cli
import ( import (
"fmt" "fmt"
"io"
"io/ioutil"
"os" "os"
"reflect" "reflect"
"regexp" "regexp"
@ -1290,3 +1292,38 @@ func TestParseGenericFromEnvCascade(t *testing.T) {
} }
a.Run([]string{"run"}) a.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)
}
}
}

@ -39,3 +39,6 @@ type FlagNamePrefixFunc func(fullName, placeholder string) string
// with the environment variable details. // with the environment variable details.
type FlagEnvHintFunc func(envVar, str string) string 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

@ -142,6 +142,7 @@ def _write_cli_flag_types(outfile, types):
Name string Name string
Usage string Usage string
EnvVar string EnvVar string
FilePath string
Hidden bool Hidden bool
""".format(**typedef)) """.format(**typedef))

Loading…
Cancel
Save