package lib import ( "encoding/json" "errors" "fmt" "github.com/fatih/color" "go.digitalcircle.com.br/open/shelly" "gopkg.in/yaml.v3" "log" "os" "os/exec" "os/user" "runtime" "sort" "strings" "time" ) var model *MkModel func FileExists(filename string) bool { info, err := os.Stat(filename) if os.IsNotExist(err) { return false } return !info.IsDir() } func ResolveConfig() (err error) { if CLI.File != "" { if CLI.Init && FileExists(CLI.File) { println(fmt.Sprintf("Using file: %s", CLI.File)) return } else { err = errors.New(fmt.Sprintf("Config File %s not found", CLI.File)) } } deffiles := []string{".mk.yaml", ".mk", "mk", "mk.yaml"} for _, v := range deffiles { if CLI.Init || FileExists(v) { println(fmt.Sprintf("Using file: %s", v)) CLI.File = v return } } err = errors.New(fmt.Sprintf("No Config File found")) return } func ResolveTask(n string) *MkTask { osname := runtime.GOOS arch := runtime.GOARCH vars["BASETASK"] = n t, ok := model.Tasks[n+"_"+osname+"_"+arch] if ok { vars["TASK"] = n + "_" + osname + "_" + arch return t } t, ok = model.Tasks[n+"_"+osname] if ok { vars["TASK"] = n + "_" + osname return t } vars["TASK"] = n return model.Tasks[n] } func Log(i int, task string, stream string, b string) { timeLb := time.Now().Format("15:04:05") tab := "" for x := 0; x < i; x++ { tab = tab + "\t" } if stream == "O" { println(color.GreenString(fmt.Sprintf("%s %s [%s - %s]: %s", timeLb, tab, task, stream, b))) } else if stream == "E" { println(color.RedString(fmt.Sprintf("%s %s [%s - %s]: %s", timeLb, tab, task, stream, b))) } else { println(fmt.Sprintf("%s %s [%s - %s]: %s", timeLb, tab, task, stream, b)) } } func RunTask(name string, t *MkTask, variant string, l int) error { cmd := t.Cmd if len(t.Variants) > 0 && variant == "" { for _, v := range t.Variants { err := RunTask(name, t, v, l+1) if err != nil { return err } } return nil } if t.Model != "" { cmd = model.Tasks[t.Model].Cmd for k, v := range t.Vars { cmd = strings.Replace(cmd, "${"+k+"}", v, -1) } } for k, v := range t.Vars { if strings.HasPrefix(k, variant+".") { nk := strings.Replace(k, variant+".", "", 1) cmd = strings.Replace(cmd, "${"+nk+"}", v, -1) } } if variant != "" { Log(l, name, "**", fmt.Sprintf("Starting %s::%s", name, variant)) } else { Log(l, name, "**", fmt.Sprintf("Starting %s", name)) } if len(t.Pre) > 0 { Log(l, name, "**", fmt.Sprintf("Will run Pre tasks: [%s]", strings.Join(t.Pre, ","))) for _, v := range t.Pre { caller, repeat := model.Stack[v] if repeat { Log(l, name, "**", fmt.Sprintf("Task %s already called by %s - skipping.", v, caller)) continue } else { model.Stack[v] = name } pr, ok := model.Tasks[v] if ok { err := RunTask(v, pr, "", l+1) if err != nil { return err } } else { return errors.New(fmt.Sprintf("Task %s, prereq of: %s not found in model", v, name)) } } } _, err := shelly.Exec(cmd, &shelly.Opts{ Debug: model.Debug, Trace: model.Trace, SetupProc: func(cmd *exec.Cmd) { for k, v := range t.Env { env[k] = v } for k, v := range t.Vars { vars[k] = v } }, }) return err } func DumpEnv() { if CLI.Env { println("Vars:") varnames := make([]string, 0) for k := range vars { varnames = append(varnames, k) } sort.Strings(varnames) for _, k := range varnames { v := vars[k] println(fmt.Sprintf("%s => %s", k, v)) } println("========") println("Env:") envnames := make([]string, 0) for k := range env { envnames = append(envnames, k) } sort.Strings(envnames) for _, k := range envnames { v := env[k] println(fmt.Sprintf("%s => %s", k, v)) } os.Exit(0) } } func jsonConvert(src interface{}, dst interface{}) error { bs, err := json.Marshal(src) if err != nil { return err } err = json.Unmarshal(bs, dst) return err } func Prepare() error { InitCli() err := ResolveConfig() if err != nil { return err } if CLI.Init { err = InitFile() if err != nil { return err } os.Exit(0) } if CLI.DumpValidator { err = DumpValidator() if err != nil { log.Printf("Error DumpValidator: %s", err.Error()) } os.Exit(0) } bs, err := os.ReadFile(CLI.File) if err != nil { panic(err) } model = &MkModel{} err = yaml.Unmarshal(bs, model) if err != nil { return err } if model.Default == "" { model.Default = "main" } model.Tasks = make(map[string]*MkTask) for k, v := range model.RawTasks { nv := &MkTask{} nv.Name = k nv.Vars = make(map[string]string) switch v1 := v.(type) { case string: nv.Cmd = v1 case map[string]interface{}: err = jsonConvert(v1, nv) if err != nil { return err } } for k1, v1 := range model.Vars { nv.Vars[k1] = v1 } model.Tasks[k] = nv } if CLI.List { println("Tasks:") tasknames := make([]string, 0) for k := range model.Tasks { tasknames = append(tasknames, k) } sort.Strings(tasknames) for _, k := range tasknames { v := model.Tasks[k] def := "" if v.Name == model.Default { def = "DEF>" } else { def = "" } println(fmt.Sprintf("%s %s [%s]: %s", def, k, strings.Join(v.Pre, ","), v.Help)) } os.Exit(0) } if len(CLI.Tasks) < 1 || CLI.Tasks[0] == "" || CLI.Tasks[0] == "." { CLI.Tasks = []string{model.Default} } return err } var env map[string]string var vars map[string]string func Run() error { env = make(map[string]string) vars = make(map[string]string) envstrs := os.Environ() for _, k := range envstrs { parts := strings.Split(k, "=") ek := strings.TrimSpace(parts[0]) ev := os.Getenv(ek) env[ek] = ev } err := Prepare() if err != nil { log.Printf("Could not execute: %s", err.Error()) os.Exit(0) } for k, v := range model.Env { env[k] = v } for k, v := range model.Vars { vars[k] = v } now := time.Now() vars["DT_YYMMDDHHmmss"] = now.Format("060102150405") vars["DT_YYMMDD"] = now.Format("060102") vars["DS_TS"] = fmt.Sprintf("%d", now.Unix()) u, err := user.Current() if err != nil { panic(err) } vars["USERNAME"] = u.Username vars["HOMEDIR"] = u.HomeDir if err != nil { return err } DumpEnv() model.Stack = make(map[string]string) for _, v := range CLI.Tasks { taskparts := strings.Split(v, ",") tasko := ResolveTask(taskparts[0]) if tasko != nil { if len(taskparts) > 1 { for i := 1; i < len(taskparts); i++ { err = RunTask(taskparts[0], tasko, taskparts[1], 0) } } else { err = RunTask(taskparts[0], tasko, "", 0) } } else { return errors.New(fmt.Sprintf("No task named %s found", v)) } } return err }