Skip to content

Commit 17a4e7a

Browse files
Breaking: Change the signature of cmd.Run to pass a context.Context (#185)
* Breaking: Change the Run method signature to pass a context.Context * Add an example for context cancellation
1 parent 5d82c7c commit 17a4e7a

22 files changed

Lines changed: 191 additions & 111 deletions

File tree

.rumdl.toml

Lines changed: 0 additions & 34 deletions
This file was deleted.

README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ go get go.followtheprocess.codes/cli@latest
5050
package main
5151

5252
import (
53+
"context"
5354
"fmt"
5455
"os"
5556

@@ -77,7 +78,7 @@ func run() error {
7778
cli.Example("Do a thing", "quickstart something"),
7879
cli.Example("Count the things", "quickstart something --count 3"),
7980
cli.Flag(&count, "count", 'c', 0, "Count the things"),
80-
cli.Run(func(cmd *cli.Command) error {
81+
cli.Run(func(ctx context.Context, cmd *cli.Command) error {
8182
fmt.Fprintf(cmd.Stdout(), "Hello from quickstart!, my args were: %v, count was %d\n", cmd.Args(), count)
8283
return nil
8384
}),
@@ -86,7 +87,7 @@ func run() error {
8687
return err
8788
}
8889

89-
return cmd.Execute()
90+
return cmd.Execute(context.Background())
9091
}
9192
```
9293

@@ -107,9 +108,9 @@ To create CLI commands, you simply call `cli.New`:
107108
cmd, err := cli.New(
108109
"name", // The name of your command
109110
cli.Short("A new command") // Shown in the help
110-
cli.Run(func(cmd *cli.Command, args []string) error {
111+
cli.Run(func(ctx context.Context, cmd *cli.Command) error {
111112
// This function is what your command does
112-
fmt.Printf("name called with args: %v\n", args)
113+
fmt.Printf("name called with args: %v\n", cmd.Args())
113114
return nil
114115
})
115116
)
@@ -124,7 +125,7 @@ To add a subcommand underneath the command you've just created, it's again `cli.
124125

125126
```go
126127
// Best to abstract it into a function
127-
func buildSubcommand() (*cli.Command, error) {
128+
func buildSubcommand(ctx context.Context) (*cli.Command, error) {
128129
return cli.New(
129130
"sub", // Name of the sub command e.g. 'clone' for 'git clone'
130131
cli.Short("A sub command"),
@@ -136,11 +137,13 @@ func buildSubcommand() (*cli.Command, error) {
136137
And add it to your parent command:
137138

138139
```go
140+
ctx := context.Background()
141+
139142
// From the example above
140143
cmd, err := cli.New(
141144
"name", // The name of your command
142145
// ...
143-
cli.SubCommands(buildSubcommand),
146+
cli.SubCommands(buildSubcommand(ctx)),
144147
)
145148
```
146149

@@ -151,6 +154,8 @@ This pattern can be repeated recursively to create complex command structures.
151154
Flags in `cli` are generic, that is, there is *one* way to add a flag to your command, and that's with the `cli.Flag` option to `cli.New`
152155

153156
```go
157+
// These will get set at command line parse time
158+
// based on your flags
154159
type options struct {
155160
name string
156161
force bool
@@ -167,7 +172,7 @@ func buildCmd() (*cli.Command, error) {
167172
cli.Flag(&options.force, "force", cli.NoShortHand, false, "Force delete without confirmation"),
168173
cli.Flag(&options.size, "size", 's', 0, "Size of something"),
169174
cli.Flag(&options.items, "items", 'i', nil, "Items to include"),
170-
cli.Run(runCmd(&options)), // Pass the parsed flag values to your command run function
175+
// ...
171176
)
172177
}
173178
```
@@ -220,7 +225,7 @@ There are two approaches to positional arguments in `cli`, you can either just g
220225
cli.New(
221226
"my-command",
222227
// ...
223-
cli.Run(func(cmd *cli.Command) error {
228+
cli.Run(func(ctx context.Context, cmd *cli.Command) error {
224229
fmt.Fprintf(cmd.Stdout(), "Hello! My arguments were: %v\n", cmd.Args())
225230
return nil
226231
})

Taskfile.yml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,12 @@ tasks:
2626
preconditions:
2727
- sh: command -v golangci-lint
2828
msg: golangci-lint not installed, see https://golangci-lint.run/usage/install/#local-installation
29-
30-
- sh: command -v rumdl
31-
msg: rumdl not installed, see https://github.com/rvben/rumdl#installation
3229
sources:
3330
- "**/*.go"
3431
- .golangci.yml
3532
- "**/*.md"
3633
cmds:
3734
- golangci-lint fmt ./...
38-
- rumdl fmt --fix .
3935

4036
test:
4137
desc: Run the test suite
@@ -73,12 +69,8 @@ tasks:
7369

7470
- sh: command -v typos
7571
msg: requires typos-cli, run `brew install typos-cli`
76-
77-
- sh: command -v rumdl
78-
msg: rumdl not installed, see https://github.com/rvben/rumdl#installation
7972
cmds:
8073
- golangci-lint run --fix
81-
- rumdl check --fix .
8274
- typos
8375
- nilaway ./...
8476

command.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package cli // import "go.followtheprocess.codes/cli"
33

44
import (
5+
"context"
56
"errors"
67
"fmt"
78
"io"
@@ -116,7 +117,7 @@ type Command struct {
116117

117118
// run is the function actually implementing the command, the command is passed into the function for access
118119
// to things like cmd.Stdout().
119-
run func(cmd *Command) error
120+
run func(ctx context.Context, cmd *Command) error
120121

121122
// flags is the set of flags for this command.
122123
flags *flag.Set
@@ -184,7 +185,7 @@ type example struct {
184185
//
185186
// If the flags fail to parse, an error will be returned and the Run function
186187
// will not be called.
187-
func (cmd *Command) Execute() error {
188+
func (cmd *Command) Execute(ctx context.Context) error {
188189
if cmd == nil {
189190
return errors.New("Execute called on a nil Command")
190191
}
@@ -265,7 +266,7 @@ func (cmd *Command) Execute() error {
265266

266267
// If the command is runnable, go and execute its run function
267268
if cmd.run != nil {
268-
return cmd.run(cmd)
269+
return cmd.run(ctx, cmd)
269270
}
270271

271272
// The only way we get here is if the command has subcommands defined but got no arguments given to it

0 commit comments

Comments
 (0)