{"id":13410490,"url":"https://github.com/yitsushi/go-commander","last_synced_at":"2025-03-14T16:32:18.163Z","repository":{"id":57484121,"uuid":"70476229","full_name":"yitsushi/go-commander","owner":"yitsushi","description":"Go library to simplify CLI workflow","archived":true,"fork":false,"pushed_at":"2020-05-24T20:27:55.000Z","size":72,"stargazers_count":35,"open_issues_count":1,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-07-31T20:42:48.163Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://yitsushi.github.io/go-commander/","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/yitsushi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-10-10T10:09:41.000Z","updated_at":"2023-10-07T13:10:47.000Z","dependencies_parsed_at":"2022-08-26T12:23:57.691Z","dependency_job_id":null,"html_url":"https://github.com/yitsushi/go-commander","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yitsushi%2Fgo-commander","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yitsushi%2Fgo-commander/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yitsushi%2Fgo-commander/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yitsushi%2Fgo-commander/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yitsushi","download_url":"https://codeload.github.com/yitsushi/go-commander/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243610231,"owners_count":20318929,"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":[],"created_at":"2024-07-30T20:01:07.280Z","updated_at":"2025-03-14T16:32:17.860Z","avatar_url":"https://github.com/yitsushi.png","language":"Go","funding_links":[],"categories":["命令行工具### 标准 CLI`用于创建一个标准命令行应用程序的库`","Command Line","命令行","命令行工具","Build Automation"],"sub_categories":["Standard CLI","标准CLI","标准 CLI"],"readme":"[![Documentation](https://godoc.org/github.com/yitsushi/go-commander?status.svg)](http://godoc.org/github.com/yitsushi/go-commander)\n[![Go Report Card](https://goreportcard.com/badge/github.com/yitsushi/go-commander)](https://goreportcard.com/report/github.com/yitsushi/go-commander)\n[![Coverage Status](https://coveralls.io/repos/github/yitsushi/go-commander/badge.svg)](https://coveralls.io/github/yitsushi/go-commander)\n[![Build Status](https://travis-ci.org/yitsushi/go-commander.svg?branch=master)](https://travis-ci.org/yitsushi/go-commander)\n\nThis is a simple Go library to manage commands for your CLI tool.\nEasy to use and now you can focus on Business Logic instead of building\nthe command routing.\n\n### What this library does for you?\n\nManage your separated commands. How? Generates a general help and command\nspecific helps for your commands. If your command fails somewhere\n(`panic` for example), commander will display the error message and\nthe command specific help to guide your user.\n\n### Install\n\n```shell\n$ go get github.com/yitsushi/go-commander\n```\n\n### Sample output _(from [totp-cli](https://github.com/yitsushi/totp-cli))_\n\n```shell\n$ totp-cli help\n\nchange-password                   Change password\nupdate                            Check and update totp-cli itself\nversion                           Print current version of this application\ngenerate \u003cnamespace\u003e.\u003caccount\u003e    Generate a specific OTP\nadd-token [namespace] [account]   Add new token\nlist [namespace]                  List all available namespaces or accounts under a namespace\ndelete \u003cnamespace\u003e[.account]      Delete an account or a whole namespace\nhelp [command]                    Display this help or a command specific help\n```\n\n### Usage\n\nEvery single command has to implement `CommandHandler`.\nCheck [this project](https://github.com/yitsushi/totp-cli) for examples.\n\n```go\npackage main\n\n// Import the package\nimport \"github.com/yitsushi/go-commander\"\n\n// Your Command\ntype YourCommand struct {\n}\n\n// Executed only on command call\nfunc (c *YourCommand) Execute(opts *commander.CommandHelper) {\n  // Command Action\n}\n\nfunc NewYourCommand(appName string) *commander.CommandWrapper {\n  return \u0026commander.CommandWrapper{\n    Handler: \u0026YourCommand{},\n    Help: \u0026commander.CommandDescriptor{\n      Name:             \"your-command\",\n      ShortDescription: \"This is my own command\",\n      LongDescription:  `This is a very long\ndescription about this command.`,\n      Arguments:        \"\u003cfilename\u003e [optional-argument]\",\n      Examples:         []string {\n        \"test.txt\",\n        \"test.txt copy\",\n        \"test.txt move\",\n      },\n    },\n  }\n}\n\n// Main Section\nfunc main() {\n\tregistry := commander.NewCommandRegistry()\n\n\tregistry.Register(NewYourCommand)\n\n\tregistry.Execute()\n}\n```\n\nNow you have a CLI tool with two commands: `help` and `your-command`.\n\n```bash\n❯ go build mytool.go\n\n❯ ./mytool\nyour-command \u003cfilename\u003e [optional-argument]   This is my own command\nhelp [command]                                Display this help or a command specific help\n\n❯ ./mytool help your-command\nUsage: mytool your-command \u003cfilename\u003e [optional-argument]\n\nThis is a very long\ndescription about this command.\n\nExamples:\n  mytool your-command test.txt\n  mytool your-command test.txt copy\n  mytool your-command test.txt move\n```\n\n#### How to use subcommand pattern?\n\nWhen you create your main command, just create a new `CommandRegistry` inside\nthe `Execute` function like you did in your `main()` and change `Depth`.\n\n```go\nimport subcommand \"github.com/yitsushi/mypackage/command/something\"\n\nfunc (c *Something) Execute(opts *commander.CommandHelper) {\n\tregistry := commander.NewCommandRegistry()\n\tregistry.Depth = 1\n\tregistry.Register(subcommand.NewSomethingMySubCommand)\n\tregistry.Execute()\n}\n```\n\n### PreValidation\n\nIf you want to write a general pre-validation for your command\nor just simply keep your validation logic separated:\n\n```go\n// Or you can define inline if you want\nfunc MyValidator(c *commander.CommandHelper) {\n  if c.Arg(0) == \"\" {\n    panic(\"File?\")\n  }\n\n  info, err := os.Stat(c.Arg(0))\n  if err != nil {\n    panic(\"File not found\")\n  }\n\n  if !info.Mode().IsRegular() {\n    panic(\"It's not a regular file\")\n  }\n\n  if c.Arg(1) != \"\" {\n    if c.Arg(1) != \"copy\" \u0026\u0026 c.Arg(1) != \"move\" {\n      panic(\"Invalid operation\")\n    }\n  }\n}\n\nfunc NewYourCommand(appName string) *commander.CommandWrapper {\n  return \u0026commander.CommandWrapper{\n    Handler: \u0026YourCommand{},\n    Validator: MyValidator\n    Help: \u0026commander.CommandDescriptor{\n      Name:             \"your-command\",\n      ShortDescription: \"This is my own command\",\n      LongDescription:  `This is a very long\ndescription about this command.`,\n      Arguments:        \"\u003cfilename\u003e [optional-argument]\",\n      Examples:         []string {\n        \"test.txt\",\n        \"test.txt copy\",\n        \"test.txt move\",\n      },\n    },\n  }\n}\n```\n\n\n### Define arguments with type\n\n```go\n\u0026commander.CommandWrapper{\n  Handler: \u0026MyCommand{},\n  Arguments: []*commander.Argument{\n    \u0026commander.Argument{\n      Name: \"list\",\n      Type: \"StringArray[]\",\n    },\n  },\n  Help: \u0026commander.CommandDescriptor{\n    Name: \"my-command\",\n  },\n}\n```\n\nIn your command:\n\n```go\nif opts.HasValidTypedOpt(\"list\") == nil {\n  myList := opts.TypedOpt(\"list\").([]string)\n  if len(myList) \u003e 0 {\n    mockPrintf(\"My list: %v\\n\", myList)\n  }\n}\n```\n\n#### Define own type\n\nYes you can ;)\n\n```go\n// Define your struct (optional)\ntype MyCustomType struct {\n\tID   uint64\n\tName string\n}\n\n// register your own type with parsing/validation\ncommander.RegisterArgumentType(\"MyType\", func(value string) (interface{}, error) {\n  values := strings.Split(value, \":\")\n\n  if len(values) \u003c 2 {\n    return \u0026MyCustomType{}, errors.New(\"Invalid format! MyType =\u003e 'ID:Name'\")\n  }\n\n  id, err := strconv.ParseUint(values[0], 10, 64)\n  if err != nil {\n    return \u0026MyCustomType{}, errors.New(\"Invalid format! MyType =\u003e 'ID:Name'\")\n  }\n\n  return \u0026MyCustomType{\n      ID:   id,\n      Name: values[1],\n    },\n    nil\n})\n\n// Define your command\n\u0026commander.CommandWrapper{\n  Handler: \u0026MyCommand{},\n  Arguments: []*commander.Argument{\n    \u0026commander.Argument{\n      Name:        \"owner\",\n      Type:        \"MyType\",\n      FailOnError: true,          // Optional boolean\n    },\n  },\n  Help: \u0026commander.CommandDescriptor{\n    Name: \"my-command\",\n  },\n}\n```\n\nIn your command:\n\n```go\nif opts.HasValidTypedOpt(\"owner\") == nil {\n  owner := opts.TypedOpt(\"owner\").(*MyCustomType)\n  mockPrintf(\"OwnerID: %d, Name: %s\\n\", owner.ID, owner.Name)\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyitsushi%2Fgo-commander","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyitsushi%2Fgo-commander","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyitsushi%2Fgo-commander/lists"}