-
Notifications
You must be signed in to change notification settings - Fork 55
/
root.go
221 lines (180 loc) · 5.69 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
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
package cli
import (
"context"
"fmt"
"os"
"os/signal"
"time"
"unicode"
"github.com/spf13/cobra"
"github.com/auth0/auth0-cli/internal/analytics"
"github.com/auth0/auth0-cli/internal/ansi"
"github.com/auth0/auth0-cli/internal/buildinfo"
"github.com/auth0/auth0-cli/internal/display"
"github.com/auth0/auth0-cli/internal/instrumentation"
)
const rootShort = "Build, manage and test your Auth0 integrations from the command line."
const panicMessage = `
!! Uh oh. Something went wrong.
!! If this problem keeps happening feel free to report an issue at
!!
!! https://github.com/auth0/auth0-cli/issues/new/choose
`
// Execute is the primary entrypoint of the CLI app.
func Execute() {
cli := &cli{
renderer: display.NewRenderer(),
tracker: analytics.NewTracker(),
}
// Prevent sorting of commands.
cobra.EnableCommandSorting = false
rootCmd := buildRootCmd(cli)
rootCmd.SetUsageTemplate(namespaceUsageTemplate())
addPersistentFlags(rootCmd, cli)
addSubCommands(rootCmd, cli)
overrideHelpAndVersionFlagText(rootCmd)
defer func() {
if v := recover(); v != nil {
err := fmt.Errorf("panic: %v", v)
if instrumentation.ReportException(err) {
fmt.Print(panicMessage) // If we're in development mode, we should throw the panic for so we have less surprises.
} else {
panic(v) // For non-developers, we'll swallow the panics.
}
}
}()
// Platform specific terminal initialization:
// this should run for all commands,
// for most of the architectures there's no requirements.
ansi.InitConsole()
cancelCtx := contextWithCancel()
if err := rootCmd.ExecuteContext(cancelCtx); err != nil {
renderErrorMessage(cli.renderer, err.Error())
instrumentation.ReportException(err)
os.Exit(1) // nolint:gocritic
}
timeoutCtx, cancel := context.WithTimeout(cancelCtx, 3*time.Second)
// Defers are executed in LIFO order.
defer cancel()
defer cli.tracker.Wait(timeoutCtx) // No event should be tracked after this has run, or it will panic e.g. in earlier deferred functions.
}
func buildRootCmd(cli *cli) *cobra.Command {
rootCmd := &cobra.Command{
Use: "auth0",
SilenceUsage: true,
SilenceErrors: true,
Short: rootShort,
Long: rootShort + "\n" + getLogin(cli),
Version: buildinfo.GetVersionWithCommit(),
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
ansi.Initialize(cli.noColor)
prepareInteractivity(cmd)
cli.configureRenderer()
if !commandRequiresAuthentication(cmd.CommandPath()) {
return nil
}
// We're tracking the login command in its Run method, so
// we'll only add this defer if the command is not login.
defer func() {
if cli.tracker != nil &&
cmd.CommandPath() != "auth0 login" &&
cli.Config.IsLoggedInWithTenant(cli.tenant) {
cli.tracker.TrackCommandRun(cmd, cli.Config.InstallID)
}
}()
if err := cli.setupWithAuthentication(cmd.Context()); err != nil {
return err
}
return nil
},
}
return rootCmd
}
func commandRequiresAuthentication(invokedCommandName string) bool {
commandsWithNoAuthRequired := []string{
"auth0 completion",
"auth0 help",
"auth0 login",
"auth0 logout",
"auth0 tenants use",
"auth0 tenants list",
}
for _, cmd := range commandsWithNoAuthRequired {
if cmd == invokedCommandName {
return false
}
}
return true
}
func addPersistentFlags(rootCmd *cobra.Command, cli *cli) {
rootCmd.PersistentFlags().StringVar(&cli.tenant,
"tenant", cli.Config.DefaultTenant, "Specific tenant to use.")
rootCmd.PersistentFlags().BoolVar(&cli.debug,
"debug", false, "Enable debug mode.")
rootCmd.PersistentFlags().BoolVar(&cli.noInput,
"no-input", false, "Disable interactivity.")
rootCmd.PersistentFlags().BoolVar(&cli.noColor,
"no-color", false, "Disable colors.")
}
func addSubCommands(rootCmd *cobra.Command, cli *cli) {
// The order of the commands here matters.
// Add new commands in a place that reflect its
// relevance or relation with other commands.
rootCmd.AddCommand(loginCmd(cli))
rootCmd.AddCommand(logoutCmd(cli))
rootCmd.AddCommand(tenantsCmd(cli))
rootCmd.AddCommand(appsCmd(cli))
rootCmd.AddCommand(usersCmd(cli))
rootCmd.AddCommand(rulesCmd(cli))
rootCmd.AddCommand(actionsCmd(cli))
rootCmd.AddCommand(apisCmd(cli))
rootCmd.AddCommand(rolesCmd(cli))
rootCmd.AddCommand(organizationsCmd(cli))
rootCmd.AddCommand(universalLoginCmd(cli))
rootCmd.AddCommand(emailCmd(cli))
rootCmd.AddCommand(customDomainsCmd(cli))
rootCmd.AddCommand(quickstartsCmd(cli))
rootCmd.AddCommand(attackProtectionCmd(cli))
rootCmd.AddCommand(testCmd(cli))
rootCmd.AddCommand(logsCmd(cli))
rootCmd.AddCommand(apiCmd(cli))
rootCmd.AddCommand(terraformCmd(cli))
// Keep completion at the bottom.
rootCmd.AddCommand(completionCmd(cli))
}
func contextWithCancel() context.Context {
ctx, cancel := context.WithCancel(context.Background())
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt)
go func() {
<-ch
defer cancel()
os.Exit(0)
}()
return ctx
}
func overrideHelpAndVersionFlagText(cmd *cobra.Command) {
cmd.Flags().BoolP("version", "v", false, "Version for auth0.")
setHelpFlagTextFunc := func(c *cobra.Command) {
c.Flags().BoolP("help", "h", false, fmt.Sprintf("Help for %s.", c.Name()))
}
setHelpFlagTextFunc(cmd)
for _, c := range cmd.Commands() {
setHelpFlagTextFunc(c)
for _, c := range c.Commands() {
setHelpFlagTextFunc(c)
}
}
}
func renderErrorMessage(display *display.Renderer, errorMessage string) {
display.Heading(ansi.Red("error"))
rawErrorMessage := []rune(errorMessage)
humanReadableErrorMessage := string(
append(
[]rune{unicode.ToUpper(rawErrorMessage[0])},
rawErrorMessage[1:]...,
),
) + "."
display.Errorf(humanReadableErrorMessage)
display.Newline()
}