Skip to content

Commit 6e5bb98

Browse files
authored
feat(main): switched cli parsing to cobra and updated readme as well (#548)
Signed-off-by: Akhil Repala <arepala@blinklabs.io>
1 parent 03d287d commit 6e5bb98

File tree

7 files changed

+95
-75
lines changed

7 files changed

+95
-75
lines changed

README.md

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,43 +16,46 @@ Events are created with a simple schema.
1616

1717
```json
1818
{
19-
"type": "event type",
20-
"timestamp": "wall clock timestamp of event",
21-
"context": "metadata about the event",
22-
"payload": "the full event specific payload"
19+
"type": "event type",
20+
"timestamp": "wall clock timestamp of event",
21+
"context": "metadata about the event",
22+
"payload": "the full event specific payload"
2323
}
2424
```
2525

2626
The chainsync input produces three event types: `block`, `rollback`, and
2727
`transaction`. Each type has a unique payload.
2828

2929
block:
30+
3031
```json
3132
{
32-
"context": {
33-
"blockNumber": 123,
34-
"slotNumber": 1234567,
35-
},
36-
"payload": {
37-
"blockBodySize": 123,
38-
"issuerVkey": "a712f81ab2eac...",
39-
"blockHash": "abcd123...",
40-
"blockCbor": "85828a1a000995c21..."
41-
}
33+
"context": {
34+
"blockNumber": 123,
35+
"slotNumber": 1234567
36+
},
37+
"payload": {
38+
"blockBodySize": 123,
39+
"issuerVkey": "a712f81ab2eac...",
40+
"blockHash": "abcd123...",
41+
"blockCbor": "85828a1a000995c21..."
42+
}
4243
}
4344
```
4445

4546
rollback:
47+
4648
```json
4749
{
48-
"payload": {
49-
"blockHash": "abcd123...",
50-
"slotNumber": 1234567
51-
}
50+
"payload": {
51+
"blockHash": "abcd123...",
52+
"slotNumber": 1234567
53+
}
5254
}
5355
```
5456

5557
transaction:
58+
5659
```json
5760
{
5861
"context": {
@@ -105,22 +108,23 @@ Adder supports multiple configuration methods for versatility: commandline
105108
arguments, YAML config file, and environment variables (in that order).
106109

107110
You can get a list of all available commandline arguments by using the
108-
`-h`/`-help` flag.
111+
`--help` flag.
109112

110113
```bash
111-
$ ./adder -h
112-
Usage of adder:
113-
-config string
114-
path to config file to load
115-
-input string
116-
input plugin to use, 'list' to show available (default "chainsync")
117-
-input-chainsync-address string
118-
specifies the TCP address of the node to connect to
114+
$ ./adder --help
115+
116+
Usage:
117+
adder [flags]
118+
119+
Flags:
120+
--config string path to config file to load
121+
--input string input plugin to use, 'list' to show available (default "chainsync")
122+
--input-chainsync-address string
123+
specifies the TCP address of the node to connect to
119124
...
120-
-output string
121-
output plugin to use, 'list' to show available (default "log")
122-
-output-log-level string
123-
specifies the log level to use (default "info")
125+
--output string output plugin to use, 'list' to show available (default "log")
126+
--output-log-level string specifies the log level to use (default "info")
127+
-h, --help help for adder
124128
```
125129

126130
Each commandline argument (other than `-config`) has a corresponding environment
@@ -133,7 +137,7 @@ environment variable, and `-output` has `OUTPUT`.
133137
Core configuration options can be set using environment variables:
134138

135139
- `INPUT` - Input plugin to use (default: "chainsync")
136-
- `OUTPUT` - Output plugin to use (default: "log")
140+
- `OUTPUT` - Output plugin to use (default: "log")
137141
- `KUPO_URL` - URL for Kupo service integration
138142
- `LOGGING_LEVEL` - Log level (default: "info")
139143
- `API_ADDRESS` - API server listen address (default: "0.0.0.0")
@@ -144,14 +148,17 @@ Core configuration options can be set using environment variables:
144148
Genesis configuration can also be controlled via environment variables:
145149

146150
**Network Transition:**
151+
147152
- `SHELLEY_TRANS_EPOCH` - Epoch number when Shelley era begins (default: 208 for mainnet)
148153

149154
**Byron Genesis:**
155+
150156
- `BYRON_GENESIS_END_SLOT` - End slot for Byron era
151157
- `BYRON_GENESIS_EPOCH_LENGTH` - Slot length of Byron epochs (default: 21600)
152158
- `BYRON_GENESIS_BYRON_SLOTS_PER_EPOCH` - Byron slots per epoch
153159

154160
**Shelley Genesis:**
161+
155162
- `SHELLEY_GENESIS_EPOCH_LENGTH` - Slot length of Shelley epochs (default: 432000)
156163

157164
You can also specify each option in the config file.
@@ -211,7 +218,7 @@ filters will be output.
211218

212219
```bash
213220
export INPUT_CHAINSYNC_NETWORK=preview
214-
./adder
221+
./adder
215222
```
216223

217224
Alternatively using equivalent commandline options:

cmd/adder/main.go

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,22 @@ import (
3434
"github.com/blinklabs-io/adder/pipeline"
3535
"github.com/blinklabs-io/adder/plugin"
3636
"github.com/inconshreveable/mousetrap"
37+
"github.com/spf13/cobra"
3738
"go.uber.org/automaxprocs/maxprocs"
3839
)
3940

40-
var programName string = "adder"
41+
var (
42+
programName string = "adder"
43+
cfg = config.GetConfig()
44+
rootCmd = &cobra.Command{
45+
Use: programName,
46+
SilenceUsage: true,
47+
Args: cobra.NoArgs,
48+
RunE: func(cmd *cobra.Command, args []string) error {
49+
return run()
50+
},
51+
}
52+
)
4153

4254
func slogPrintf(format string, v ...any) {
4355
slog.Info(fmt.Sprintf(format, v...))
@@ -46,6 +58,7 @@ func slogPrintf(format string, v ...any) {
4658
func init() {
4759
if os.Args != nil && os.Args[0] != programName {
4860
programName = os.Args[0]
61+
rootCmd.Use = programName
4962
}
5063

5164
// Bail if we were run via double click on Windows, borrowed from ngrok
@@ -64,58 +77,47 @@ func init() {
6477
os.Exit(1)
6578
}
6679
}
67-
}
68-
69-
func main() {
70-
cfg := config.GetConfig()
71-
72-
if os.Args == nil {
73-
fmt.Println("Failed to detect arguments, aborting")
74-
os.Exit(1)
75-
}
7680

77-
if err := cfg.ParseCmdlineArgs(programName, os.Args[1:]); err != nil {
78-
fmt.Printf("Failed to parse commandline: %s\n", err)
79-
os.Exit(1)
81+
if err := cfg.BindFlags(rootCmd.Flags()); err != nil {
82+
panic(err)
8083
}
84+
}
8185

86+
func run() error {
8287
if cfg.Version {
8388
fmt.Printf("%s %s\n", programName, version.GetVersionString())
84-
os.Exit(0)
89+
return nil
8590
}
8691

8792
if cfg.Input == "list" {
8893
fmt.Printf("Available input plugins:\n\n")
8994
for _, plugin := range plugin.GetPlugins(plugin.PluginTypeInput) {
9095
fmt.Printf("%- 14s %s\n", plugin.Name, plugin.Description)
9196
}
92-
return
97+
return nil
9398
}
9499

95100
if cfg.Output == "list" {
96101
fmt.Printf("Available output plugins:\n\n")
97102
for _, plugin := range plugin.GetPlugins(plugin.PluginTypeOutput) {
98103
fmt.Printf("%- 14s %s\n", plugin.Name, plugin.Description)
99104
}
100-
return
105+
return nil
101106
}
102107

103108
// Load config
104109
if err := cfg.Load(cfg.ConfigFile); err != nil {
105-
fmt.Printf("Failed to load config: %s\n", err)
106-
os.Exit(1)
110+
return fmt.Errorf("failed to load config: %w", err)
107111
}
108112

109113
// Process config for plugins
110114
if err := plugin.ProcessConfig(cfg.Plugin); err != nil {
111-
fmt.Printf("Failed to process plugin config: %s\n", err)
112-
os.Exit(1)
115+
return fmt.Errorf("failed to process plugin config: %w", err)
113116
}
114117

115118
// Process env vars for plugins
116119
if err := plugin.ProcessEnvVars(); err != nil {
117-
fmt.Printf("Failed to process env vars: %s\n", err)
118-
os.Exit(1)
120+
return fmt.Errorf("failed to process env vars: %w", err)
119121
}
120122

121123
// Configure logging
@@ -128,7 +130,7 @@ func main() {
128130
if err != nil {
129131
// If we hit this, something really wrong happened
130132
logger.Error(err.Error())
131-
os.Exit(1)
133+
return err
132134
}
133135

134136
// Start debug listener
@@ -170,7 +172,7 @@ func main() {
170172
input := plugin.GetPlugin(plugin.PluginTypeInput, cfg.Input)
171173
if input == nil {
172174
logger.Error("unknown input: " + cfg.Input)
173-
os.Exit(1)
175+
return fmt.Errorf("unknown input: %s", cfg.Input)
174176
}
175177
pipe.AddInput(input)
176178

@@ -184,7 +186,7 @@ func main() {
184186
output := plugin.GetPlugin(plugin.PluginTypeOutput, cfg.Output)
185187
if output == nil {
186188
logger.Error("unknown output: " + cfg.Output)
187-
os.Exit(1)
189+
return fmt.Errorf("unknown output: %s", cfg.Output)
188190
}
189191
// Check if output plugin implements APIRouteRegistrar
190192
if registrar, ok := any(output).(api.APIRouteRegistrar); ok {
@@ -195,13 +197,13 @@ func main() {
195197
// Start API after plugins are configured
196198
if err := apiInstance.Start(); err != nil {
197199
logger.Error(fmt.Sprintf("failed to start API: %s", err))
198-
os.Exit(1)
200+
return fmt.Errorf("failed to start API: %w", err)
199201
}
200202

201203
// Start pipeline and wait for error
202204
if err := pipe.Start(); err != nil {
203205
logger.Error(fmt.Sprintf("failed to start pipeline: %s", err))
204-
os.Exit(1)
206+
return fmt.Errorf("failed to start pipeline: %w", err)
205207
}
206208

207209
// Setup graceful shutdown
@@ -225,8 +227,15 @@ func main() {
225227
// Graceful shutdown using Stop() method
226228
if err := pipe.Stop(); err != nil {
227229
logger.Error(fmt.Sprintf("failed to stop pipeline: %s", err))
228-
os.Exit(1)
230+
return fmt.Errorf("failed to stop pipeline: %w", err)
229231
}
230232

231233
logger.Info("Adder stopped gracefully")
234+
return nil
235+
}
236+
237+
func main() {
238+
if err := rootCmd.Execute(); err != nil {
239+
os.Exit(1)
240+
}
232241
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ require (
1414
github.com/gin-gonic/gin v1.11.0
1515
github.com/inconshreveable/mousetrap v1.1.0
1616
github.com/kelseyhightower/envconfig v1.4.0
17+
github.com/spf13/cobra v1.8.1
18+
github.com/spf13/pflag v1.0.6
1719
github.com/stretchr/testify v1.11.1
1820
github.com/swaggo/files v1.0.1
1921
github.com/swaggo/gin-swagger v1.6.1

go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI
6161
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
6262
github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80=
6363
github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0=
64+
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
6465
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
6566
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6667
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -218,10 +219,16 @@ github.com/quic-go/quic-go v0.54.1 h1:4ZAWm0AhCb6+hE+l5Q1NAL0iRn/ZrMwqHRGQiFwj2e
218219
github.com/quic-go/quic-go v0.54.1/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
219220
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
220221
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
222+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
221223
github.com/sergeymakinen/go-bmp v1.0.0 h1:SdGTzp9WvCV0A1V0mBeaS7kQAwNLdVJbmHlqNWq0R+M=
222224
github.com/sergeymakinen/go-bmp v1.0.0/go.mod h1:/mxlAQZRLxSvJFNIEGGLBE/m40f3ZnUifpgVDlcUIEY=
223225
github.com/sergeymakinen/go-ico v1.0.0-beta.0 h1:m5qKH7uPKLdrygMWxbamVn+tl2HfiA3K6MFJw4GfZvQ=
224226
github.com/sergeymakinen/go-ico v1.0.0-beta.0/go.mod h1:wQ47mTczswBO5F0NoDt7O0IXgnV4Xy3ojrroMQzyhUk=
227+
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
228+
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
229+
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
230+
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
231+
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
225232
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
226233
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
227234
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=

internal/config/config.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
package config
1616

1717
import (
18-
"flag"
1918
"fmt"
2019
"os"
2120

2221
"github.com/blinklabs-io/adder/plugin"
2322
"github.com/kelseyhightower/envconfig"
23+
"github.com/spf13/pflag"
2424
"gopkg.in/yaml.v2"
2525
)
2626

@@ -151,8 +151,7 @@ func (c *Config) Load(configFile string) error {
151151
return nil
152152
}
153153

154-
func (c *Config) ParseCmdlineArgs(programName string, args []string) error {
155-
fs := flag.NewFlagSet(programName, flag.ExitOnError)
154+
func (c *Config) BindFlags(fs *pflag.FlagSet) error {
156155
fs.StringVar(&c.ConfigFile, "config", "", "path to config file to load")
157156
fs.BoolVar(&c.Version, "version", false, "show version and exit")
158157
fs.StringVar(
@@ -167,13 +166,7 @@ func (c *Config) ParseCmdlineArgs(programName string, args []string) error {
167166
DefaultOutputPlugin,
168167
"output plugin to use, 'list' to show available",
169168
)
170-
if err := plugin.PopulateCmdlineOptions(fs); err != nil {
171-
return err
172-
}
173-
if err := fs.Parse(args); err != nil {
174-
return err
175-
}
176-
return nil
169+
return plugin.PopulateCmdlineOptions(fs)
177170
}
178171

179172
// GetConfig returns the global config instance

plugin/option.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@
1515
package plugin
1616

1717
import (
18-
"flag"
1918
"fmt"
2019
"os"
2120
"strconv"
2221
"strings"
22+
23+
"github.com/spf13/pflag"
2324
)
2425

2526
type PluginOptionType int
@@ -42,7 +43,7 @@ type PluginOption struct {
4243
}
4344

4445
func (p *PluginOption) AddToFlagSet(
45-
fs *flag.FlagSet,
46+
fs *pflag.FlagSet,
4647
pluginType string,
4748
pluginName string,
4849
) error {

0 commit comments

Comments
 (0)