{"id":30048402,"url":"https://github.com/segmentio/cli","last_synced_at":"2025-10-10T17:32:10.136Z","repository":{"id":37954343,"uuid":"211977696","full_name":"segmentio/cli","owner":"segmentio","description":"Go package providing high-level constructs for command-line tools.","archived":false,"fork":false,"pushed_at":"2025-05-27T16:47:55.000Z","size":221,"stargazers_count":54,"open_issues_count":4,"forks_count":8,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-09-01T21:07:54.305Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/segmentio.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":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-09-30T23:40:58.000Z","updated_at":"2025-05-28T19:28:49.000Z","dependencies_parsed_at":"2024-06-18T18:43:43.298Z","dependency_job_id":null,"html_url":"https://github.com/segmentio/cli","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"purl":"pkg:github/segmentio/cli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/segmentio%2Fcli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/segmentio%2Fcli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/segmentio%2Fcli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/segmentio%2Fcli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/segmentio","download_url":"https://codeload.github.com/segmentio/cli/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/segmentio%2Fcli/sbom","scorecard":{"id":809889,"data":{"date":"2025-08-11","repo":{"name":"github.com/segmentio/cli","commit":"e5dc62b798960468a4ab91260f2acbb15eea40c1"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.6,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":8,"reason":"Found 19/22 approved changesets -- score normalized to 8","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/segmentio/cli/go.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/segmentio/cli/go.yml/master?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/go.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":4,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'master'","Info: 'force pushes' disabled on branch 'master'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'master'","Info: 'stale review dismissal' is required to merge on branch 'master'","Warn: required approving review count is 1 on branch 'master'","Warn: codeowners review is required - but no codeowners file found in repo","Warn: 'last push approval' is disabled on branch 'master'","Warn: 'up-to-date branches' is disabled on branch 'master'","Info: status check found to merge onto on branch 'master'","Info: PRs are required in order to make changes on branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 29 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-23T12:49:49.788Z","repository_id":37954343,"created_at":"2025-08-23T12:49:49.788Z","updated_at":"2025-08-23T12:49:49.788Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279004821,"owners_count":26083784,"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","status":"online","status_checked_at":"2025-10-10T02:00:06.843Z","response_time":62,"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":[],"created_at":"2025-08-07T10:11:26.848Z","updated_at":"2025-10-10T17:32:10.131Z","avatar_url":"https://github.com/segmentio.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cli ![build status](https://github.com/segmentio/cli/actions/workflows/go.yml/badge.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/segmentio/cli)](https://goreportcard.com/report/github.com/segmentio/cli) [![GoDoc](https://godoc.org/github.com/segmentio/cli?status.svg)](https://godoc.org/github.com/segmentio/cli)\n\nGo package providing high-level constructs for command-line tools.\n\n## Motivation\n\nThe user interface of a program is a major contributor to its adoption and\nmaintainability, however it is often overlooked as a second-class requirement.\nDevelopers often focus on the _core functionalities_ of their programs and don't\nput as much time in designing and understanding how the program will be used.\n\nThe reality is that even when effort is spent on building powerful interfaces,\nthe tooling available in Go can be a blocker to generalization of the practice.\n\nThe standard library does offer a [package](https://golang.org/pkg/flag/) for\nparsing command line arguments, but it is limited to flags, and doesn't support\nloading configuration options from the environment, or building advanced UX with\nsub-commands.\n\nAnother popular package is [spf13/cobra](https://godoc.org/github.com/spf13/cobra),\nwhich has been the to-go solution for most projects. This package is powerful\nbut also very large, brings a lot of complexity to programs that use it, and\ncan be very time consuming to navigate for developers.\n\nWe believed that creating powerful tools should be simple, that developers\nshould be empowered to build programs that are safe to use and easy to evolve.\n\nThe `segmentio/cli` package was designed to have a minimal yet flexible API,\nmaking it easy to learn, and offering clear guidlines on how to build and evolve\ncommand line programs.\n\n## Command Line Interface\n\nThis section contains a couple of examples that showcase the features of the\npackage. (For more, see the \"examples\" directory.)\n\n### Flags\n\nThis first example presents how to construct a command which accepts a --name\nflag:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/segmentio/cli\"\n)\n\nfunc main() {\n\ttype config struct {\n\t\tName string `flag:\"-n,--name\" help:\"Someone's name\" default:\"Luke\"`\n\t}\n\n\tcli.Exec(cli.Command(func(config config) {\n\t\tfmt.Printf(\"hello %s!\\n\", config.Name)\n\t}))\n}\n```\n\n```\n$ ./example1 --help\n\nUsage:\n  example1 [options]\n\nOptions:\n  -h, --help         Show this help message\n  -n, --name string  Someone's name (default: Luke)\n\n```\n\n```\n$ ./example1 --name Han\nhello Han!\n```\n\nThe key take away here is how flags are declared by the first argument of the\nfunction implementing the command. The `segmentio/cli` package implements a\ncalling convention which maps the program arguments to the arguments of the\nfunction being called.\n\n### Default Values\n\nThe first example shows how to set a default value for a flag. If a flag is\ntruly optional, then set its default value to \"-\"; when the flag isn't used, its\nfield assumes its zero-value. When a flag does not have any default value\ndefined, then it is required.\n\n```go\ntype config struct {\n\t// optional, default \"Luke\"\n\tName     string `flag:\"-n,--name\"     help:\"Someone's name\"        default:\"Luke\"`\n\t// optional, no default\n\tPlanet   string `flag:\"-p,--planet\"   help:\"Someone's home planet\" default:\"-\"`\n\t// required\n\tGreeting string `flag:\"-g,--greeting\" help:\"Greeting word, such as hello\"`\n}\n```\n\n### Hidden Flags\n\nA hidden flag is not included in help text, making it undocumented but still\nusable.\n\n```go\n\t// optional, default \"Leia\", hidden\n\tSibling  string `flag:\"-s,--sibling\"  help:\"Secret family member\"  default:\"Leia\" hidden:\"true\"`\n```\n\n### Command Help Text\n\nWhen the struct used for flags contains a field named `_`, its \"help\" tag\ndefines the command's own help message. The field type is ignored.\n\n```go\ntype config struct {\n\t_    struct{} `help:\"Greets someone from a galaxy far, far away\"`\n\tName string   `flag:\"-n,--name\" help:\"Someone's name\" default:\"Luke\"`\n}\n```\n\n### Positional Arguments\n\nWhile the first argument of a command must always be a struct defining the set\nof accepted flags, the function may also define extra arguments which will be\nloaded from positional arguments:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/segmentio/cli\"\n)\n\nfunc main() {\n\ttype noflags struct{}\n\n\tcli.Exec(cli.Command(func(_ noflags, x, y int) {\n\t\tfmt.Println(x + y)\n\t}))\n}\n```\n```\n$ ./example2 --help\n\nUsage:\n  example2 [options] [int] [int]\n\nOptions:\n  -h, --help  Show this help message\n```\n```\n$ ./example2 1 2\n3\n```\n\nThe last function parameter may also be a slice which captures all remaining\npositional arguments:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/segmentio/cli\"\n)\n\nfunc main() {\n\ttype noflags struct{}\n\n\tcli.Exec(cli.Command(func(_ noflags, words []string) {\n\t\tfor _, word := range words {\n\t\t\tfmt.Println(word)\n\t\t}\n\t}))\n}\n```\n```\n$ ./example3 --help\n\nUsage:\n  example3 [options] [string...]\n\nOptions:\n  -h, --help  Show this help message\n\n```\n```\n$ ./example3 hello world\nhello\nworld\n```\n\n### Child Commands\n\nIt is common for wrapper programs to accept an arbitrary command that they\nexecute after performing some initializations. To reduce the risk of mixing\nthe program's arguments and the arguments of its child-command, a \"--\" separator\nis employed as a delimiter between the two on the command line.\n\nWith the `segmentio/cli` package, this model is supported by adding a variadic\nlist of string parameters to the command:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/segmentio/cli\"\n)\n\nfunc main() {\n\ttype noflags struct{}\n\n\tcli.Exec(cli.Command(func(_ noflags, args ...string) {\n\t\tfmt.Println(\"run:\", strings.Join(args, \" \"))\n\t}))\n}\n```\n```\n$ ./example4 --help\n\nUsage:\n  example4 [options] -- [command]\n\nOptions:\n  -h, --help  Show this help message\n\n```\n```\n$ ./example4 -- echo hello world\nrun: echo hello world\n```\n\n### Command Sets\n\nAdvanced tools often have a set of commands in a single program, each exposing\na different feature of the tool (e.g. `git checkout`, `git commit`).\n\nThe `segmentio/cli` package supports constructing programs like these using the\n`cli.CommandSet` type. The next example showcases how to construct a program\naccepting three sub-commands:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/segmentio/cli\"\n)\n\nfunc main() {\n\ttype oneConfig struct {\n\t\t_ struct{} `help:\"Usage text for command one\"`\n\t}\n\tone := cli.Command(func(cfg oneConfig) {\n\t\tfmt.Println(\"1\")\n\t})\n\n\ttwo := cli.Command(func() {\n\t\tfmt.Println(\"2\")\n\t})\n\n\tthree := cli.CommandSet{\n\t\t\"_\": cli.CommandFunc{\n\t\t\tHelp: \"Usage text for the command three\",\n\t\t},\n\t\t\"four\": cli.Command(func() {\n\t\t\tfmt.Println(\"4\")\n\t\t}),\n\t\t\"five\": cli.Command(func() {\n\t\t\tfmt.Println(\"4\")\n\t\t}),\n\t}\n\n\tcli.Exec(cli.CommandSet{\n\t\t\"one\":   one,\n\t\t\"two\":   two,\n\t\t\"three\": three,\n\t})\n}\n```\n```\n$ ./example5 --help\nUsage:\n  example5 [command] [-h] [--help] ...\n\nCommands:\n  one    Usage text for command one\n  three  Usage text for the 'three' command\n  two\n\nOptions:\n  -h, --help  Show this help message\n\n```\n```\n$ ./example5 one\n1\n```\n\nWhen the command set contains a value for the key `\"_\"`, its function value's\n\"Help\" value defines the command set's own help message.\n\n## Environment Variables\n\nWhile passing configuration options on the command line using flags and\npositional arguments provides great UX, it is also very common to use\nenvironment variables in configuration files like kubernetes templates.\n\nEvery _long flag_ accepted by a command (flags starting with \"--\") can also be\nloaded from environment variables. The package maps environment variables to\nflags by prefixing it with the program name and converting the flag to\nupper-snake-case, for example:\n\n    \u003e --verbose =\u003e ${PROGRAM}_VERBOSE\n\n## Testing Commands\n\nTesting command line programs is often overlooked, because packages which\nfacilitate loading program configurations often aren't designed with ease of\ntesting in mind.\n\nOn the other hand, commands declared with the `segmentio/cli` package are easily\ntestable using the `cli.Call` function, which combined with Go's support for\ntestable examples, offer a great model for testing commands.\n\nUsing the first example, here is how we could write tests to validate the\nbehavior of the command:\n\n```go\ntype config struct {\n\tName string `flag:\"-n,--name\" help:\"Someone's name\" default:\"Luke\"`\n}\n\nvar command = cli.Command(func(config config) {\n\tfmt.Printf(\"hello %s!\\n\", config.Name)\n})\n```\n```go\nfunc Example_noArguments() {\n\tcli.Call(command)\n\t// Output: hello Luke!\n}\n\nfunc Example_withArgument() {\n\tcli.Call(command, \"--name\", \"Han\")\n\t// Output: hello Han!\n}\n```\n\n## Formatting Output\n\nA lot of command line programs also output information to their caller, and\noften need to support multiple formats to be used in different conditions\n(called by an operator, used in a script for automation, etc...).\n\nThis formatting work is often tedious and redundant, so the `segmentio/cli`\npackage exposes abstractions to help developers build tools which support\nmultiple output formats:\n\n```go\ntype config struct {\n\tOutput string `flag:\"-o,--output\" help:\"Output format of the command\" default:\"text\"`\n}\n\ntype result struct {\n\tName  string\n\tValue int\n}\n\nvar command = cli.Command(func(config config) error {\n\tp, err := cli.Format(config.Output, os.Stdout)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer p.Flush()\n\n\t...\n\n\t// Call p.Print one or more times to output content to stdout\n\t//\n\t// p.Print(v)\n})\n```\n\nThe package supports three formats out-of-the-box: text, json, and yaml.\n\nIn the text format, struct and map values are printed as table representations\nwith a header being the name of the struct fields or the keys of the map.\nOther value types are simply printed one value per line.\n\nAll formats interpret the `json` struct tag to configure the names of the fields\nand the behavior of the formatting operation.\n\nThe text format also interprets `fmt` tags as carrying the formatting string\npassed in calls to functions of the `fmt` package.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsegmentio%2Fcli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsegmentio%2Fcli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsegmentio%2Fcli/lists"}