Пример #1
0
/// Look for a file from which to parse configuration (nut.yml in current
/// directory). Parse the file, and returns an updated context (root directory)
/// TODO: look for nut.yml file in parent folders
func FindProject(context Utils.Context) (Project, Utils.Context, error) {
	// var parentProject Project
	var project Project
	var err error
	var newContext Utils.Context
	var store Persist.Store

	foundDirectory := context.GetUserDirectory()
	// fullpath := filepath.Join(foundDirectory, NutFileName)
	fullpath := filepath.Join(foundDirectory, NutOverrideFileName)
	previousFoundDirectory := ""
	var exists bool

	searchHigher := true
	found := false
	for searchHigher == true {
		if exists, err = Utils.FileExists(fullpath); exists && err == nil {
			found = true
			searchHigher = false
		} else {
			fullpath = filepath.Join(foundDirectory, NutFileName)

			if exists, err = Utils.FileExists(fullpath); exists && err == nil {
				found = true
				searchHigher = false
			} else {
				previousFoundDirectory = foundDirectory
				foundDirectory = filepath.Dir(foundDirectory)
				// fullpath = filepath.Join(foundDirectory, NutFileName)
				fullpath = filepath.Join(foundDirectory, NutOverrideFileName)

				if foundDirectory == previousFoundDirectory {
					searchHigher = false
				}
			}
		}
	}

	if found {
		if project, err = LoadProjectFromFile(fullpath); err == nil {
			log.Debug("Parsed from file: ", fullpath)
			if newContext, err = Utils.NewContext(foundDirectory, context.GetUserDirectory()); err == nil {
				log.Debug("Context updated: ", newContext)
				if store, err = Persist.InitStore(newContext.GetRootDirectory()); err == nil {
					log.Debug("Store initialized: ", store)
					if err = ResolveDependencies(project, store, fullpath); err == nil {
						log.Debug("Resolved dependencies from file: ", fullpath)
						return project, newContext, err
					}
				}
			}
		}
	} else {
		err = errors.New("Could not find '" + NutFileName + "', neither in current directory nor in its parents.")
	}
	return nil, nil, err
}
Пример #2
0
func main() {
	log.SetLevel(log.ErrorLevel)
	// log.SetLevel(log.DebugLevel)

	// get the folder where nut has been called
	pwd, err := os.Getwd()
	if err != nil {
		log.Fatal("find path error: ", err)
	}

	context, err := Utils.NewContext(pwd, pwd)
	if err != nil {
		log.Fatal("Error with context: ", err)
	}

	log.Debug("main first context: ", context)
	// try to parse the folder's configuration to add macros
	var macros []cli.Command
	project, projectContext, err := Config.FindProject(context)
	log.Debug("main second context: ", projectContext)
	projectMacros := map[string]Config.Macro{}

	if err == nil {
		// Macros are stored in a random order.
		// But we want to display them in the same order everytime.
		// So sort the names of the macros.
		projectMacros = Config.GetMacros(project)
		macroNamesOrdered := make([]string, 0, len(projectMacros))
		for key, _ := range projectMacros {
			macroNamesOrdered = append(macroNamesOrdered, key)
		}
		sort.Strings(macroNamesOrdered)

		macros = make([]cli.Command, len(projectMacros))
		log.Debug(len(macroNamesOrdered), " macros")
		for index, name := range macroNamesOrdered {
			macro := projectMacros[name]

			log.Debug("macro ", name, ": ", macro)
			// If the nut file containes a macro which has not any field defined,
			// then the macro will be nil.
			// So check value of macro:
			if macro == reflect.Zero(reflect.TypeOf(macro)).Interface() { // just check whether it macro is nil
				// it seems uselessly complicated, but:
				// if macro == nil { // doesn't work the trivial way one could expect
				// if macro.(*MacroBase) == nil { // panic: interface conversion: main.Macro is *main.MacroV3, not *main.MacroBase
				// Checking for nil doesn't seems like a good solution. TODO: ? require at least a "usage" field for each macro in the nut file?
				log.Warn("Undefined properties of macro " + name + ".")
			} else {
				nameClosure := name
				usage := "macro: "
				if macroUsage := Config.GetUsage(macro); macroUsage == "" {
					usage += "undefined usage. define one with 'usage' property for this macro."
				} else {
					usage += macroUsage
				}

				macros[index] = cli.Command{
					Name:        nameClosure,
					Usage:       usage,
					Aliases:     Config.GetAliases(macro),
					UsageText:   Config.GetUsageText(macro),
					Description: Config.GetDescription(macro),
					Action: func(c *cli.Context) {
						execMacro(macro, projectContext, false)
					},
				}
			}
		}
	} else {
		log.Error("Could not parse configuration: " + err.Error())
		macros = []cli.Command{}
	}

	initFlag := false
	logsFlag := false
	cleanFlag := false
	execFlag := ""
	inheriteConfigMacroFlag := ""
	gitHubFlag := ""
	macroFlag := ""
	useDockerCLIFlag := DOCKERCLI_FLAG_DEFAULT_VALUE

	app := cli.NewApp()
	app.Name = "nut"
	app.Version = "0.1.3 dev"
	app.Usage = "the development environment, containerized"
	// app.EnableBashCompletion = true
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:        "clean",
			Usage:       "clean all data stored in .nut",
			Destination: &cleanFlag,
		},
		cli.BoolFlag{
			Name:        "init",
			Usage:       "initialize a nut project",
			Destination: &initFlag,
		},
		cli.StringFlag{
			Name:        "github",
			Usage:       "Use with --init: provide a GitHub repository to initialize Nut.",
			Destination: &gitHubFlag,
		},
		cli.BoolFlag{
			Name:        "logs",
			Usage:       "Use with --exec: display log messages. Useful for contributors and to report an issue",
			Destination: &logsFlag,
		},
		cli.StringFlag{
			Name:        "exec",
			Usage:       "execute a command in a container.",
			Destination: &execFlag,
		},
		cli.StringFlag{
			Name:        "macro",
			Usage:       "Name of the macro to execute. Use with --logs.",
			Destination: &macroFlag,
		},
		cli.StringFlag{
			Name:        "extend-macro",
			Usage:       "Use with --exec: name of the macro from which to inherite the configuration",
			Destination: &inheriteConfigMacroFlag,
		},
	}
	if DOCKERCLI_FLAG_DEFAULT_VALUE == false {
		app.Flags = append(app.Flags, cli.BoolFlag{
			Name:        "dockercli",
			Usage:       "Use Docker CLI, instead of using Docker API to reach the host directly.",
			Destination: &useDockerCLIFlag,
		})
	} else {
		app.Flags = append(app.Flags, cli.BoolFlag{
			Name:        "dockerapi",
			Usage:       "Use Docker API to reach the host directly, instead of using Docker CLI.",
			Destination: &useDockerCLIFlag,
		})
	}
	defaultAction := app.Action
	app.Action = func(c *cli.Context) {
		if logsFlag {
			log.SetLevel(log.DebugLevel)
		}

		if cleanFlag {
			if project != nil {
				persist.CleanStoreFromProject(projectContext.GetRootDirectory())
			}
		} else if initFlag {
			log.Debug("main context for init: ", context)
			initSubcommand(c, context, gitHubFlag)
			// return
		} else if execFlag != "" {
			if project != nil {
				if inheriteConfigMacroFlag == "" {
					execInContainer([]string{execFlag}, project, projectContext, useDockerCLIFlag != DOCKERCLI_FLAG_DEFAULT_VALUE)
				} else if macro, ok := projectMacros[inheriteConfigMacroFlag]; ok && project != nil {
					execInContainer([]string{execFlag}, macro, projectContext, useDockerCLIFlag != DOCKERCLI_FLAG_DEFAULT_VALUE)
				} else {
					log.Error("Undefined macro " + macroFlag)
					defaultAction(c)
				}
			} else {
				log.Error("Could not find nut configuration.")
				defaultAction(c)
			}
			// return
		} else if macroFlag != "" {
			if macro, ok := projectMacros[macroFlag]; ok && project != nil {
				execMacro(macro, projectContext, useDockerCLIFlag != DOCKERCLI_FLAG_DEFAULT_VALUE)
			} else {
				log.Error("Undefined macro " + macroFlag)
				defaultAction(c)
			}
		} else {
			defaultAction(c)
		}
	}

	app.Commands = macros

	app.Run(os.Args)
}