// TestAppfile returns a compiled appfile for the given path. This uses // defaults for detectors and such so it is up to you to use a fairly // complete Appfile. func TestAppfile(t TestT, path string) *appfile.Compiled { def, err := appfile.Default(filepath.Dir(path), &detect.Config{ Detectors: []*detect.Detector{ &detect.Detector{ Type: "test", File: []string{"Appfile"}, }, }, }) if err != nil { t.Fatal("err: ", err) } // Default type should be "test" def.Infrastructure[0].Type = "test" def.Infrastructure[0].Flavor = "test" def.Infrastructure[0].Foundations = nil // Parse the raw file f, err := appfile.ParseFile(path) if err != nil { t.Fatal("err: ", err) } // Merge if err := def.Merge(f); err != nil { t.Fatal("err: ", err) } f = def // Create a temporary directory for the compilation data. We don't // delete this now in case we're using any of that data, but the // temp dir should get cleaned up by the system at some point. td, err := ioutil.TempDir("", "otto") if err != nil { t.Fatal("err: ", err) } // Compile it! compiler, err := appfile.NewCompiler(&appfile.CompileOpts{ Dir: td, }) if err != nil { t.Fatal("err: ", err) } result, err := compiler.Compile(f) if err != nil { t.Fatal("err: ", err) } return result }
func testLoader(t *testing.T) (*Loader, *app.Mock) { td, err := ioutil.TempDir("", "otto") if err != nil { t.Fatalf("err: %s", err) } compiler, err := appfile.NewCompiler(&appfile.CompileOpts{ Dir: filepath.Join(td, "compile"), }) if err != nil { t.Fatalf("err: %s", err) } // Create a single mock instance that is returned so we can verify // calls and modify return values. appMock := new(app.Mock) return &Loader{ Detector: &detect.Config{ Detectors: []*detect.Detector{ &detect.Detector{ Type: "test", File: []string{"test-file"}, }, }, }, Compiler: compiler, Apps: map[app.Tuple]app.Factory{ app.Tuple{"test", "*", "*"}: func() (app.App, error) { return appMock, nil }, }, }, appMock }
func (c *CompileCommand) Run(args []string) int { var flagAppfile string fs := c.FlagSet("compile", FlagSetNone) fs.Usage = func() { c.Ui.Error(c.Help()) } fs.StringVar(&flagAppfile, "appfile", "", "") if err := fs.Parse(args); err != nil { return 1 } // Load all the plugins, we use all the plugins for compilation only // so we have full access to detectors and app types. pluginMgr, err := c.PluginManager() if err != nil { c.Ui.Error(fmt.Sprintf( "Error initializing plugin manager: %s", err)) return 1 } if err := pluginMgr.LoadAll(); err != nil { c.Ui.Error(fmt.Sprintf( "Error loading plugins: %s", err)) return 1 } // Load the detectors from the plugins detectors := make([]*detect.Detector, 0, 20) detectors = append(detectors, c.Detectors...) for _, p := range pluginMgr.Plugins() { detectors = append(detectors, p.AppMeta.Detectors...) } // Parse the detectors dataDir, err := c.DataDir() if err != nil { c.Ui.Error(err.Error()) return 1 } detectorDir := filepath.Join(dataDir, DefaultLocalDataDetectorDir) log.Printf("[DEBUG] loading detectors from: %s", detectorDir) detectConfig, err := detect.ParseDir(detectorDir) if err != nil { c.Ui.Error(err.Error()) return 1 } if detectConfig == nil { detectConfig = &detect.Config{} } err = detectConfig.Merge(&detect.Config{Detectors: detectors}) if err != nil { c.Ui.Error(err.Error()) return 1 } // Load a UI ui := c.OttoUi() ui.Header("Loading Appfile...") app, appPath, err := loadAppfile(flagAppfile) if err != nil { c.Ui.Error(err.Error()) return 1 } // Tell the user what is happening if they have no Appfile if app == nil { ui.Header("No Appfile found! Detecting project information...") ui.Message(fmt.Sprintf( "No Appfile was found. If there is no Appfile, Otto will do its best\n" + "to detect the type of application this is and set reasonable defaults.\n" + "This is a good way to get started with Otto, but over time we recommend\n" + "writing a real Appfile since this will allow more complex customizations,\n" + "the ability to reference dependencies, versioning, and more.")) } // Build the appfile compiler var loader appfileLoad.Loader compiler, err := appfile.NewCompiler(&appfile.CompileOpts{ Dir: filepath.Join( appPath, DefaultOutputDir, DefaultOutputDirCompiledAppfile), Loader: loader.Load, Callback: c.compileCallback(ui), }) if err != nil { c.Ui.Error(fmt.Sprintf( "Error initializing Appfile compiler: %s", err)) return 1 } // Create the Appfile loader if err := pluginMgr.ConfigureCore(c.CoreConfig); err != nil { panic(err) } loader.Detector = detectConfig loader.Compiler = compiler loader.Apps = c.CoreConfig.Apps // Load the complete Appfile app, err = loader.Load(app, appPath) if err != nil { c.Ui.Error(err.Error()) return 1 } // If there was no loaded Appfile and we don't have an application // type then we weren't able to detect the type. Error. if app == nil || app.Application.Type == "" { c.Ui.Error(strings.TrimSpace(errCantDetectType)) return 1 } // Compile the Appfile ui.Header("Fetching all Appfile dependencies...") capp, err := compiler.Compile(app) if err != nil { c.Ui.Error(fmt.Sprintf( "Error compiling Appfile: %s", err)) return 1 } // Get a core core, err := c.Core(capp) if err != nil { c.Ui.Error(fmt.Sprintf( "Error loading core: %s", err)) return 1 } // Get the active infrastructure just for UI reasons infra := app.ActiveInfrastructure() // Before the compilation, output to the user what is going on ui.Header("Compiling...") ui.Message(fmt.Sprintf( "Application: %s (%s)", app.Application.Name, app.Application.Type)) ui.Message(fmt.Sprintf("Project: %s", app.Project.Name)) ui.Message(fmt.Sprintf( "Infrastructure: %s (%s)", infra.Type, infra.Flavor)) ui.Message("") // Compile! if err := core.Compile(); err != nil { c.Ui.Error(fmt.Sprintf( "Error compiling: %s", err)) return 1 } // Store the used plugins so later calls don't have to load everything usedPath, err := c.AppfilePluginsPath(capp) if err != nil { c.Ui.Error(fmt.Sprintf( "Error compiling: %s", err)) return 1 } if err := pluginMgr.StoreUsed(usedPath); err != nil { c.Ui.Error(fmt.Sprintf( "Error compiling plugin data: %s", err)) return 1 } // Success! ui.Header("[green]Compilation success!") ui.Message(fmt.Sprintf( "[green]This means that Otto is now ready to start a development environment,\n" + "deploy this application, build the supporting infrastructure, and\n" + "more. See the help for more information.\n\n" + "Supporting files to enable Otto to manage your application from\n" + "development to deployment have been placed in the output directory.\n" + "These files can be manually inspected to determine what Otto will do.")) return 0 }