github.com/leowmjw/otto@v0.2.1-0.20160126165905-6400716cf085/builtin/app/scriptpack/app.go (about)

     1  package scriptpackapp
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"github.com/hashicorp/otto/app"
    12  	"github.com/hashicorp/otto/appfile"
    13  	"github.com/hashicorp/otto/builtin/app/go"
    14  	"github.com/hashicorp/otto/helper/bindata"
    15  	"github.com/hashicorp/otto/helper/compile"
    16  	execHelper "github.com/hashicorp/otto/helper/exec"
    17  	"github.com/hashicorp/otto/helper/router"
    18  	"github.com/hashicorp/otto/helper/vagrant"
    19  )
    20  
    21  //go:generate go-bindata -pkg=scriptpackapp -nomemcopy -nometadata ./data/...
    22  
    23  // App is an implementation of app.App
    24  type App struct{}
    25  
    26  func (a *App) Meta() (*app.Meta, error) {
    27  	return Meta, nil
    28  }
    29  
    30  func (a *App) Implicit(ctx *app.Context) (*appfile.File, error) {
    31  	return nil, nil
    32  }
    33  
    34  func (a *App) Compile(ctx *app.Context) (*app.CompileResult, error) {
    35  	// Get the import path for this
    36  	path, err := goapp.DetectImportPath(ctx)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	if path == "" {
    41  		return nil, fmt.Errorf(
    42  			"Your ScriptPack development folder must be within your GOPATH like\n" +
    43  				"a standard Go project. This is required for the dev environment\n" +
    44  				"to function properly. Please put this folder in a proper GOPATH\n" +
    45  				"location.")
    46  	}
    47  
    48  	var opts compile.AppOptions
    49  	opts = compile.AppOptions{
    50  		Ctx: ctx,
    51  		Bindata: &bindata.Data{
    52  			Asset:    Asset,
    53  			AssetDir: AssetDir,
    54  			Context: map[string]interface{}{
    55  				"working_gopath": path,
    56  			},
    57  		},
    58  	}
    59  
    60  	return compile.App(&opts)
    61  }
    62  
    63  func (a *App) Build(ctx *app.Context) error {
    64  	return fmt.Errorf(strings.TrimSpace(buildErr))
    65  }
    66  
    67  func (a *App) Deploy(ctx *app.Context) error {
    68  	return fmt.Errorf(strings.TrimSpace(buildErr))
    69  }
    70  
    71  func (a *App) Dev(ctx *app.Context) error {
    72  	r := vagrant.Dev(&vagrant.DevOptions{
    73  		Instructions: strings.TrimSpace(devInstructions),
    74  	})
    75  
    76  	// Add our customer actions
    77  	r.Actions["scriptpack-rebuild"] = &router.SimpleAction{
    78  		ExecuteFunc:  a.actionRebuild,
    79  		SynopsisText: actionRebuildSyn,
    80  		HelpText:     strings.TrimSpace(actionRebuildHelp),
    81  	}
    82  
    83  	r.Actions["scriptpack-test"] = &router.SimpleAction{
    84  		ExecuteFunc:  a.actionTest,
    85  		SynopsisText: actionTestSyn,
    86  		HelpText:     strings.TrimSpace(actionTestHelp),
    87  	}
    88  
    89  	return r.Route(ctx)
    90  }
    91  
    92  func (a *App) DevDep(dst, src *app.Context) (*app.DevDep, error) {
    93  	return nil, fmt.Errorf("A ScriptPack can't be a dependency")
    94  }
    95  
    96  func (a *App) actionRebuild(rctx router.Context) error {
    97  	ctx := rctx.(*app.Context)
    98  	cwd := filepath.Dir(ctx.Appfile.Path)
    99  
   100  	// Get the path to the rebuild binary
   101  	path := filepath.Join(ctx.Dir, "rebuild", "rebuild.go")
   102  
   103  	// Run it to regenerate the contents
   104  	ctx.Ui.Header("Rebuilding ScriptPack data...")
   105  	cmd := exec.Command("go", "generate")
   106  	cmd.Dir = cwd
   107  	if err := execHelper.Run(ctx.Ui, cmd); err != nil {
   108  		return err
   109  	}
   110  
   111  	cmd = exec.Command("go", "run", path)
   112  	cmd.Dir = cwd
   113  	if err := execHelper.Run(ctx.Ui, cmd); err != nil {
   114  		return err
   115  	}
   116  
   117  	// Success
   118  	ctx.Ui.Message("ScriptPack data rebuilt!")
   119  	return nil
   120  }
   121  
   122  func (a *App) actionTest(rctx router.Context) error {
   123  	ctx := rctx.(*app.Context)
   124  
   125  	// Rebuild
   126  	if err := a.actionRebuild(rctx); err != nil {
   127  		return err
   128  	}
   129  
   130  	// Verify we have the files
   131  	dir := filepath.Join(filepath.Dir(ctx.Appfile.Path), "_scriptpack_staging")
   132  	if _, err := os.Stat(dir); err != nil {
   133  		return fmt.Errorf(
   134  			"The directory with the built ScriptPack files doesn't exist!\n" +
   135  				"Please build this with `otto dev scriptpack-rebuild` prior to\n" +
   136  				"running tests.")
   137  	}
   138  
   139  	// Get the env vars
   140  	f, err := os.Open(filepath.Join(dir, "env"))
   141  	if err != nil {
   142  		return err
   143  	}
   144  	var env map[string]string
   145  	err = json.NewDecoder(f).Decode(&env)
   146  	f.Close()
   147  	if err != nil {
   148  		return err
   149  	}
   150  
   151  	// Build the command we execute to run the tests
   152  	cmd := []string{
   153  		"docker",
   154  		"run",
   155  		"-v /vagrant:/devroot",
   156  	}
   157  	for k, v := range env {
   158  		cmd = append(cmd, fmt.Sprintf("-e %s=%s", k, v))
   159  	}
   160  	cmd = append(cmd, "hashicorp/otto-scriptpack-test-ubuntu:14.04")
   161  	cmd = append(cmd, "bats")
   162  
   163  	// Determine the test to execute
   164  	testPath := "test"
   165  	if args := rctx.RouteArgs(); len(args) > 0 {
   166  		testPath = args[0]
   167  	}
   168  	if !filepath.IsAbs(testPath) {
   169  		testPath = fmt.Sprintf("/devroot/" + testPath)
   170  	}
   171  
   172  	cmd = append(cmd, testPath)
   173  
   174  	// Run the command
   175  	ctx.Ui.Header(fmt.Sprintf("Executing: %s", strings.Join(cmd, " ")))
   176  	v := &vagrant.DevOptions{}
   177  	v.Vagrant(ctx).Execute("ssh", "-c", strings.Join(cmd, " "))
   178  
   179  	return nil
   180  }
   181  
   182  const devInstructions = `
   183  A development environment has been created for testing this ScriptPack.
   184  
   185  Anytime you change the contents of the ScriptPack, you must run
   186  "otto dev scriptpack-rebuild". This will update the contents in the
   187  dev environment. This is a very fast operation.
   188  
   189  To run tests, you can use "otto dev scriptpack-test" with the path
   190  to the directory or BATS test file to run. This will be automatically
   191  configured and run within the dev environment.
   192  `
   193  
   194  const buildErr = `
   195  Build and deploy aren't supported for ScriptPacks since it doesn't
   196  make a lot of sense.
   197  `
   198  
   199  const (
   200  	actionRebuildSyn = "Rebuild ScriptPack output for dev and test"
   201  	actionTestSyn    = "Run ScriptPack tests"
   202  )
   203  
   204  const actionRebuildHelp = `
   205  Usage: otto dev scriptpack-rebuild
   206  
   207    Rebuilds the ScriptPack files and dependencies into a single directory
   208    for dev and test within the development environment.
   209  
   210    This command must be run before running tests after making any changes
   211    to the ScriptPack.
   212  
   213  `
   214  
   215  const actionTestHelp = `
   216  Usage: otto dev scriptpack-test [path]
   217  
   218    Tests the ScriptPack with a BATS test file with the given path.
   219    If no path is specified, the entire "test" directory is used.
   220  
   221    If a path is given, it must be relative to the working directory.
   222    If an absolute path is given, it must be convertable to a relative
   223    subpath of this directory.
   224  
   225  `