{"id":15676823,"url":"https://github.com/bwplotka/flagarize","last_synced_at":"2025-05-07T00:29:16.814Z","repository":{"id":48079072,"uuid":"247264433","full_name":"bwplotka/flagarize","owner":"bwplotka","description":"Flagarize your Go struct to initialize your even complex struct from flags! 🚀","archived":false,"fork":false,"pushed_at":"2021-08-08T11:25:25.000Z","size":149,"stargazers_count":13,"open_issues_count":8,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-31T04:32:27.073Z","etag":null,"topics":["configuration","go","golang","kingpin","struct"],"latest_commit_sha":null,"homepage":"https://bwplotka.dev/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bwplotka.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-03-14T11:33:51.000Z","updated_at":"2023-08-16T19:57:28.000Z","dependencies_parsed_at":"2022-08-12T18:10:26.248Z","dependency_job_id":null,"html_url":"https://github.com/bwplotka/flagarize","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bwplotka%2Fflagarize","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bwplotka%2Fflagarize/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bwplotka%2Fflagarize/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bwplotka%2Fflagarize/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bwplotka","download_url":"https://codeload.github.com/bwplotka/flagarize/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252790441,"owners_count":21804595,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["configuration","go","golang","kingpin","struct"],"created_at":"2024-10-03T16:05:48.373Z","updated_at":"2025-05-07T00:29:16.795Z","avatar_url":"https://github.com/bwplotka.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# flagarize\n[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go\u0026logoColor=white\u0026style=flat-square)](https://pkg.go.dev/github.com/bwplotka/flagarize)\n[![Latest Release](https://img.shields.io/github/release/bwplotka/flagarize.svg?style=flat-square)](https://github.com/bwplotka/flagarize/releases/latest)\n[![CI](https://github.com/bwplotka/flagarize/workflows/test/badge.svg)](https://github.com/bwplotka/flagarize/actions?query=workflow%3Atest)\n[![Go Report Card](https://goreportcard.com/badge/github.com/bwplotka/flagarize)](https://goreportcard.com/report/github.com/bwplotka/flagarize)\n\nFlagarize your Go struct to initialize your even complex CLI config struct from flags!\n\n**NOTE: This project is in maintainance mode. This means that until new maintainer will be willing to support it, no additional features will be added. The project was fun to create and is fully functional, but benefits are not strong enough to justify lack of type safety her (thus lack of autocomplection).**\n\n## Goals\n\n* Allow flag parsing for any struct field using Go struct tags.\n* Minimal dependencies: Only `\"gopkg.in/alecthomas/kingpin.v2\"`.\n* Extensible with [custom types](#custom-type-parsing) and [custom flagarizing](#custom-flags).\n* Native supports for all [kingpin](https://github.com/alecthomas/kingpin) flag types and more like [`regexp`](./regexp.go) , [`pathorcontent`](./pathorcontent.go), [`timeorduration`](./timeorduration.go).\n\n## Requirements:\n\n* Go 1.3+\n* `gopkg.in/alecthomas/kingpin.v2`\n\n\n## Usage\n\nFor each field of the configuration struct that you want to register as a flag, add ``flagarize:\"\u003ckey=value,\u003e\"`` struct tag.\n\n### Flagarize tags\n\nFlagarize struct tag expected value to be map where key=values are separated by `|` (can be configured via `WithElemSep` function.)\n\n**Available keys:**\n\n* `name`: Name of the flag. If empty field name will be used and parsed to different case (e.g `FooBar` field will be `foo-bar`)\n* `help`: Usage description for the flag. If empty, value from string `\u003cFieldName\u003eFlagarizeHelp` field in the same struct will be used.\n* `hidden`: Optional. if `true` flag will be hidden.\n* `required`: Optional. if `true` flag will be required.\n* `default`: Optional. Value will be used as a value if the flag is not specified. Otherwise default value for type will be used.\n* `envvar`: Optional. Name of environment variable if needed next to the flag.\n* `short`: Optional. Short single character for a flag name alternative.\n* `placeholder` Optional. Flag placeholder for expected type.\n\nShort tag example:\n\n```go\ntype config struct {\n   Field1 string `flagarize:\"name=case9|help=help|hidden=true|required=true|default=some|envvar=LOL|short=z|placeholder=\u003csomething2\u003e\"`\n}\n```\n\n### Supported types\n\nWithout extensions flagarize supports all kingpin supported types plus few more. For current supported types it's best to\nsee `TestFlagarize_OK` unit test [here](flagarize_ext_test.go).\n\n### Example\n\nSee below example for usage:\n\n```go\nfunc main() {\n\t// Create new kingpin app as usual.\n\ta := kingpin.New(filepath.Base(os.Args[0]), \"\u003cYour CLI description\u003e\")\n\n\t// Define you own config.\n    type ComponentAOptions struct {\n\t    Field1 []string `flagarize:\"name=a.flag1|help=Help for field 1 in nested struct for component A.\"`\n    }\n\ttype ConfigForCLI struct {\n\t\tField1     string                   `flagarize:\"name=flag1|help=Help for field 1.|default=something\"`\n\t\tField2     *url.URL                 `flagarize:\"name=flag2|help=Help for field 2.|placeholder=\u003cURL\u003e\"`\n\t\tField3     int                      `flagarize:\"name=flag3|help=Help for field 3.|default=2144\"`\n\t\tField4     flagarize.TimeOrDuration `flagarize:\"name=flag4|help=Help for field 4.|default=1m|placeholder=\u003ctime or duration\u003e\"`\n\t\tComponentA ComponentAOptions\n\n\t\tNotFromFlags int\n\t}\n\n\t// You can define some fields as usual as well.\n\tvar notInConfigField time.Duration\n\ta.Flag(\"some-field10\", \"Help for some help which is defined outside of ConfigForCLI struct.\").\n\t\tDurationVar(\u0026notInConfigField)\n\n\t// Create new config.\n\tcfg := \u0026ConfigForCLI{}\n\n\t// Flagarize your config! (Register flags from config). Parse the flags afterwards.\n\tif err := flagarize.Flagarize(a, cfg); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tif _, err := a.Parse(os.Args[1:]); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Config is filled with values from flags!\n\t_ = cfg.Field1\n\n    // Run your command...\n}\n```\n\n## But Bartek, such Go projects already exist!\n\nYes, but not as simple, not focused on kingpin, and they does not allow custom flagarazing!  🤗\n\n## But Bartek, normal flag registration is enough, don't overengineer!\n\nWell, depends. It might get quite weird. Here is how it could look with and without flagarize:\n\n### Without\n\n```go\ncfg := struct {\n    configFile string\n\n    localStoragePath    string\n    notifier            notifier.Options\n    notifierTimeout     model.Duration\n    forGracePeriod      model.Duration\n    outageTolerance     model.Duration\n    resendDelay         model.Duration\n    web                 web.Options\n    tsdb                tsdbOptions\n    lookbackDelta       model.Duration\n    webTimeout          model.Duration\n    queryTimeout        model.Duration\n    queryConcurrency    int\n    queryMaxSamples     int\n    RemoteFlushDeadline model.Duration\n\n    prometheusURL   string\n    corsRegexString string\n\n    promlogConfig promlog.Config\n}{}\n\na := kingpin.New(filepath.Base(os.Args[0]), \"The Prometheus monitoring server\")\na.Version(version.Print(\"prometheus\"))\na.HelpFlag.Short('h')\n\na.Flag(\"config.file\", \"Prometheus configuration file path.\").\n    Default(\"prometheus.yml\").StringVar(\u0026cfg.configFile)\n\na.Flag(\"web.listen-address\", \"Address to listen on for UI, API, and telemetry.\").\n    Default(\"0.0.0.0:9090\").StringVar(\u0026cfg.web.ListenAddress)\n\na.Flag(\"web.read-timeout\",\n    \"Maximum duration before timing out read of the request, and closing idle connections.\").\n    Default(\"5m\").SetValue(\u0026cfg.webTimeout)\n\na.Flag(\"web.max-connections\", \"Maximum number of simultaneous connections.\").\n    Default(\"512\").IntVar(\u0026cfg.web.MaxConnections)\n\na.Flag(\"web.external-url\",\n    \"The URL under which Prometheus is externally reachable (for example, if Prometheus is served via a reverse proxy). Used for generating relative and absolute links back to Prometheus itself. If the URL has a path portion, it will be used to prefix all HTTP endpoints served by Prometheus. If omitted, relevant URL components will be derived automatically.\").\n    PlaceHolder(\"\u003cURL\u003e\").StringVar(\u0026cfg.prometheusURL)\n\na.Flag(\"web.route-prefix\",\n    \"Prefix for the internal routes of web endpoints. Defaults to path of --web.external-url.\").\n    PlaceHolder(\"\u003cpath\u003e\").StringVar(\u0026cfg.web.RoutePrefix)\n\na.Flag(\"web.user-assets\", \"Path to static asset directory, available at /user.\").\n    PlaceHolder(\"\u003cpath\u003e\").StringVar(\u0026cfg.web.UserAssetsPath)\n\na.Flag(\"web.enable-lifecycle\", \"Enable shutdown and reload via HTTP request.\").\n    Default(\"false\").BoolVar(\u0026cfg.web.EnableLifecycle)\n\na.Flag(\"web.enable-admin-api\", \"Enable API endpoints for admin control actions.\").\n    Default(\"false\").BoolVar(\u0026cfg.web.EnableAdminAPI)\n\na.Flag(\"web.console.templates\", \"Path to the console template directory, available at /consoles.\").\n    Default(\"consoles\").StringVar(\u0026cfg.web.ConsoleTemplatesPath)\n\n//... Thousands more of that.\n\nif _, err := a.Parse(os.Args[1:]); err != nil {\n    fmt.Fprintln(os.Stderr, errors.Wrapf(err, \"Error parsing commandline arguments\"))\n    a.Usage(os.Args[1:])\n    os.Exit(2)\n}\n```\n\n### With flagarize\n\n```go\ncfg := struct {\n    ConfigFile           string         `flagarize:\"name=config.file|help=Prometheus configuration file path.|default=prometheus.yml\"`\n    ExternalURL          string         `flagarize:\"name=web.external-url|help=The URL under which Prometheus is externally reachable (for example, if Prometheus is served via a reverse proxy). Used for generating relative and absolute links back to Prometheus itself. If the URL has a path portion, it will be used to prefix all HTTP endpoints served by Prometheus. If omitted, relevant URL components will be derived automatically.|placeholder=\u003cURL\u003e\"`\n    StoragePath          string         `flagarize:\"name=storage.tsdb.path|help=Base path for metrics storage.|default=data/\"`\n    RemoteFlushDeadline  model.Duration `flagarize:\"name=storage.remote.flush-deadline|help=How long to wait flushing sample on shutdown or config reload.|default=1m|placeholder=\u003cduration\u003e\"`\n    RulesOutageTolerance model.Duration `flagarize:\"name=rules.alert.for-outage-tolerance|help=Max time to tolerate prometheus outage for restoring \\\"for\\\" state of alert.|default=1h\"`\n    RulesForGracePeriod  model.Duration `flagarize:\"name=rules.alert.for-grace-period|help=Minimum duration between alert and restored \\\"for\\\" state. This is maintained only for alerts with configured \\\"for\\\" time greater than grace period.|default=10m\"`\n    RulesResendDelay     model.Duration `flagarize:\"name=rules.alert.resend-delay|help=Minimum amount of time to wait before resending an alert to Alertmanager.|default=1m\"`\n    LookbackDelta        model.Duration `flagarize:\"name=query.lookback-delta|help=The maximum lookback duration for retrieving metrics during expression evaluations and federation.|default=5m\"`\n    QueryTimeout         model.Duration `flagarize:\"name=query.timeout|help=Maximum time a query may take before being aborted.|default=2m\"`\n    QueryConcurrency     int            `flagarize:\"name=query.max-concurrency|help=Maximum number of queries executed concurrently.|default=20\"`\n    QueryMaxSamples      int            `flagarize:\"name=query.max-samples|help=Maximum number of samples a single query can load into memory. Note that queries will fail if they try to load more samples than this into memory, so this also limits the number of samples a query can return.|default=50000000\"`\n\n    Web      web.Options\n    Notifier notifier.Options\n    TSDB     tsdbOptions\n    PromLog  promlog.Config\n}{}\n\na := kingpin.New(filepath.Base(os.Args[0]), \"The Prometheus monitoring server\")\na.Version(version.Print(\"prometheus\"))\na.HelpFlag.Short('h')\n\nif err := flagarize.Flagarize(a, \u0026cfg); err != nil {\n    fmt.Fprintln(os.Stderr, err)\n    os.Exit(2)\n}\n\nif _, err := a.Parse(os.Args[1:]); err != nil {\n    fmt.Fprintln(os.Stderr, errors.Wrapf(err, \"Error parsing commandline arguments\"))\n    a.Usage(os.Args[1:])\n    os.Exit(2)\n}\n\n// Done!\n```\n\nMuch cleaner (:\n\n## Custom Type Parsing\n\nFlagarize allows parsing of native types like int, string, etc (all that kingpin supports). For custom\ntypes it's enough if your type implements part of `kingping.Value` interface as follows:\n\n```go\n// ValueFlagarizer is the simplest way to extend flagarize to parse your custom type.\n// If any field has `flagarize:` struct tag and it implements the ValueFlagarizer, this will be\n// used by kingping to parse the flag value.\n//\n// For an example see: `./timeduration.go` or `./regexp.go`\ntype ValueFlagarizer interface {\n\t// FlagarizeSetValue is invoked on kinpgin.Parse with the flag value passed as string.\n\t// It is expected from this method to parse the string to the underlying type.\n\t// This method has to be a pointer receiver for the method to take effect.\n\t// Flagarize will return error otherwise.\n\tSet(s string) error\n}\n```\n\n## Custom Flags\n\nSometimes custom parsing is not enough. Sometimes you need to register more flags than just one from\nsingle flagarize definition. To do so the type has to implement following interface:\n\n```go\n// Flagarizer is more advanced way to extend flagarize to parse a type. It allows to register\n// more than one flag or register them in a custom way. It's ok for a method to register nothing.\n// If any field implements `Flagarizer` this method will be invoked even if field does not\n// have `flagarize:` struct tag.\n//\n// If the field implements both ValueFlagarizer and Flagarizer, only Flagarizer will be used.\n//\n// For an example usage see: `./pathorcontent.go`\ntype Flagarizer interface {\n\t// Flagarize is invoked on Flagarize. If field type does not implement custom Flagarizer\n\t// default one will be used.\n\t// Tag argument is nil if no `flagarize` struct tag was specified. Otherwise it has parsed\n\t//`flagarize` struct tag.\n\t// The ptr argument is an address of the already allocated type, that can be used\n\t// by FlagRegisterer kingping *Var methods.\n\tFlagarize(r FlagRegisterer, tag *Tag, ptr unsafe.Pointer) error\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbwplotka%2Fflagarize","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbwplotka%2Fflagarize","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbwplotka%2Fflagarize/lists"}