-
Notifications
You must be signed in to change notification settings - Fork 55
/
README.md.template
308 lines (244 loc) · 9.61 KB
/
README.md.template
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# mow.cli
![CI](https://github.com/jawher/mow.cli/workflows/CI/badge.svg)
[![GoDoc](https://pkg.go.dev/badge/github.com/jawher/mow.cli)](https://pkg.go.dev/github.com/jawher/mow.cli)
[![Coverage Status](https://coveralls.io/repos/github/jawher/mow.cli/badge.svg?branch=master)](https://coveralls.io/github/jawher/mow.cli?branch=master)
{{.Synopsis}}
## Getting Started
The following examples demonstrate basic usage the package.
### Simple Application
In this simple application, we mimic the argument parsing of the standard UNIX
cp command. Our application requires the user to specify one or more source
files followed by a destination. An optional recursive flag may be provided.
```go
package main
import (
"fmt"
"os"
"github.com/jawher/mow.cli"
)
func main() {
// create an app
app := cli.App("cp", "Copy files around")
// Here's what differentiates mow.cli from other CLI libraries:
// This line is not just for help message generation.
// It also validates the call to reject calls with less than 2 arguments
// and split the arguments between SRC or DST
app.Spec = "[-r] SRC... DST"
var (
// declare the -r flag as a boolean flag
recursive = app.BoolOpt("r recursive", false, "Copy files recursively")
// declare the SRC argument as a multi-string argument
src = app.StringsArg("SRC", nil, "Source files to copy")
// declare the DST argument as a single string (string slice) arguments
dst = app.StringArg("DST", "", "Destination where to copy files to")
)
// Specify the action to execute when the app is invoked correctly
app.Action = func() {
fmt.Printf("Copying %v to %s [recursively: %v]\n", *src, *dst, *recursive)
}
// Invoke the app passing in os.Args
app.Run(os.Args)
}
```
### Pointers to existing variables
This variant of the cp command uses the Ptr variants, where you can pass pointers to existing variables
instead of declaring new ones for the options/arguments:
```go
package main
import (
"fmt"
"os"
cli "github.com/jawher/mow.cli"
)
type Config struct {
Recursive bool
Src []string
Dst string
}
func main() {
var (
app = cli.App("cp", "Copy files around")
cfg Config
)
// Here's what differentiates mow.cli from other CLI libraries:
// This line is not just for help message generation.
// It also validates the call to reject calls with less than 2 arguments
// and split the arguments between SRC or DST
app.Spec = "[-r] SRC... DST"
// declare the -r flag as a boolean flag
app.BoolOptPtr(&cfg.Recursive, "r recursive", false, "Copy files recursively")
// declare the SRC argument as a multi-string argument
app.StringsArgPtr(&cfg.Src, "SRC", nil, "Source files to copy")
// declare the DST argument as a single string (string slice) arguments
app.StringArgPtr(&cfg.Dst, "DST", "", "Destination where to copy files to")
// Specify the action to execute when the app is invoked correctly
app.Action = func() {
fmt.Printf("Copying using config: %+v\n", cfg)
}
// Invoke the app passing in os.Args
app.Run(os.Args)
}
```
### Multi-Command Application
In the next example, we create a multi-command application in the same style as
familiar commands such as git and docker. We build a fictional utility called
uman to manage users in a system. It provides two commands that can be invoked:
list and get. The list command takes an optional flag to specify all users
including disabled ones. The get command requires one argument, the user ID,
and takes an optional flag to specify a detailed listing.
```go
package main
import (
"fmt"
"os"
"github.com/jawher/mow.cli"
)
func main() {
app := cli.App("uman", "User Manager")
app.Spec = "[-v]"
var (
verbose = app.BoolOpt("v verbose", false, "Verbose debug mode")
)
app.Before = func() {
if *verbose {
// Here we can enable debug output in our logger for example
fmt.Println("Verbose mode enabled")
}
}
// Declare our first command, which is invocable with "uman list"
app.Command("list", "list the users", func(cmd *cli.Cmd) {
// These are the command-specific options and args, nicely scoped
// inside a func so they don't pollute the namespace
var (
all = cmd.BoolOpt("all", false, "List all users, including disabled")
)
// Run this function when the command is invoked
cmd.Action = func() {
// Inside the action, and only inside, we can safely access the
// values of the options and arguments
fmt.Printf("user list (including disabled ones: %v)\n", *all)
}
})
// Declare our second command, which is invocable with "uman get"
app.Command("get", "get a user details", func(cmd *cli.Cmd) {
var (
detailed = cmd.BoolOpt("detailed", false, "Display detailed info")
id = cmd.StringArg("ID", "", "The user id to display")
)
cmd.Action = func() {
fmt.Printf("user %q details (detailed mode: %v)\n", *id, *detailed)
}
})
// With the app configured, execute it, passing in the os.Args array
app.Run(os.Args)
}
```
### A Larger Multi-Command Example
This example shows an alternate way of organizing our code when dealing with a
larger number of commands and subcommands. This layout emphasizes the command
structure and defers the details of each command to subsequent functions. Like
the prior examples, options and arguments are still scoped to their respective
functions and don't pollute the global namespace.
```go
package main
import (
"fmt"
"os"
"github.com/jawher/mow.cli"
)
// Global options available to any of the commands
var filename *string
func main() {
app := cli.App("vault", "Password Keeper")
// Define our top-level global options
filename = app.StringOpt("f file", os.Getenv("HOME")+"/.safe", "Path to safe")
// Define our command structure for usage like this:
app.Command("list", "list accounts", cmdList)
app.Command("creds", "display account credentials", cmdCreds)
app.Command("config", "manage accounts", func(config *cli.Cmd) {
config.Command("list", "list accounts", cmdList)
config.Command("add", "add an account", cmdAdd)
config.Command("remove", "remove an account(s)", cmdRemove)
})
app.Run(os.Args)
}
// Sample use: vault list OR vault config list
func cmdList(cmd *cli.Cmd) {
cmd.Action = func() {
fmt.Printf("list the contents of the safe here")
}
}
// Sample use: vault creds reddit.com
func cmdCreds(cmd *cli.Cmd) {
cmd.Spec = "ACCOUNT"
account := cmd.StringArg("ACCOUNT", "", "Name of account")
cmd.Action = func() {
fmt.Printf("display account info for %s\n", *account)
}
}
// Sample use: vault config add reddit.com -u username -p password
func cmdAdd(cmd *cli.Cmd) {
cmd.Spec = "ACCOUNT [ -u=<username> ] [ -p=<password> ]"
var (
account = cmd.StringArg("ACCOUNT", "", "Account name")
username = cmd.StringOpt("u username", "admin", "Account username")
password = cmd.StringOpt("p password", "admin", "Account password")
)
cmd.Action = func() {
fmt.Printf("Adding account %s:%s@%s", *username, *password, *account)
}
}
// Sample use: vault config remove reddit.com twitter.com
func cmdRemove(cmd *cli.Cmd) {
cmd.Spec = "ACCOUNT..."
var (
accounts = cmd.StringsArg("ACCOUNT", nil, "Account names to remove")
)
cmd.Action = func() {
fmt.Printf("Deleting accounts: %v", *accounts)
}
}
```
## Comparison to Other Tools
There are several tools in the Go ecosystem to facilitate the creation of
command line tools. The following is a comparison to the built-in flag package
as well as the popular urfave/cli (formerly known as codegangsta/cli):
| | mow.cli | urfave/cli | flag |
|---------------------------------------------------------------------|---------|------------|------|
| Contextual help | ✓ | ✓ | |
| Commands | ✓ | ✓ | |
| Option folding `-xyz` | ✓ | | |
| Option value folding `-fValue` | ✓ | | |
| Option exclusion `--start ❘ --stop` | ✓ | | |
| Option dependency `[-a -b]` or `[-a [-b]]` | ✓ | | |
| Arguments validation `SRC DST` | ✓ | | |
| Argument optionality `SRC [DST]` | ✓ | | |
| Argument repetition `SRC... DST` | ✓ | | |
| Option/argument dependency `SRC [-f DST]` | ✓ | | |
| Any combination of the above `[-d ❘ --rm] IMAGE [COMMAND [ARG...]]` | ✓ | | |
Unlike the simple packages above, docopt is another library that supports rich
set of flag and argument validation. It does, however, fall short for many use
cases including:
| | mow.cli | docopt |
|----------------------------|---------|--------|
| Contextual help | ✓ | |
| Backtracking `SRC... DST` | ✓ | |
| Backtracking `[SRC] DST` | ✓ | |
| Branching `(SRC ❘ -f DST)` | ✓ | |
## Installation
To install this package, run the following:
```shell
go get {{.Import}}
```
# Package Documentation
<!-- Do NOT edit past here. This is replaced by the contents of the package documentation -->
{{.Doc}}
{{if .Bugs}}
# Bugs
{{range .Bugs}}* {{.}}{{end}}
{{end}}
## License
This work is published under the MIT license.
Please see the `LICENSE` file for details.
* * *
Automatically generated by [autoreadme](https://github.com/jawher/autoreadme) on {{.Today}}