package common import ( "fmt" "os/exec" "regexp" "strings" ) type Command struct { Id string `json:"id"` Cmd string `json:"cmd"` Description string `json:"description"` IgnoreErros bool `json:"ignore.errors"` } type BuildSet struct { Modes map[string]*BuildMode `json:"modes"` Cmd string `json:"cmd"` Workdir string `json:"workdir"` Steps []*Command `json:"steps"` Before []*Command `json:"beforeCompile"` After []*Command `json:"afterCompile"` Options *BuildOptions `json:"options"` } func (b *BuildSet) Commands() []*Command { var ( mode *BuildMode ok, ignore bool steps = []*Command{} modeKey = b.Options.Mode ) if modeKey == "" { modeKey = "default" } if mode, ok = b.Modes[modeKey]; !ok { panic(fmt.Sprintf("Build mode '%s' not defined", modeKey)) } if (len(mode.Ignore) == 0) && len(mode.ExecuteOnly) == 0 { return b.Steps } ignore = len(mode.Ignore) > 0 if ignore && len(mode.ExecuteOnly) > 0 { panic("Ignore and ExecuteOnly are mutually exclusive") } ids := map[string]bool{} for _, v := range append(mode.Ignore, mode.ExecuteOnly...) { ids[v] = true } if ignore { for _, step := range b.Steps { if _, ok := ids[step.Id]; !ok { steps = append(steps, step) } } } else { for _, step := range b.Steps { if _, ok := ids[step.Id]; ok { steps = append(steps, step) } } } return steps } type BuildMode struct { Ignore []string `json:"ignore"` ExecuteOnly []string `json:"executeOnly"` } var ( expressionRegexp = regexp.MustCompile(`\{([\w\.]+)\}`) ) func NewBuildOptions() *BuildOptions { return &BuildOptions{ Mode: "default", ProjectDotNotation: map[string]interface{}{}, } } func runBuildCommads(project *Project, client *Client, buildOptions *BuildOptions) (err error) { var ( instance *exec.Cmd translateOptions *BuildSet step int cmd string out []byte // variables = map[string]interface{}{} buildfile = fmt.Sprintf("build/%s", client.Id) mode = project.Mode buildSet = &BuildSet{ Steps: []*Command{}, Modes: map[string]*BuildMode{ "default": {}, }, } ) if buildOptions == nil { buildOptions = NewBuildOptions() } buildOptions.Parse() if err = JsonParseMode(buildfile, mode, &buildSet); err != nil { err = nil fmt.Printf("[warning] running '%s' without build file", client.Id) buildOptions.Mode = "default" } buildSet.Options = buildOptions if bo, found := project.TranslatorBuildOptionsMap[client.Id]; found { translateOptions, err = bo(project, buildOptions) if translateOptions.Before != nil { buildSet.Steps = append(translateOptions.Before, buildSet.Steps...) } if translateOptions.After != nil { buildSet.Steps = append(buildSet.Steps, translateOptions.After...) } } if buildSet.After != nil { buildSet.Steps = append(buildSet.Steps, buildSet.After...) } fmt.Println("Running build commands... on mode ", mode) fmt.Println("RUN translate ", client.Id) cmds := buildSet.Commands() total := len(cmds) for k, command := range cmds { if cmd, err = parseCommand(command, buildOptions.ProjectDotNotation); err != nil { return } step = k + 1 // goterm.MoveCursor(1, 1) if buildOptions.IgnoreStep(step) { // goterm.Printf("\tStep (%d / %d) - [ Skiped ] %s\n", step, total, cmd) fmt.Printf("\tStep (%d / %d) - [ Skiped ] %s\n", step, total, cmd) continue } // goterm.Printf("\tStep (%d / %d) - %s\n", step, total, cmd) fmt.Printf("\tStep (%d / %d) - %s\n", step, total, cmd) instance = exec.Command("sh", "-c", cmd) instance.Dir = buildSet.Workdir // goterm.Flush() // Call it every time at the end of rendering if out, err = instance.CombinedOutput(); err != nil { fmt.Println(string(out)) return } } return } func parseCommand(c *Command, variables map[string]interface{}) (string, error) { var ( expression, replace, dotPathName string cmd = c.Cmd matches = expressionRegexp.FindAllStringSubmatch(cmd, -1) ) for _, match := range matches { dotPathName = match[1] if value, exists := variables[dotPathName]; !exists || value == nil { return "", fmt.Errorf("Value for expression '%s' not defined", dotPathName) } expression = match[0] replace = fmt.Sprintf("%v", variables[dotPathName]) cmd = strings.ReplaceAll(cmd, expression, replace) } return cmd, nil }