cli icon indicating copy to clipboard operation
cli copied to clipboard

altsrc with YAML: Flag is Never Assigned

Open sshaw opened this issue 4 years ago • 6 comments

How to use a YAML config file to set flags? I've read the docs and tried a couple of things; then waited a month and read them again but, nothing seems to work.

I'm using v2.3.0.

package cli

var Flags []cli.Flag

func init() {
        Flags = []cli.Flag{
                altsrc.NewStringFlag(
                        &cli.StringFlag{
                                Required: true,
                                Name:    "foo",
                        }
                )
        }
}
package main

// In main()
app := &cli.App{
        Name: "tester",
        Before: altsrc.InitInputSourceWithContext(cmd.Flags,
                func(context *cli.Context) (altsrc.InputSourceContext, error) {
                        return altsrc.NewYamlSourceFromFile(".myconfig")
                }
        )
}

But every time I run it I get an error because foo is required and not set.

I have also tried what sounds like a more verbose version of the above:

app := &cli.App{
        Name: "sdt",
        Before: func (c *cli.Context) error {
                src, err := altsrc.NewYamlSourceFromFile(".myconfig")
                if err != nil {
                        return err
                }

                fmt.Printf("%+v\n", src)

                err = altsrc.ApplyInputSourceValues(c, src, cmd.Flags)
                if err != nil {
                        return err
                }

                return nil
        },
}

The config is printed and everything is there but the flag is never assigned.

I thought it may be related to #1086 but this appears to be fixed in v2.3.0. Help!

sshaw avatar Mar 06 '21 20:03 sshaw

This issue or PR has been automatically marked as stale because it has not had recent activity. Please add a comment bumping this if you're still interested in it's resolution! Thanks for your help, please let us know if you need anything else.

stale[bot] avatar Jun 05 '21 00:06 stale[bot]

Still interested in its resolution

sshaw avatar Jun 05 '21 00:06 sshaw

This issue or PR has been bumped and is no longer marked as stale! Feel free to bump it again in the future, if it's still relevant.

stale[bot] avatar Jun 05 '21 00:06 stale[bot]

This issue or PR has been automatically marked as stale because it has not had recent activity. Please add a comment bumping this if you're still interested in it's resolution! Thanks for your help, please let us know if you need anything else.

stale[bot] avatar Sep 04 '21 23:09 stale[bot]

Still interested in its resolution

sshaw avatar Sep 04 '21 23:09 sshaw

This issue or PR has been bumped and is no longer marked as stale! Feel free to bump it again in the future, if it's still relevant.

stale[bot] avatar Sep 04 '21 23:09 stale[bot]

Here's an simple example on v2.20.2:

main.go

package main

import (
	"fmt"
	"os"

	"github.com/urfave/cli/v2"
	"github.com/urfave/cli/v2/altsrc"
)

var (
	logfile string
	verbose int
)

func main() {
	flags := []cli.Flag{
		&cli.StringFlag{
			Name:  "config",
			Usage: "yaml config file name",
		},
		altsrc.NewStringFlag(
			&cli.StringFlag{
				Name:        "logfile",
				Aliases:     []string{"log.file"}, // match config file structure
				Value:       "",
				Usage:       "log to file",
				Destination: &logfile,
			},
		),
		altsrc.NewIntFlag(
			&cli.IntFlag{
				Name:        "verbose",
				Aliases:     []string{"log.verbose"},
				Value:       0,
				Destination: &verbose,
			},
		),
	}

	app := &cli.App{
		Flags:  flags,
		Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("config")),
		Action: func(ctx *cli.Context) error {
			fmt.Printf("config file: %s, log {file: %s, verbose: %d}\n", ctx.String("config"), logfile, verbose)
			return nil
		},
	}

	fmt.Println(app.Run(os.Args))
}

example.yml

log:
  verbose: 1
  file: log.txt

Aliases attributes are used to specify mapping between flag and config file structure.

Archwizard avatar Oct 19 '22 03:10 Archwizard

@Archwizard The code you have posted works

$ cat example.yml 
log:
  verbose: 1
  file: log.txt
$ go run main.go --config example.yml
config file: example.yml, log {file: log.txt, verbose: 1}
<nil>

dearchap avatar Oct 19 '22 15:10 dearchap

@sshaw Do you have a complete example to share ?

dearchap avatar Oct 19 '22 15:10 dearchap

Thanks @Archwizard. The Aliases matching the log file structure are key, though a bit ugly considering they're now also command-line options.

@sshaw Do you have a complete example to share ?

@dearchap is this not a compete example?

sshaw avatar Oct 22 '22 03:10 sshaw

@sshaw You need to provide Flag to app as well

	app := &cli.App{
		Name:  "tester",
		Flags: Flags,
		Before: altsrc.InitInputSourceWithContext(Flags,
			func(context *cli.Context) (altsrc.InputSourceContext, error) {
				return altsrc.NewYamlSourceFromFile(".myconfig")
			}),
	}

dearchap avatar Jan 15 '23 17:01 dearchap

I hope that the Aliases stuff can be mentioned in the documents...

dreamerlzl avatar Jan 09 '24 13:01 dreamerlzl