-
Notifications
You must be signed in to change notification settings - Fork 2k
/
run.go
234 lines (193 loc) · 6.33 KB
/
run.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
222
223
224
225
226
227
228
229
230
231
232
233
234
package commands
import (
"bytes"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/git-lfs/git-lfs/v3/config"
"github.com/git-lfs/git-lfs/v3/tools"
"github.com/git-lfs/git-lfs/v3/tr"
"github.com/spf13/cobra"
)
var (
commandFuncs []func() *cobra.Command
commandMu sync.Mutex
rootVersion bool
)
// NewCommand creates a new 'git-lfs' sub command, given a command name and
// command run function.
//
// Each command will initialize the local storage ('.git/lfs') directory when
// run, unless the PreRun hook is set to nil.
func NewCommand(name string, runFn func(*cobra.Command, []string)) *cobra.Command {
return &cobra.Command{Use: name, Run: runFn, PreRun: setupHTTPLogger}
}
// RegisterCommand creates a direct 'git-lfs' subcommand, given a command name,
// a command run function, and an optional callback during the command
// initialization process.
//
// The 'git-lfs' command initialization is deferred until the `commands.Run()`
// function is called. The fn callback is passed the output from NewCommand,
// and gives the caller the flexibility to customize the command by adding
// flags, tweaking command hooks, etc.
func RegisterCommand(name string, runFn func(cmd *cobra.Command, args []string), fn func(cmd *cobra.Command)) {
commandMu.Lock()
commandFuncs = append(commandFuncs, func() *cobra.Command {
cmd := NewCommand(name, runFn)
if fn != nil {
fn(cmd)
}
return cmd
})
commandMu.Unlock()
}
// Run initializes the 'git-lfs' command and runs it with the given stdin and
// command line args.
//
// It returns an exit code.
func Run() int {
log.SetOutput(ErrorWriter)
tr.InitializeLocale()
root := NewCommand("git-lfs", gitlfsCommand)
root.PreRun = nil
completionCmd := &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate completion script",
Long: tr.Tr.Get(`To load completions:
Bash:
$ source <(%[1]s completion bash)
# To load completions for each session, execute once:
# Linux:
$ %[1]s completion bash > /etc/bash_completion.d/%[1]s
# macOS:
$ %[1]s completion bash > $(brew --prefix)/etc/bash_completion.d/%[1]s
Zsh:
# If shell completion is not already enabled in your environment,
# you will need to enable it. You can execute the following once:
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
# To load completions for each session, execute once:
$ %[1]s completion zsh > "${fpath[1]}/_%[1]s"
# You will need to start a new shell for this setup to take effect.
fish:
$ %[1]s completion fish | source
# To load completions for each session, execute once:
$ %[1]s completion fish > ~/.config/fish/completions/%[1]s.fish
PowerShell:
PS> %[1]s completion powershell | Out-String | Invoke-Expression
# To load completions for every new session, run:
PS> %[1]s completion powershell > %[1]s.ps1
# and source this file from your PowerShell profile.
`, root.Name()),
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
Run: func(cmd *cobra.Command, args []string) {
switch args[0] {
case "bash":
completion := new(bytes.Buffer)
cmd.Root().GenBashCompletion(completion)
completion.WriteString("_git_lfs() { __start_git-lfs; }\n") // this is needed for git bash completion to pick up the completion for the subcommand
completion.WriteTo(os.Stdout)
case "zsh":
completion := new(bytes.Buffer)
cmd.Root().GenZshCompletion(completion)
newCompletion := bytes.NewBuffer(bytes.Replace(completion.Bytes(), []byte("requestComp=\"${words[1]}"), []byte("requestComp=\"git-${words[1]#*git-}"), 1)) // this is needed for git zsh completion to use the right command for completion
newCompletion.WriteTo(os.Stdout)
case "fish":
cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
}
},
}
root.AddCommand(completionCmd)
// Set up help/usage funcs based on manpage text
helpcmd := &cobra.Command{
Use: "help [command]",
Short: "Help about any command",
Long: `Help provides help for any command in the application.
Simply type ` + root.Name() + ` help [path to command] for full details.`,
Run: func(c *cobra.Command, args []string) {
cmd, _, e := c.Root().Find(args)
// In the case of "git lfs help config" or "git lfs help
// faq", pretend the last arg was "help" so our command
// lookup succeeds, since cmd will be ignored in
// helpCommand().
if e != nil && (args[0] == "config" || args[0] == "faq") {
cmd, _, e = c.Root().Find([]string{"help"})
}
if cmd == nil || e != nil {
c.Println(tr.Tr.Get("Unknown help topic %#q", args))
c.Root().Usage()
} else {
c.HelpFunc()(cmd, args)
}
},
}
root.SetHelpCommand(helpcmd)
root.SetHelpTemplate("{{.UsageString}}")
root.SetHelpFunc(helpCommand)
root.SetUsageFunc(usageCommand)
root.Flags().BoolVarP(&rootVersion, "version", "v", false, "")
canonicalizeEnvironment()
cfg = config.New()
for _, f := range commandFuncs {
if cmd := f(); cmd != nil {
root.AddCommand(cmd)
}
}
err := root.Execute()
closeAPIClient()
if err != nil {
return 127
}
return 0
}
func gitlfsCommand(cmd *cobra.Command, args []string) {
versionCommand(cmd, args)
if !rootVersion {
cmd.Usage()
}
}
func helpCommand(cmd *cobra.Command, args []string) {
if len(args) == 0 {
printHelp("git-lfs")
} else {
printHelp(args[0])
}
}
func usageCommand(cmd *cobra.Command) error {
printHelp(cmd.Name())
return nil
}
func printHelp(commandName string) {
if commandName == "--help" {
commandName = "git-lfs"
}
if txt, ok := ManPages[commandName]; ok {
fmt.Println(strings.TrimSpace(txt))
} else {
fmt.Println(tr.Tr.Get("Sorry, no usage text found for %q", commandName))
}
}
func setupHTTPLogger(cmd *cobra.Command, args []string) {
if len(os.Getenv("GIT_LOG_STATS")) < 1 {
return
}
logBase := filepath.Join(cfg.LocalLogDir(), "http")
if err := tools.MkdirAll(logBase, cfg); err != nil {
fmt.Fprintln(os.Stderr, tr.Tr.Get("Error logging HTTP stats: %s", err))
return
}
logFile := fmt.Sprintf("http-%d.log", time.Now().Unix())
file, err := os.Create(filepath.Join(logBase, logFile))
if err != nil {
fmt.Fprintln(os.Stderr, tr.Tr.Get("Error logging HTTP stats: %s", err))
} else {
getAPIClient().LogHTTPStats(file)
}
}