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  }