mk/lib/run.go

346 lines
6.6 KiB
Go

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
}