package common import ( "encoding/json" "fmt" "os/exec" "regexp" "strings" "github.com/jeremywohl/flatten" ) type Command struct { Id string `json:"id"` Cmd string `json:"cmd"` 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"` 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 ( buildSet = &BuildSet{ Steps: []*Command{}, Modes: map[string]*BuildMode{ "default": &BuildMode{}, }, } ) func NewBuildOptions() *BuildOptions { return &BuildOptions{ Mode: "default", } } func RunBuildCommads(project *Project, buildOptions *BuildOptions) (err error) { var ( instance *exec.Cmd variables = map[string]interface{}{} variablesJson, cmd string step int out []byte buildfile = "build/build" mode = project.Mode ) if out, err = json.Marshal(project); err != nil { return } if variablesJson, err = flatten.FlattenString(string(out), "project.", flatten.DotStyle); err != nil { return } if err = json.Unmarshal([]byte(variablesJson), &variables); err != nil { return } if buildOptions == nil { buildOptions = NewBuildOptions() } buildOptions.Parse() if err = JsonParseMode(buildfile, mode, &buildSet); err != nil { return } buildSet.Options = buildOptions fmt.Println("Running build commands... on mode ", mode) cmds := buildSet.Commands() total := len(cmds) for k, command := range cmds { if cmd, err = parseCommand(command, variables); err != nil { return } step = k + 1 if buildOptions.IgnoreStep(step) { fmt.Printf("Step (%d / %d) - [ Skiped ] %s\n", step, total, cmd) continue } fmt.Printf("Step (%d / %d) - %s\n", step, total, cmd) instance = exec.Command("sh", "-c", cmd) instance.Dir = buildSet.Workdir if out, err = instance.CombinedOutput(); err != nil { fmt.Println(string(out)) return } } return } var ( expressionRegexp = regexp.MustCompile(`\{([\w\.]+)\}`) ) 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 }