Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: urfave/cli
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.7.1
Choose a base ref
...
head repository: urfave/cli
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v2.7.2
Choose a head ref

Commits on Nov 3, 2020

  1. Report the source of a value when we cannot parse it

    If you allow a flag to be set from environment variables or files and
    a parse error occurs from one of them, it is very useful for the error
    message to mention where the value came from.
    
    Without this, it can be difficult to notice an error caused by an
    unexpected environment variable being set.
    
    Implements #1167.
    mostynb committed Nov 3, 2020
    Copy the full SHA
    500d6b0 View commit details

Commits on Nov 7, 2020

  1. fixup! Report the source of a value when we cannot parse it

    move bool to the end of the return arguments
    
    remove "from " prefix in the source/fromWhere description
    
    remove TODO notes from functions that don't currently perform error checking
    mostynb committed Nov 7, 2020
    Copy the full SHA
    cdc1f6e View commit details

Commits on Apr 19, 2022

  1. Simplify zsh completion

    Completion file shouldn't be sourced. It should provide only completion
    code (source of _command) for command. It's task for package manager or
    user to put under $fpath.
    z0rc authored and meatballhat committed Apr 19, 2022
    Copy the full SHA
    f89647b View commit details
  2. Properly detect Zsh shell

    There is no need to define custom shell var, when Zsh can be detected by
    checking SHELL env var.
    z0rc authored and meatballhat committed Apr 19, 2022
    Copy the full SHA
    1150c2e View commit details
  3. Merge branch 'report_source_of_parse_errors' of ssh://github.com/most…

    …ynb/cli into mostynb-report_source_of_parse_errors
    meatballhat committed Apr 19, 2022
    Copy the full SHA
    826b3ed View commit details

Commits on Apr 22, 2022

  1. Add a security policy document

    Closes #1342
    meatballhat committed Apr 22, 2022
    Copy the full SHA
    f1d0b0e View commit details
  2. Copy the full SHA
    de39f01 View commit details
  3. Copy the full SHA
    849e7f0 View commit details

Commits on Apr 26, 2022

  1. Copy the full SHA
    b42bf69 View commit details
  2. Copy the full SHA
    1bef031 View commit details

Commits on Apr 30, 2022

  1. Copy the full SHA
    56837b0 View commit details
  2. Copy the full SHA
    f3ef95f View commit details
  3. Copy the full SHA
    e7db6af View commit details

Commits on May 7, 2022

  1. Copy the full SHA
    6c811a8 View commit details
  2. Copy the full SHA
    04f5ff0 View commit details
  3. Copy the full SHA
    31d6090 View commit details
  4. Copy the full SHA
    32be625 View commit details

Commits on May 22, 2022

  1. Copy the full SHA
    e66ce91 View commit details
  2. Copy the full SHA
    e66017d View commit details
  3. Merge pull request #1350 from urfave/applying-pr1218

    Another approach for zsh completion (#1218)
    meatballhat authored May 22, 2022
    Copy the full SHA
    f528cf0 View commit details
  4. Copy the full SHA
    fc27cb0 View commit details
  5. Merge pull request #1354 from urfave/mostynb-report_source_of_parse_e…

    …rrors
    
    Merging mostynb report source of parse errors
    meatballhat authored May 22, 2022
    Copy the full SHA
    60a6bf5 View commit details
  6. Merge pull request #1365 from urfave/security-policy-doc

    Add a security policy document
    meatballhat authored May 22, 2022
    Copy the full SHA
    939ab7f View commit details
Showing with 103 additions and 69 deletions.
  1. +6 −5 CODE_OF_CONDUCT.md
  2. +5 −2 app_test.go
  3. +2 −5 autocomplete/zsh_autocomplete
  4. +27 −0 docs/SECURITY.md
  5. +5 −6 docs/v2/manual.md
  6. +8 −5 flag.go
  7. +2 −2 flag_bool.go
  8. +2 −2 flag_duration.go
  9. +2 −2 flag_float64.go
  10. +2 −2 flag_float64_slice.go
  11. +2 −2 flag_generic.go
  12. +2 −2 flag_int.go
  13. +2 −2 flag_int64.go
  14. +2 −2 flag_int64_slice.go
  15. +2 −2 flag_int_slice.go
  16. +1 −1 flag_path.go
  17. +1 −1 flag_string.go
  18. +2 −2 flag_string_slice.go
  19. +17 −17 flag_test.go
  20. +2 −2 flag_timestamp.go
  21. +2 −2 flag_uint.go
  22. +2 −2 flag_uint64.go
  23. +1 −1 help.go
  24. +4 −0 help_test.go
11 changes: 6 additions & 5 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -55,11 +55,12 @@ further defined and clarified by project maintainers.
## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting Dan Buch at dan@meatballhat.com. All complaints will be
reviewed and investigated and will result in a response that is deemed necessary
and appropriate to the circumstances. The project team is obligated to maintain
confidentiality with regard to the reporter of an incident. Further details of
specific enforcement policies may be posted separately.
reported by contacting urfave-governance@googlegroups.com, a members-only group
that is world-postable. All complaints will be reviewed and investigated and
will result in a response that is deemed necessary and appropriate to the
circumstances. The project team is obligated to maintain confidentiality with
regard to the reporter of an incident. Further details of specific enforcement
policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
7 changes: 5 additions & 2 deletions app_test.go
Original file line number Diff line number Diff line change
@@ -228,6 +228,7 @@ func ExampleApp_Run_subcommandNoAction() {
}

func ExampleApp_Run_bashComplete_withShortFlag() {
os.Setenv("SHELL", "bash")
os.Args = []string{"greet", "-", "--generate-bash-completion"}

app := NewApp()
@@ -255,6 +256,7 @@ func ExampleApp_Run_bashComplete_withShortFlag() {
}

func ExampleApp_Run_bashComplete_withLongFlag() {
os.Setenv("SHELL", "bash")
os.Args = []string{"greet", "--s", "--generate-bash-completion"}

app := NewApp()
@@ -283,6 +285,7 @@ func ExampleApp_Run_bashComplete_withLongFlag() {
// --similar-flag
}
func ExampleApp_Run_bashComplete_withMultipleLongFlag() {
os.Setenv("SHELL", "bash")
os.Args = []string{"greet", "--st", "--generate-bash-completion"}

app := NewApp()
@@ -315,7 +318,7 @@ func ExampleApp_Run_bashComplete_withMultipleLongFlag() {
}

func ExampleApp_Run_bashComplete() {
// set args for examples sake
os.Setenv("SHELL", "bash")
os.Args = []string{"greet", "--generate-bash-completion"}

app := &App{
@@ -355,7 +358,7 @@ func ExampleApp_Run_bashComplete() {
func ExampleApp_Run_zshComplete() {
// set args for examples sake
os.Args = []string{"greet", "--generate-bash-completion"}
_ = os.Setenv("_CLI_ZSH_AUTOCOMPLETE_HACK", "1")
_ = os.Setenv("SHELL", "/usr/bin/zsh")

app := NewApp()
app.Name = "greet"
7 changes: 2 additions & 5 deletions autocomplete/zsh_autocomplete
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
#compdef $PROG

_cli_zsh_autocomplete() {

local -a opts
local cur
cur=${words[-1]}
if [[ "$cur" == "-"* ]]; then
opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}")
opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}")
else
opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-bash-completion)}")
opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-bash-completion)}")
fi

if [[ "${opts[1]}" != "" ]]; then
_describe 'values' opts
else
_files
fi

return
}

compdef _cli_zsh_autocomplete $PROG
27 changes: 27 additions & 0 deletions docs/SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Security Policy

Hello and thank you for your interest in the `urfave/cli` security
policy! :tada: :lock:

## Supported Versions

| Version | Supported |
| ------------ | ------------------------------------- |
| `>= v2.3.x` | :white_check_mark: |
| `< v2.3` | :x: |
| `>= v1.22.x` | :white_check_mark: :lady_beetle: [^1] |
| `< v1.22` | :x: |

## Reporting a Vulnerability

Please disclose any vulnerabilities by sending an email to:

[urfave-security@googlegroups.com](mailto:urfave-security@googlegroups.com)

You should expect a response within 48 hours and further
communications to be decided via email. The `urfave/cli` maintainer
team comprises volunteers who contribute when possible, so please
have patience :bow:

[^1]: The `v1.22.x` series will receive bug fixes and security
patches only.
11 changes: 5 additions & 6 deletions docs/v2/manual.md
Original file line number Diff line number Diff line change
@@ -1165,15 +1165,14 @@ func main() {
```

#### ZSH Support
Auto-completion for ZSH is also supported using the `autocomplete/zsh_autocomplete`
file included in this repo. Two environment variables are used, `PROG` and `_CLI_ZSH_AUTOCOMPLETE_HACK`.
Set `PROG` to the program name as before, set `_CLI_ZSH_AUTOCOMPLETE_HACK` to `1`, and
then `source path/to/autocomplete/zsh_autocomplete`. Adding the following lines to your ZSH
configuration file (usually `.zshrc`) will allow the auto-completion to persist across new shells:
Auto-completion for ZSH is also supported using the `autocomplete/zsh_autocomplete`
file included in this repo. One environment variable is used, `PROG`. Set
`PROG` to the program name as before, and then `source path/to/autocomplete/zsh_autocomplete`.
Adding the following lines to your ZSH configuration file (usually `.zshrc`)
will allow the auto-completion to persist across new shells:

```
PROG=<myprogram>
_CLI_ZSH_AUTOCOMPLETE_HACK=1
source path/to/autocomplete/zsh_autocomplete
```
#### ZSH default auto-complete example
13 changes: 8 additions & 5 deletions flag.go
Original file line number Diff line number Diff line change
@@ -386,21 +386,24 @@ func hasFlag(flags []Flag, fl Flag) bool {
return false
}

func flagFromEnvOrFile(envVars []string, filePath string) (val string, ok bool) {
// Return the first value from a list of environment variables and files
// (which may or may not exist), a description of where the value was found,
// and a boolean which is true if a value was found.
func flagFromEnvOrFile(envVars []string, filePath string) (value string, fromWhere string, found bool) {
for _, envVar := range envVars {
envVar = strings.TrimSpace(envVar)
if val, ok := syscall.Getenv(envVar); ok {
return val, true
if value, found := syscall.Getenv(envVar); found {
return value, fmt.Sprintf("environment variable %q", envVar), true
}
}
for _, fileVar := range strings.Split(filePath, ",") {
if fileVar != "" {
if data, err := ioutil.ReadFile(fileVar); err == nil {
return string(data), true
return string(data), fmt.Sprintf("file %q", filePath), true
}
}
}
return "", false
return "", "", false
}

func flagSplitMultiValues(val string) []string {
4 changes: 2 additions & 2 deletions flag_bool.go
Original file line number Diff line number Diff line change
@@ -37,12 +37,12 @@ func (f *BoolFlag) GetEnvVars() []string {

// Apply populates the flag given the flag set and environment
func (f *BoolFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
valBool, err := strconv.ParseBool(val)

if err != nil {
return fmt.Errorf("could not parse %q as bool value for flag %s: %s", val, f.Name, err)
return fmt.Errorf("could not parse %q as bool value from %s for flag %s: %s", val, source, f.Name, err)
}

f.Value = valBool
4 changes: 2 additions & 2 deletions flag_duration.go
Original file line number Diff line number Diff line change
@@ -37,12 +37,12 @@ func (f *DurationFlag) GetEnvVars() []string {

// Apply populates the flag given the flag set and environment
func (f *DurationFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
valDuration, err := time.ParseDuration(val)

if err != nil {
return fmt.Errorf("could not parse %q as duration value for flag %s: %s", val, f.Name, err)
return fmt.Errorf("could not parse %q as duration value from %s for flag %s: %s", val, source, f.Name, err)
}

f.Value = valDuration
4 changes: 2 additions & 2 deletions flag_float64.go
Original file line number Diff line number Diff line change
@@ -37,11 +37,11 @@ func (f *Float64Flag) GetEnvVars() []string {

// Apply populates the flag given the flag set and environment
func (f *Float64Flag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
valFloat, err := strconv.ParseFloat(val, 64)
if err != nil {
return fmt.Errorf("could not parse %q as float64 value for flag %s: %s", val, f.Name, err)
return fmt.Errorf("could not parse %q as float64 value from %s for flag %s: %s", val, source, f.Name, err)
}

f.Value = valFloat
4 changes: 2 additions & 2 deletions flag_float64_slice.go
Original file line number Diff line number Diff line change
@@ -115,13 +115,13 @@ func (f *Float64SliceFlag) GetEnvVars() []string {

// Apply populates the flag given the flag set and environment
func (f *Float64SliceFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
f.Value = &Float64Slice{}

for _, s := range flagSplitMultiValues(val) {
if err := f.Value.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as float64 slice value for flag %s: %s", f.Value, f.Name, err)
return fmt.Errorf("could not parse %q as float64 slice value from %s for flag %s: %s", f.Value, source, f.Name, err)
}
}

4 changes: 2 additions & 2 deletions flag_generic.go
Original file line number Diff line number Diff line change
@@ -46,10 +46,10 @@ func (f *GenericFlag) GetEnvVars() []string {
// Apply takes the flagset and calls Set on the generic flag with the value
// provided by the user for parsing by the flag
func (f GenericFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
if err := f.Value.Set(val); err != nil {
return fmt.Errorf("could not parse %q as value for flag %s: %s", val, f.Name, err)
return fmt.Errorf("could not parse %q from %s as value for flag %s: %s", val, source, f.Name, err)
}

f.HasBeenSet = true
4 changes: 2 additions & 2 deletions flag_int.go
Original file line number Diff line number Diff line change
@@ -37,12 +37,12 @@ func (f *IntFlag) GetEnvVars() []string {

// Apply populates the flag given the flag set and environment
func (f *IntFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
valInt, err := strconv.ParseInt(val, 0, 64)

if err != nil {
return fmt.Errorf("could not parse %q as int value for flag %s: %s", val, f.Name, err)
return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err)
}

f.Value = int(valInt)
4 changes: 2 additions & 2 deletions flag_int64.go
Original file line number Diff line number Diff line change
@@ -37,12 +37,12 @@ func (f *Int64Flag) GetEnvVars() []string {

// Apply populates the flag given the flag set and environment
func (f *Int64Flag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if val != "" {
valInt, err := strconv.ParseInt(val, 0, 64)

if err != nil {
return fmt.Errorf("could not parse %q as int value for flag %s: %s", val, f.Name, err)
return fmt.Errorf("could not parse %q as int value from %s for flag %s: %s", val, source, f.Name, err)
}

f.Value = valInt
4 changes: 2 additions & 2 deletions flag_int64_slice.go
Original file line number Diff line number Diff line change
@@ -116,12 +116,12 @@ func (f *Int64SliceFlag) GetEnvVars() []string {

// Apply populates the flag given the flag set and environment
func (f *Int64SliceFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
f.Value = &Int64Slice{}

for _, s := range flagSplitMultiValues(val) {
if err := f.Value.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as int64 slice value for flag %s: %s", val, f.Name, err)
return fmt.Errorf("could not parse %q as int64 slice value from %s for flag %s: %s", val, source, f.Name, err)
}
}

4 changes: 2 additions & 2 deletions flag_int_slice.go
Original file line number Diff line number Diff line change
@@ -127,12 +127,12 @@ func (f *IntSliceFlag) GetEnvVars() []string {

// Apply populates the flag given the flag set and environment
func (f *IntSliceFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
f.Value = &IntSlice{}

for _, s := range flagSplitMultiValues(val) {
if err := f.Value.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as int slice value for flag %s: %s", val, f.Name, err)
return fmt.Errorf("could not parse %q as int slice value from %s for flag %s: %s", val, source, f.Name, err)
}
}

2 changes: 1 addition & 1 deletion flag_path.go
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ func (f *PathFlag) GetEnvVars() []string {

// Apply populates the flag given the flag set and environment
func (f *PathFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
f.Value = val
f.HasBeenSet = true
}
2 changes: 1 addition & 1 deletion flag_string.go
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ func (f *StringFlag) GetEnvVars() []string {

// Apply populates the flag given the flag set and environment
func (f *StringFlag) Apply(set *flag.FlagSet) error {
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
f.Value = val
f.HasBeenSet = true
}
4 changes: 2 additions & 2 deletions flag_string_slice.go
Original file line number Diff line number Diff line change
@@ -117,7 +117,7 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {

}

if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found {
if f.Value == nil {
f.Value = &StringSlice{}
}
@@ -128,7 +128,7 @@ func (f *StringSliceFlag) Apply(set *flag.FlagSet) error {

for _, s := range flagSplitMultiValues(val) {
if err := destination.Set(strings.TrimSpace(s)); err != nil {
return fmt.Errorf("could not parse %q as string value for flag %s: %s", val, f.Name, err)
return fmt.Errorf("could not parse %q as string value from %s for flag %s: %s", val, source, f.Name, err)
}
}

Loading