Merge pull request #675 from jmccann/continue3
Ability to load variable from file - UPDATED
This commit is contained in:
commit
44cb242eeb
36
README.md
36
README.md
@ -32,6 +32,7 @@ applications in an expressive way.
|
||||
+ [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)
|
||||
@ -587,6 +588,41 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
#### Values from files
|
||||
|
||||
You can also have the default value set from file via `FilePath`. e.g.
|
||||
|
||||
<!-- {
|
||||
"args": ["--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)
|
||||
|
||||
There is a separate package altsrc that adds support for getting flag values
|
||||
|
29
context.go
29
context.go
@ -3,6 +3,7 @@ package cli
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"syscall"
|
||||
@ -93,18 +94,26 @@ func (c *Context) IsSet(name string) bool {
|
||||
val = val.Elem()
|
||||
}
|
||||
|
||||
envVarValue := val.FieldByName("EnvVar")
|
||||
if !envVarValue.IsValid() {
|
||||
return
|
||||
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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
eachName(envVarValue.String(), func(envVar string) {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if _, ok := syscall.Getenv(envVar); ok {
|
||||
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
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
299
flag.go
299
flag.go
@ -3,6 +3,7 @@ package cli
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@ -45,6 +46,10 @@ var FlagNamePrefixer FlagNamePrefixFunc = prefixedNames
|
||||
// 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
|
||||
|
||||
@ -120,15 +125,9 @@ func (f GenericFlag) Apply(set *flag.FlagSet) {
|
||||
// provided by the user for parsing by the flag
|
||||
func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
val := f.Value
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
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
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,21 +170,15 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
||||
|
||||
// ApplyWithError populates the flag given the flag set and environment
|
||||
func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal, ok := syscall.Getenv(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)
|
||||
}
|
||||
}
|
||||
f.Value = newVal
|
||||
break
|
||||
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)
|
||||
}
|
||||
}
|
||||
f.Value = newVal
|
||||
}
|
||||
|
||||
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
|
||||
func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal, ok := syscall.Getenv(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)
|
||||
}
|
||||
}
|
||||
f.Value = newVal
|
||||
break
|
||||
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)
|
||||
}
|
||||
}
|
||||
f.Value = newVal
|
||||
}
|
||||
|
||||
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
|
||||
func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal, ok := syscall.Getenv(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)
|
||||
}
|
||||
}
|
||||
f.Value = newVal
|
||||
break
|
||||
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)
|
||||
}
|
||||
}
|
||||
f.Value = newVal
|
||||
}
|
||||
|
||||
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
|
||||
func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
val := false
|
||||
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 err != nil {
|
||||
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
|
||||
}
|
||||
|
||||
val = envValBool
|
||||
break
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -372,23 +345,16 @@ func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
||||
// ApplyWithError populates the flag given the flag set and environment
|
||||
func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
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 err != nil {
|
||||
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
|
||||
}
|
||||
|
||||
val = envValBool
|
||||
break
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -411,14 +377,8 @@ func (f StringFlag) Apply(set *flag.FlagSet) {
|
||||
|
||||
// ApplyWithError populates the flag given the flag set and environment
|
||||
func (f StringFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal, ok := syscall.Getenv(envVar); ok {
|
||||
f.Value = envVal
|
||||
break
|
||||
}
|
||||
}
|
||||
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
|
||||
f.Value = envVal
|
||||
}
|
||||
|
||||
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
|
||||
func (f IntFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal, ok := syscall.Getenv(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)
|
||||
break
|
||||
}
|
||||
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) {
|
||||
@ -473,19 +427,13 @@ func (f Int64Flag) Apply(set *flag.FlagSet) {
|
||||
|
||||
// ApplyWithError populates the flag given the flag set and environment
|
||||
func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal, ok := syscall.Getenv(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
|
||||
break
|
||||
}
|
||||
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) {
|
||||
@ -507,19 +455,13 @@ func (f UintFlag) Apply(set *flag.FlagSet) {
|
||||
|
||||
// ApplyWithError populates the flag given the flag set and environment
|
||||
func (f UintFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal, ok := syscall.Getenv(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)
|
||||
break
|
||||
}
|
||||
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) {
|
||||
@ -541,19 +483,13 @@ func (f Uint64Flag) Apply(set *flag.FlagSet) {
|
||||
|
||||
// ApplyWithError populates the flag given the flag set and environment
|
||||
func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal, ok := syscall.Getenv(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 = uint64(envValInt)
|
||||
break
|
||||
}
|
||||
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 = uint64(envValInt)
|
||||
}
|
||||
|
||||
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
|
||||
func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal, ok := syscall.Getenv(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
|
||||
break
|
||||
}
|
||||
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) {
|
||||
@ -609,19 +539,13 @@ func (f Float64Flag) Apply(set *flag.FlagSet) {
|
||||
|
||||
// ApplyWithError populates the flag given the flag set and environment
|
||||
func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error {
|
||||
if f.EnvVar != "" {
|
||||
for _, envVar := range strings.Split(f.EnvVar, ",") {
|
||||
envVar = strings.TrimSpace(envVar)
|
||||
if envVal, ok := syscall.Getenv(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 = float64(envValFloat)
|
||||
break
|
||||
}
|
||||
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 = float64(envValFloat)
|
||||
}
|
||||
|
||||
eachName(f.Name, func(name string) {
|
||||
@ -705,6 +629,14 @@ func withEnvHint(envVar, str string) string {
|
||||
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 {
|
||||
@ -718,14 +650,29 @@ func stringifyFlag(f Flag) string {
|
||||
|
||||
switch f.(type) {
|
||||
case IntSliceFlag:
|
||||
return FlagEnvHinter(fv.FieldByName("EnvVar").String(),
|
||||
stringifyIntSliceFlag(f.(IntSliceFlag)))
|
||||
return FlagFileHinter(
|
||||
fv.FieldByName("FilePath").String(),
|
||||
FlagEnvHinter(
|
||||
fv.FieldByName("EnvVar").String(),
|
||||
stringifyIntSliceFlag(f.(IntSliceFlag)),
|
||||
),
|
||||
)
|
||||
case Int64SliceFlag:
|
||||
return FlagEnvHinter(fv.FieldByName("EnvVar").String(),
|
||||
stringifyInt64SliceFlag(f.(Int64SliceFlag)))
|
||||
return FlagFileHinter(
|
||||
fv.FieldByName("FilePath").String(),
|
||||
FlagEnvHinter(
|
||||
fv.FieldByName("EnvVar").String(),
|
||||
stringifyInt64SliceFlag(f.(Int64SliceFlag)),
|
||||
),
|
||||
)
|
||||
case StringSliceFlag:
|
||||
return FlagEnvHinter(fv.FieldByName("EnvVar").String(),
|
||||
stringifyStringSliceFlag(f.(StringSliceFlag)))
|
||||
return FlagFileHinter(
|
||||
fv.FieldByName("FilePath").String(),
|
||||
FlagEnvHinter(
|
||||
fv.FieldByName("EnvVar").String(),
|
||||
stringifyStringSliceFlag(f.(StringSliceFlag)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
return FlagEnvHinter(fv.FieldByName("EnvVar").String(),
|
||||
fmt.Sprintf("%s\t%s", FlagNamePrefixer(fv.FieldByName("Name").String(), placeholder), usageWithDefault))
|
||||
return FlagFileHinter(
|
||||
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 {
|
||||
@ -805,3 +757,18 @@ func stringifySliceFlag(usage, name string, defaultVals []string) string {
|
||||
usageWithDefault := strings.TrimSpace(fmt.Sprintf("%s%s", usage, defaultVal))
|
||||
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
|
||||
Usage string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Hidden bool
|
||||
Destination *bool
|
||||
}
|
||||
@ -60,6 +61,7 @@ type BoolTFlag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Hidden bool
|
||||
Destination *bool
|
||||
}
|
||||
@ -107,6 +109,7 @@ type DurationFlag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Hidden bool
|
||||
Value time.Duration
|
||||
Destination *time.Duration
|
||||
@ -155,6 +158,7 @@ type Float64Flag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Hidden bool
|
||||
Value float64
|
||||
Destination *float64
|
||||
@ -200,11 +204,12 @@ func lookupFloat64(name string, set *flag.FlagSet) float64 {
|
||||
|
||||
// GenericFlag is a flag with type Generic
|
||||
type GenericFlag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
Hidden bool
|
||||
Value Generic
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Hidden bool
|
||||
Value Generic
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
@ -250,6 +255,7 @@ type Int64Flag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Hidden bool
|
||||
Value int64
|
||||
Destination *int64
|
||||
@ -298,6 +304,7 @@ type IntFlag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Hidden bool
|
||||
Value int
|
||||
Destination *int
|
||||
@ -343,11 +350,12 @@ func lookupInt(name string, set *flag.FlagSet) int {
|
||||
|
||||
// IntSliceFlag is a flag with type *IntSlice
|
||||
type IntSliceFlag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
Hidden bool
|
||||
Value *IntSlice
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Hidden bool
|
||||
Value *IntSlice
|
||||
}
|
||||
|
||||
// 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
|
||||
type Int64SliceFlag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
Hidden bool
|
||||
Value *Int64Slice
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Hidden bool
|
||||
Value *Int64Slice
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
@ -440,6 +449,7 @@ type StringFlag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Hidden bool
|
||||
Value string
|
||||
Destination *string
|
||||
@ -485,11 +495,12 @@ func lookupString(name string, set *flag.FlagSet) string {
|
||||
|
||||
// StringSliceFlag is a flag with type *StringSlice
|
||||
type StringSliceFlag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
Hidden bool
|
||||
Value *StringSlice
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Hidden bool
|
||||
Value *StringSlice
|
||||
}
|
||||
|
||||
// String returns a readable representation of this value
|
||||
@ -535,6 +546,7 @@ type Uint64Flag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Hidden bool
|
||||
Value uint64
|
||||
Destination *uint64
|
||||
@ -583,6 +595,7 @@ type UintFlag struct {
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Hidden bool
|
||||
Value uint
|
||||
Destination *uint
|
||||
|
37
flag_test.go
37
flag_test.go
@ -2,6 +2,8 @@ package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
@ -1290,3 +1292,38 @@ func TestParseGenericFromEnvCascade(t *testing.T) {
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
3
funcs.go
3
funcs.go
@ -39,3 +39,6 @@ type FlagNamePrefixFunc func(fullName, placeholder string) string
|
||||
// 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
|
||||
|
@ -142,6 +142,7 @@ def _write_cli_flag_types(outfile, types):
|
||||
Name string
|
||||
Usage string
|
||||
EnvVar string
|
||||
FilePath string
|
||||
Hidden bool
|
||||
""".format(**typedef))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user