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