github.com/goonzoid/gcli@v0.2.3-0.20150926213610-155587606ea1/command/apply.go (about) 1 package command 2 3 import ( 4 "bufio" 5 "flag" 6 "fmt" 7 "io" 8 "os" 9 "strings" 10 11 "github.com/BurntSushi/toml" 12 "github.com/tcnksm/gcli/skeleton" 13 ) 14 15 // ApplyCommand is a Command that generates a new cli project 16 type ApplyCommand struct { 17 Meta 18 } 19 20 // Run generates a new cli project. It returns exit code 21 func (c *ApplyCommand) Run(args []string) int { 22 23 var ( 24 frameworkStr string 25 skipTest bool 26 verbose bool 27 owner string 28 name string 29 ) 30 31 uflag := flag.NewFlagSet("apply", flag.ContinueOnError) 32 uflag.Usage = func() { c.UI.Error(c.Help()) } 33 34 uflag.StringVar(&frameworkStr, "framework", "", "framework") 35 uflag.StringVar(&frameworkStr, "F", "", "framework (short)") 36 37 uflag.BoolVar(&skipTest, "skip-test", false, "skip-test") 38 uflag.BoolVar(&skipTest, "T", false, "skip-test (short)") 39 40 uflag.BoolVar(&verbose, "verbose", false, "verbose") 41 uflag.BoolVar(&verbose, "V", false, "verbose (short)") 42 43 // These flags are supposed only to use in test 44 uflag.StringVar(&owner, "owner", "", "owner (Should only for test)") 45 uflag.StringVar(&name, "name", "", "name (Should only for test)") 46 47 errR, errW := io.Pipe() 48 errScanner := bufio.NewScanner(errR) 49 uflag.SetOutput(errW) 50 51 go func() { 52 for errScanner.Scan() { 53 c.UI.Error(errScanner.Text()) 54 } 55 }() 56 57 if err := uflag.Parse(args); err != nil { 58 return 1 59 } 60 61 parsedArgs := uflag.Args() 62 if len(parsedArgs) != 1 { 63 c.UI.Error("Invalid argument: Usage glic apply [options] FILE") 64 return 1 65 } 66 67 designFile := parsedArgs[0] 68 c.UI.Info(fmt.Sprintf( 69 "Use design template %q for generating new cli project", designFile)) 70 71 // Check file is exist or not 72 if _, err := os.Stat(designFile); os.IsNotExist(err) { 73 c.UI.Error(fmt.Sprintf( 74 "Design file does not exsit")) 75 return 1 76 } 77 78 // Decode design file as skeleton.Executable 79 executable := skeleton.NewExecutable() 80 if _, err := toml.DecodeFile(designFile, executable); err != nil { 81 c.UI.Error(fmt.Sprintf( 82 "Failed to decode design file %q: %s", designFile, err)) 83 return 1 84 } 85 86 // validate executable 87 88 if errs := executable.Validate(); len(errs) > 0 { 89 c.UI.Error(fmt.Sprintf( 90 "%q is not valid template file. It has %d errors:", designFile, len(errs))) 91 for _, err := range errs { 92 c.UI.Error(fmt.Sprintf( 93 " * %s", err.Error())) 94 } 95 return ExitCodeFailed 96 } 97 98 output := executable.Name 99 if _, err := os.Stat(output); !os.IsNotExist(err) { 100 msg := fmt.Sprintf("Cannot create directory %s: file exists", output) 101 c.UI.Error(msg) 102 return 1 103 } 104 105 // Check option input first and if it's specified use it 106 if len(frameworkStr) == 0 { 107 if len(executable.FrameworkStr) != 0 { 108 // If FrameworStr is specified from design file use it 109 frameworkStr = executable.FrameworkStr 110 } else { 111 frameworkStr = defaultFrameworkString 112 } 113 } 114 115 fmt.Println(frameworkStr) 116 framework, err := skeleton.FrameworkByName(frameworkStr) 117 if err != nil { 118 c.UI.Error(fmt.Sprintf("Failed to generate %q: %s", executable.Name, err.Error())) 119 return 1 120 } 121 122 // Run fix flag struct. complement empty variable. 123 if len(executable.Flags) > 0 { 124 fixedFlags := []skeleton.Flag{} 125 for _, f := range executable.Flags { 126 if err := f.Fix(); err != nil { 127 c.UI.Error(fmt.Sprintf( 128 "Failed to fix flag struct: %s", err.Error())) 129 return 1 130 } 131 fixedFlags = append(fixedFlags, f) 132 } 133 134 executable.Flags = fixedFlags 135 } 136 137 if len(name) != 0 { 138 executable.Name = name 139 output = name 140 } 141 142 if len(owner) != 0 { 143 executable.Owner = owner 144 } 145 146 // Channels to receive artifact path (result) and error 147 artifactCh, errCh := make(chan string), make(chan error) 148 149 // Define Skeleton 150 skeleton := &skeleton.Skeleton{ 151 Path: output, 152 Framework: framework, 153 SkipTest: skipTest, 154 Executable: executable, 155 ArtifactCh: artifactCh, 156 ErrCh: errCh, 157 Verbose: verbose, 158 LogWriter: os.Stdout, 159 } 160 161 // Create project directory 162 doneCh := skeleton.Generate() 163 164 for { 165 select { 166 case artifact := <-artifactCh: 167 c.UI.Output(fmt.Sprintf(" Created %s", artifact)) 168 case err := <-errCh: 169 c.UI.Error(fmt.Sprintf("Failed to generate %s: %s", output, err.Error())) 170 171 // If some file are created before error happend 172 // Should be cleanuped 173 if _, err := os.Stat(output); !os.IsNotExist(err) { 174 c.UI.Output(fmt.Sprintf("Cleanup %s", output)) 175 os.RemoveAll(output) 176 } 177 return ExitCodeFailed 178 case <-doneCh: 179 c.UI.Info(fmt.Sprintf("====> Successfully generated %s", executable.Name)) 180 return ExitCodeOK 181 } 182 } 183 } 184 185 // Synopsis is a one-line, short synopsis of the command. 186 func (c *ApplyCommand) Synopsis() string { 187 return "Apply design template file for generating cli project" 188 } 189 190 // Help is a long-form help text that includes the command-line 191 // usage, a brief few sentences explaining the function of the command, 192 // and the complete list of flags the command accepts. 193 func (c *ApplyCommand) Help() string { 194 helpText := ` 195 Usage: gcli apply [option] FILE 196 197 Apply design template file for generating cli project. You can generate 198 design template file via 'gcli design' command. If framework name is not 199 specified gcli use codegangsta/cli. You can set framework name via '-F' 200 option. To check cli framework you can use, run 'gcli list'. 201 202 Options: 203 204 -framework=name, -F Cli framework name. By default, gcli use "codegangsta/cli" 205 To check cli framework you can use, run 'gcli list'. 206 If you set invalid framework, it will be failed. 207 208 -skip-test, -T Skip generating *_test.go file. By default, gcli generates 209 test file If you specify this flag, gcli will not generate 210 test files. 211 ` 212 return strings.TrimSpace(helpText) 213 }