{"id":19900509,"url":"https://github.com/fredbi/go-cli","last_synced_at":"2026-06-07T18:31:45.553Z","repository":{"id":64297483,"uuid":"563993469","full_name":"fredbi/go-cli","owner":"fredbi","description":"A few utilities to build CLI on top of cobra and viper","archived":false,"fork":false,"pushed_at":"2023-11-17T11:30:41.000Z","size":157,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-11-23T22:08:41.549Z","etag":null,"topics":["cli","configuration-management","golang"],"latest_commit_sha":null,"homepage":"","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/fredbi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-09T19:09:23.000Z","updated_at":"2025-09-15T16:19:49.000Z","dependencies_parsed_at":"2024-06-20T13:02:13.651Z","dependency_job_id":"0432609a-e0c7-4959-814c-c7320fb6f248","html_url":"https://github.com/fredbi/go-cli","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/fredbi/go-cli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredbi%2Fgo-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredbi%2Fgo-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredbi%2Fgo-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredbi%2Fgo-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fredbi","download_url":"https://codeload.github.com/fredbi/go-cli/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fredbi%2Fgo-cli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34034024,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-07T02:00:07.652Z","response_time":124,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["cli","configuration-management","golang"],"created_at":"2024-11-12T20:12:32.250Z","updated_at":"2026-06-07T18:31:45.522Z","avatar_url":"https://github.com/fredbi.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Lint](https://github.com/fredbi/go-cli/actions/workflows/01-golang-lint.yaml/badge.svg)\n![CI](https://github.com/fredbi/go-cli/actions/workflows/02-test.yaml/badge.svg)\n[![Coverage Status](https://coveralls.io/repos/github/fredbi/go-cli/badge.svg)](https://coveralls.io/github/fredbi/go-cli)\n![Vulnerability Check](https://github.com/fredbi/go-cli/actions/workflows/03-govulncheck.yaml/badge.svg)\n[![Go Report Card](https://goreportcard.com/badge/github.com/fredbi/go-cli)](https://goreportcard.com/report/github.com/fredbi/go-cli)\n\n![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/fredbi/go-cli)\n[![Go Reference](https://pkg.go.dev/badge/github.com/fredbi/go-cli.svg)](https://pkg.go.dev/github.com/fredbi/go-cli)\n[![license](http://img.shields.io/badge/license/License-Apache-yellow.svg)](https://raw.githubusercontent.com/fredbi/go-cli/master/LICENSE.md)\n\n# go-cli\n\nThis repo exposes a few utilities to\n(i) [build command-line utilities](#CLI) with\n(ii) [flexible configurations](#Configuration)\non top of 3 great libraries:\n[`github.com/spf13/cobra`](https://github.com/spf13/cobra),\n[`github.com/spf13/viper`](https://github.com/spf13/viper), and\n[`github.com/spf13/pflag`](https://github.com/spf13/pflag).\n\n**TL,DR**: this is not yet another CLI-building library, but rather a mere wrapper on top of `cobra`\nto use that great lib with a functional style.\n\n## CLI\n\n### Example for CLI\n\nSample CLI-building code. This example is taken from [one of the testable examples](cli/example_test.go).\n\nNotice our main objectives here:\n* no globals\n* inline flag registration \u0026 binding\n* access to settings using viper only\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/fredbi/go-cli/cli\"\n    \"github.com/spf13/cobra\"\n)\n\nconst (\n    // viper config keys\n\tkeyLog      = \"app.log.level\"\n\tkeyDry      = \"run.dryRun\"\n)\n\nfunc main() {\n    // no global vars, no init() ...\n\tif err := RootCmd().Execute(); err != nil {\n\t\tcli.Die(\"executing: %v\", err)\n\t}\n}\n\n// RootCmd builds a runnable root command\nfunc RootCmd() *cli.Command {\n\treturn cli.NewCommand(\n        // your usual cobra command, wrapped as a function\n\t\t\u0026cobra.Command{\n\t\t\tUse:   \"example\",\n\t\t\tShort: \"examplifies a cobra command\",\n\t\t\tLong:  \"...\",\n\t\t\tRunE:  rootRunFunc,\n\t\t},\n        // flag bindings\n        // {flag name}, {the flag type is inferred from the default value}, {flag help description}\n\t\tcli.WithFlag(\"dry-run\", false, \"Dry run\",\n\t\t\tcli.BindFlagToConfig(keyDry), // flag bindings to a viper config\n\t\t),\n        // a flag inherited by subcommands\n\t\tcli.WithPersistentFlag(\"log-level\", \"info\", \"Controls logging verbosity\",\n\t\t\tcli.BindFlagToConfig(keyLog),\n\t\t),\n        // apply viper config to the command tree\n        // command binding to a viper config -\u003e config will be available from context\n\t\tcli.WithConfig(cli.Config()),\n\t)\n}\n\n// rootRunFunc runs the root command\nfunc rootRunFunc(c *cobra.Command, _ []string) error {\n    // retrieve injected dependencies, create new empty viper registry if unresolved\n\tcfg := injectable.ConfigFromContext(c.Context(), viper.New)\n\n\tfmt.Println(\n\t\t\"example called\\n\",\n\t\tfmt.Sprintf(\"dry-run: %t\\n\", cfg.GetBool(keyDry)),\n\t\tfmt.Sprintf(\"log level config: %s\\n\", cfg.GetString(keyLog)),\n\t)\n\n\treturn nil\n}\n```\n\n### Goals\n\nThe `cli` packages proposes an opinionated approach to building command-line binaries on top of `github.com/spf13/cobra`.\n\n\u003e There are a few great existing libraries around to build a CLI.\n\u003e I believe that `cobra` stands out as the richest and most flexible,\n\u003e as CLIs are entirely built programmatically.\n\n`cobra` is great, but building CLIs again and again, I came to identify a few repetitive boiler-plate patterns.\n\nSo this module reflects my opinions about how to build more elegant CLIs, wich abide by [12-factor](https://12factor.net)\nout-of-the-box, with more expressive code and less low-level tinkering.\n\nFeedback is always welcome, as opinions may evolve over time...\nFeel free to post issues to leave your comments and/or proposals.\n\n[More detailed design goals](docs/goals.md#CLI)\n\n## Configuration\n\nThe `config` package proposes an opinionated approach to dealing with config files on top of `github.com/spf13/viper`.\n\nIt exposes configuration loaders which know about the deployment context\n(e.g a deployment environment such as `dev`, `production`) and secrets.\n\nAlthough developped primarily to serve a CLI, this package may be used independently.\n\n### Example: loading a config\n\nOther examples are available [here](config/examples_test.go).\n\n```go\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/fredbi/go-cli/config\"\n)\n\n...\n\n// load and merge configuration files for environment \"dev\"\ncfg, err := config.Load(\"dev\", config.WithMute(true))\nif err != nil {\n\tlog.Fatalf(\"loading config: %w\", err)\n\n\treturn\n}\n\n```\n\n### Goals\n\nThis describes my approach to configuration. We want to:\n\n1. retrieve a config organized as a hierarchy of settings, e.g. a YAML document\n2. merge configuration files with environment-specific settings\n3. merge configuration files with secrets, usually these are environment-specific\n4. clearly isolate and merge default settings\n5. applications to be able to consume the settings from a single viper configuration registry\n\nIn addition,\n\n* we want the hierarchy to be agnostic to the environment context\n* most of the time, we don't want env-specific sections to propagate to the app level\n  (e.g. in the style of `.ini` sections)\n\n\u003e In our code, we should never check for a dev or prod specific section of the configuration.\n\n\nSupported format: YAML, JSON\n\nSupported file extensions: \"yml\", \"yaml\", \"json\"\n\nSee other [examples](.config/examples_test.go)\n\n[More detailed design goals](docs/goals.md#Configuration)\n\n### Folders structure for configurations\n\nBy default we have:\n```\n# \u003c- root configuration\n{base path}/config.yaml\n            # \u003c- environment-specifics folder\n            config.d/\n                     # \u003c- extra configuration to merge\n                     config.yaml\n                     # \u003c- possibly with a modified name: config.*.yaml\n                     config.default.yaml\n                     # \u003c- configuration to merge for environment\n                     {environment}/config.yaml\n                     # other environment-specifics ....\n                     {...}/config.yaml\n```\n\n[Here is an example](./config/examples)\n\nWhen using default settings for this module (these are configurable),\nthe base path is defined by the `CONFIG_DIR` environment variable.\n\nSecret configurations:\n```\n{base path}/secrets.yaml\n            config.d/\n                     # \u003c- secrets to merge\n                     secrets.yaml\n                     # \u003c- configuration to merge for environment\n                     {environment}/secrets.yaml\n```\n\n### [Typical configuration for a Kubernetes deployment](docs/k8s.md)\n\n### Side notes\n\n#### TODOs\n\n* [CLI todo list](cli/TODO.md)\n* [config todo list](config/TODO.md)\n\n#### Dealing with secrets locally\n\nTODO(fredbi)\n\n### Credits\n\nThe config part is largely based on some seminal past work by [@casualjim](https://github.com/casualjim/).\nI am grateful to him for his much inspiring code.\n\nThe version-from-go-runtime piece of code is largely inspired by the wonderful work from the\n[golangci](https://github.com/golangci/golangci-lint) community.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffredbi%2Fgo-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffredbi%2Fgo-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffredbi%2Fgo-cli/lists"}