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 `