forked from tcnksm/gcli
/
new.go
213 lines (175 loc) · 6.53 KB
/
new.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
package command
import (
"bufio"
"flag"
"fmt"
"io"
"os"
"strings"
"github.com/tcnksm/gcli/skeleton"
"github.com/tcnksm/go-gitconfig"
)
// NewCommand is a Command that generates a new cli project
type NewCommand struct {
Meta
}
// Run generates a new cli project. It returns exit code
func (c *NewCommand) Run(args []string) int {
var (
commands []skeleton.Command
flags []skeleton.Flag
frameworkStr string
owner string
skipTest bool
verbose bool
)
uflag := flag.NewFlagSet("new", flag.ContinueOnError)
uflag.Usage = func() { c.UI.Error(c.Help()) }
uflag.Var((*CommandFlag)(&commands), "command", "command")
uflag.Var((*CommandFlag)(&commands), "c", "command (short)")
uflag.Var((*FlagFlag)(&flags), "flag", "flag")
uflag.Var((*FlagFlag)(&flags), "f", "flag (short)")
uflag.StringVar(&frameworkStr, "framework", defaultFrameworkString, "framework")
uflag.StringVar(&frameworkStr, "F", defaultFrameworkString, "framework (short)")
uflag.StringVar(&owner, "owner", "", "owner")
uflag.StringVar(&owner, "o", "", "owner (short)")
uflag.BoolVar(&skipTest, "skip-test", false, "skip-test")
uflag.BoolVar(&skipTest, "T", false, "skip-test (short)")
uflag.BoolVar(&verbose, "verbose", false, "verbose")
uflag.BoolVar(&verbose, "V", false, "verbose (short)")
errR, errW := io.Pipe()
errScanner := bufio.NewScanner(errR)
uflag.SetOutput(errW)
go func() {
for errScanner.Scan() {
c.UI.Error(errScanner.Text())
}
}()
if err := uflag.Parse(args); err != nil {
return 1
}
parsedArgs := uflag.Args()
if len(parsedArgs) != 1 {
msg := fmt.Sprintf("Invalid arguments: %s", strings.Join(parsedArgs, " "))
c.UI.Error(msg)
return 1
}
name := parsedArgs[0]
// TODO, should be configurable
// or chagne direcotry to GOPATH/github.com/owner/output
// Some gcli template assume command is executed
// from GOPATH/github.com/owner
output := name
if _, err := os.Stat(output); !os.IsNotExist(err) {
msg := fmt.Sprintf("Cannot create directory %s: file exists", output)
c.UI.Error(msg)
return 1
}
framework, err := skeleton.FrameworkByName(frameworkStr)
if err != nil {
c.UI.Error(fmt.Sprintf("Failed to generate %q: %s", name, err.Error()))
return 1
}
// Use .gitconfig value.
if owner == "" {
owner, err = gitconfig.GithubUser()
if err != nil {
owner, err = gitconfig.Username()
if err != nil {
msg := "Cannot find owner name\n" +
"By default, owener name is retrieved from `~/.gitcofig` file.\n" +
"Please set one via -owner option or `~/.gitconfig` file."
c.UI.Error(msg)
return 1
}
}
}
// Define Executable
executable := &skeleton.Executable{
Name: name,
Owner: owner,
Commands: commands,
Flags: flags,
Version: skeleton.DefaultVersion,
Description: skeleton.DefaultDescription,
}
// Channels to receive artifact path (result) and error
artifactCh, errCh := make(chan string), make(chan error)
// Define Skeleton
skeleton := &skeleton.Skeleton{
Path: output,
Framework: framework,
SkipTest: skipTest,
Executable: executable,
ArtifactCh: artifactCh,
ErrCh: errCh,
Verbose: verbose,
LogWriter: os.Stdout,
}
// Create project directory
doneCh := skeleton.Generate()
for {
select {
case artifact := <-artifactCh:
c.UI.Output(fmt.Sprintf(" Created %s", artifact))
case err := <-errCh:
c.UI.Error(fmt.Sprintf("Failed to generate %s: %s", output, err.Error()))
// If some file are created before error happend
// Should be cleanuped
if _, err := os.Stat(output); !os.IsNotExist(err) {
c.UI.Output(fmt.Sprintf("Cleanup %s", output))
os.RemoveAll(output)
}
return ExitCodeFailed
case <-doneCh:
c.UI.Info(fmt.Sprintf("====> Successfully generated %s", name))
return ExitCodeOK
}
}
}
// Synopsis is a one-line, short synopsis of the command.
func (c *NewCommand) Synopsis() string {
return "Generate new cli project"
}
// Help is a long-form help text that includes the command-line
// usage, a brief few sentences explaining the function of the command,
// and the complete list of flags the command accepts.
func (c *NewCommand) Help() string {
helpText := `
Usage: gcli new [option] NAME
Generate new cli skeleton project. At least, you must provide executable
name. You can select cli package and set commands via command line option.
See more about that on Options section. By default, gcli use codegangsta/cli.
To check cli framework you can use, run 'gcli list'.
Options:
-command=name, -c Command name which you want to add.
This is valid only when cli pacakge support commands.
This can be specified multiple times. Synopsis can be
set after ":". Namely, you can specify command by
-command=NAME:SYNOPSYS. Only NAME is required.
You can set multiple variables at same time with ","
separator.
-flag=name, -f Global flag option name which you want to add.
This can be specified multiple times. By default, flag type
is string and its description is empty. You can set them,
with ":" separator. Namaly, you can specify flag by
-flag=NAME:TYPE:DESCIRPTION. Order must be flow this and
TYPE must be string, bool or int. Only NAME is required.
You can set multiple variables at same time with ","
separator.
-framework=name, -F Cli framework name. By default, gcli use "codegangsta/cli"
To check cli framework you can use, run 'gcli list'.
If you set invalid framework, it will be failed.
-owner=name, -o Command owner (author) name. This value is also used for
import path name. By default, owner name is extracted from
~/.gitconfig variable.
-skip-test, -T Skip generating *_test.go file. By default, gcli generates
test file If you specify this flag, gcli will not generate
test files.
Examples:
This example shows creating todo command application skeleton
which has 'add' and 'delete' command by using mitchellh/cli package.
$ gcli new -command=add:"Add new task" -commnad=delete:"delete task" todo
`
return strings.TrimSpace(helpText)
}