-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathroot.go
174 lines (141 loc) · 5.19 KB
/
root.go
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
package cmd
import (
"context"
"errors"
"fmt"
"os"
"os/signal"
"path/filepath"
"runtime/debug"
"strings"
"syscall"
"time"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"go.uber.org/zap"
falcon "github.com/bandprotocol/falcon/relayer"
)
const (
appName = "falcon"
defaultCoinType = 60
)
var defaultHome = filepath.Join(os.Getenv("HOME"), ".falcon")
// NewRootCmd returns the root command for falcon.
func NewRootCmd(log *zap.Logger) *cobra.Command {
app := falcon.NewApp(log, defaultHome, false, nil)
// RootCmd represents the base command when called without any subcommands
rootCmd := &cobra.Command{
Use: appName,
Short: "Falcon relays tss tunnel messages from BandChain to destination chains/smart contracts",
Long: strings.TrimSpace(`This application has:
1. Configuration Management: Handles the configuration of the program.
2. Key Management: Supports managing multiple keys across multiple chains.
3. Transaction Execution: Enables executing transactions on destination chains.
4. Query Functionality: Facilitates querying data from both source and destination chains.
NOTE: Most of the commands have aliases that make typing them much quicker
(i.e. 'falcon tx', 'falcon q', etc...)`),
}
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, _ []string) error {
// retrieve log level from viper
logLevelViper := viper.GetString("log-level")
if viper.GetBool("debug") {
logLevelViper = "debug"
}
logFormat := viper.GetString("log-format")
return app.Init(rootCmd.Context(), logLevelViper, logFormat)
}
rootCmd.PersistentPostRun = func(cmd *cobra.Command, _ []string) {
// Force syncing the logs before exit, if anything is buffered.
// check error of log.Sync() https://github.com/uber-go/zap/issues/991#issuecomment-962098428
if err := app.Log.Sync(); err != nil && !errors.Is(err, syscall.ENOTTY) {
fmt.Fprintf(os.Stderr, "failed to sync logs: %v\n", err)
}
}
// Register --home flag
rootCmd.PersistentFlags().StringVar(&app.HomePath, flagHome, defaultHome, "set home directory")
if err := viper.BindPFlag(flagHome, rootCmd.PersistentFlags().Lookup(flagHome)); err != nil {
panic(err)
}
// Register --debug flag
rootCmd.PersistentFlags().BoolVarP(&app.Debug, "debug", "d", false, "debug output")
if err := viper.BindPFlag("debug", rootCmd.PersistentFlags().Lookup("debug")); err != nil {
panic(err)
}
// Register --log-format flag
rootCmd.PersistentFlags().String("log-format", "auto", "log output format (auto, logfmt, json, or console)")
if err := viper.BindPFlag("log-format", rootCmd.PersistentFlags().Lookup("log-format")); err != nil {
panic(err)
}
// Register --log-level flag
rootCmd.PersistentFlags().String("log-level", "", "log level format (info, debug, warn, error, panic or fatal)")
if err := viper.BindPFlag("log-level", rootCmd.PersistentFlags().Lookup("log-level")); err != nil {
panic(err)
}
// Register subcommands
rootCmd.AddCommand(
configCmd(app),
chainsCmd(app),
keysCmd(app),
lineBreakCommand(),
transactionCmd(app),
queryCmd(app),
startCmd(app),
lineBreakCommand(),
versionCmd(app),
)
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() error {
cobra.EnableCommandSorting = false
rootCmd := NewRootCmd(nil)
rootCmd.SilenceUsage = true
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt)
go func() {
// Wait for interrupt signal.
sig := <-sigCh
// Cancel the context to signal to the rest of the application to shut down.
cancel()
// Short delay before printing the received signal message.
// This should result in cleaner output from non-interactive commands that stop quickly.
time.Sleep(250 * time.Millisecond)
fmt.Fprintf(
os.Stderr,
"Received signal %v. Attempting clean shutdown. Send interrupt again to force hard shutdown.\n",
sig,
)
// Dump all goroutines on panic, not just the current one.
debug.SetTraceback("all")
// Block waiting for a second interrupt or a timeout.
// The main goroutine ought to finish before either case is reached.
// But if a case is reached, panic so that we get a non-zero exit and a dump of remaining goroutines.
select {
case <-time.After(time.Minute):
panic(errors.New("falcon did not shut down within one minute of interrupt"))
case sig := <-sigCh:
panic(fmt.Errorf("received signal %v; forcing quit", sig))
}
}()
return rootCmd.ExecuteContext(ctx)
}
// lineBreakCommand returns a new instance of the lineBreakCommand every time to avoid
// data races in concurrent tests exercising commands.
func lineBreakCommand() *cobra.Command {
return &cobra.Command{Run: func(*cobra.Command, []string) {}}
}
// withUsage wraps a PositionalArgs to display usage only when the PositionalArgs
// variant is violated.
func withUsage(inner cobra.PositionalArgs) cobra.PositionalArgs {
return func(cmd *cobra.Command, args []string) error {
if err := inner(cmd, args); err != nil {
cmd.Root().SilenceUsage = false
cmd.SilenceUsage = false
return err
}
return nil
}
}