-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcli.go
210 lines (194 loc) · 7.33 KB
/
cli.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
package jkl
import (
"fmt"
"io"
"os"
"path/filepath"
"github.com/spf13/cobra"
)
// Main is an exported function that calls the same entrypoint as the jkl
// binary. This is used by tests, to run jkl. See script_test.go.
func Main() (exitCode int) {
err := RunCLI(os.Args, os.Stdout, os.Stderr)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
return 1
}
return 0
}
// RunCLI determines how this binary was run, and either calls RunShim() or
// processes JKL commands and arguments.
func RunCLI(args []string, output, errOutput io.Writer) error {
j, err := NewJKL()
if err != nil {
return err
}
calledProgName := filepath.Base(args[0])
if calledProgName != callMeProgName { // Running as a shim
return j.RunShim(args)
}
// Cobra commands are defined here to inharit the JKL instance.
var debugFlagEnabled bool
var rootCmd = &cobra.Command{
Use: "jkl",
Short: "A command-line tool version manager",
Long: `JKL is a version manager for other command-line tools. It installs tools quickly with minimal input, and helps you switch versions of tools while you work.`,
SilenceErrors: true, // will be bubbled up and output elsewhere
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if os.Getenv("JKL_DEBUG") != "" || debugFlagEnabled {
EnableDebugOutput()
}
err := j.displayPreFlightCheck(cmd.OutOrStdout())
return err
},
RunE: func(cmd *cobra.Command, args []string) error {
err := j.displayGettingStarted(cmd.OutOrStdout())
return err
},
}
rootCmd.CompletionOptions.DisableDefaultCmd = true // Until completion behavior is tested
rootCmd.PersistentFlags().BoolVarP(&debugFlagEnabled, "debug", "D", false, "Enable debug output (also enabled by setting the JKL_DEBUG environment variable to any value).")
var versionOnly, commitOnly bool
var versionCmd = &cobra.Command{
Use: "version",
Short: "Display the jkl version and git commit",
Long: "Display the jkl version and git commit",
Aliases: []string{"ver", "v"},
Run: func(cmd *cobra.Command, args []string) {
if versionOnly {
fmt.Fprintf(cmd.OutOrStdout(), "%s\n", Version)
return
}
if commitOnly {
fmt.Fprintf(cmd.OutOrStdout(), "%s\n", GitCommit)
return
}
fmt.Fprintf(cmd.OutOrStdout(), "%s version %s, git commit %s\n", callMeProgName, Version, GitCommit)
g, err := NewGithubRepo("ivanfetch/jkl")
if err != nil {
// Since the current version was displayed, do not display an error if unable to contact Github.
debugLog.Printf("unable to determine if there is a newer version of jkl: %v\n", err)
return
}
latestTag, err := g.GetTagForLatestRelease()
if err != nil {
debugLog.Printf("unable to determine if there is a newer version of jkl: %v\n", err)
return
}
if latestTag != "v"+Version {
fmt.Printf("The latest released version of jkl is %s - to update, run: jkl update\n", latestTag)
}
},
}
versionCmd.Flags().BoolVarP(&versionOnly, "version-only", "v", false, "Only output the jkl version.")
versionCmd.Flags().BoolVarP(&commitOnly, "commit-only", "c", false, "Only output the jkl git commit.")
versionCmd.MarkFlagsMutuallyExclusive("version-only", "commit-only")
rootCmd.AddCommand(versionCmd)
var installCmd = &cobra.Command{
Use: "install <provider>:<source>[:version]",
Short: "Install a command-line tool",
Long: `Install a command-line tool.
If no version is specified, the latest version will be installed (not including pre-release versions). A partial major version will match the latest minor one.
Available providers are:
github|gh - install a Github release. The source is specified as <Github user>/<Github repository>.
hashicorp|hashi - install a Hashicorp product. The source is the name of the Hashicorp product.`,
Example: ` jkl install github:fairwindsops/rbac-lookup
jkl install github:fairwindsops/rbac-lookup:0.9.0
jkl install github:fairwindsops/rbac-lookup:0.8
jkl install hashicorp:terraform:1.2`,
Aliases: []string{"add", "inst", "i"},
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("Please specify what you would like to install, using a colon-separated provider, source, and optional version. Run %s install -h for more information about installation providers, and matching tool versions.", callMeProgName)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
_, err := j.Install(args[0])
if err != nil {
return err
}
return nil
},
}
rootCmd.AddCommand(installCmd)
var uninstallCmd = &cobra.Command{
Use: "uninstall <tool name>[:version]",
Short: "Uninstall a command-line tool",
Long: `Uninstall a command-line tool managed by JKL.
A tool version must be exact, as shown by: jkl list <tool name>
If no version is specified, all versions of the tool will be uninstalled.`,
Example: ` jkl uninstall rbac-lookup
jkl uninstall rbac-lookup:0.9.0`,
Aliases: []string{"remove", "uninst", "u", "rm"},
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("Please specify which tool JKL should uninstall, using the form: ToolName[:version]\nThe `jkl list` command will show JKL-managed tools. Run %s uninstall -h for more information about uninstallation.", callMeProgName)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
err := j.Uninstall(args[0])
if err != nil {
return err
}
return nil
},
}
rootCmd.AddCommand(uninstallCmd)
var listCmd = &cobra.Command{
Use: "list [<tool name>]",
Short: "List installed command-line tools or installed versions for a specific tool",
Long: `List command-line tools that jkl has installed.
With no arguments, all tools that jkl has installed are shown. With a tool name, jkl lists installed versions of that tool.`,
Example: ` jkl list
jkl list rbac-lookup`,
Aliases: []string{"ls", "lis", "l"},
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 1 {
err := j.displayInstalledVersionsOfTool(cmd.OutOrStdout(), args[0])
return err
}
err := j.displayInstalledTools(cmd.OutOrStdout())
return err
},
}
rootCmd.AddCommand(listCmd)
var updateSelfCmd = &cobra.Command{
Use: "update",
Short: "Update JKL to the latest release",
Long: fmt.Sprintf("Update the JKL binary to the latest release. This will replace %s, retaining its current file mode.", j.executable),
Aliases: []string{"update-self", "update-jkl"},
RunE: func(cmd *cobra.Command, args []string) error {
newVersion, isNewVersion, err := j.UpdateSelf()
if err != nil {
return err
}
if isNewVersion {
fmt.Printf("%s was updated to version %s\n", j.executable, newVersion)
} else {
fmt.Printf("JKL is already at the most current version (%s).\n", Version)
}
return nil
},
}
rootCmd.AddCommand(updateSelfCmd)
cobra.CheckErr(rootCmd.Execute())
return nil
}
// RunShim executes the desired version of the tool which the JKL shim was called.
// The remaining command-line arguments are passed to the actual tool being
// executed.
func (j JKL) RunShim(args []string) error {
if os.Getenv("JKL_DEBUG") != "" {
EnableDebugOutput()
}
calledProgName := filepath.Base(args[0])
tool := j.getManagedTool(calledProgName)
err := tool.Run(args[1:])
if err != nil {
return fmt.Errorf("jkl: %v", err)
}
return nil
}