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)
|
+ [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": ["--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
|
||||||
|
29
context.go
29
context.go
@ -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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
299
flag.go
299
flag.go
@ -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
|
||||||
|
37
flag_test.go
37
flag_test.go
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
3
funcs.go
3
funcs.go
@ -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…
Reference in New Issue
Block a user