1st ver
This commit is contained in:
+19
@@ -0,0 +1,19 @@
|
||||
package lib
|
||||
|
||||
import "github.com/alecthomas/kong"
|
||||
|
||||
var CLI = struct {
|
||||
File string `help:"File to be used - Defaults are: .mk.yaml, .mk, mk, mk.yaml" short:"f"`
|
||||
Init bool `help:"Creates a new empty file (default is .mk.yaml in case no filename is provided)" short:"i"`
|
||||
Tasks []string `arg:"" help:"Tasks to be run - Default is main." default:"."`
|
||||
Ver bool `help:"Prints version and exit" short:"v"`
|
||||
List bool `help:"Check file and print tasks" short:"l"`
|
||||
Dbg bool `help:"Debugs execution" short:"d"`
|
||||
DumpValidator bool `help:"Dumps Validator JSON File" default:"false"`
|
||||
Env bool `help:"Dumps env and vars" default:"false" short:"e"`
|
||||
}{}
|
||||
|
||||
func InitCli() {
|
||||
kong.Parse(&CLI)
|
||||
Ver(CLI.Ver)
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
CMD_ZIP = "zip"
|
||||
CMD_PACK = "pack"
|
||||
)
|
||||
|
||||
func RecursiveZip(i int, pathToZip, destinationPath string) error {
|
||||
Log(i, "mk:zip", "O", "Starting Zip")
|
||||
destinationFile, err := os.Create(destinationPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
myZip := zip.NewWriter(destinationFile)
|
||||
err = filepath.Walk(pathToZip, func(filePath string, info os.FileInfo, err error) error {
|
||||
Log(i, "mk:zip", "O", fmt.Sprintf("Adding file: %s", filePath))
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
relPath := strings.TrimPrefix(filePath, filepath.Dir(pathToZip))
|
||||
zipFile, err := myZip.Create(relPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fsFile, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(zipFile, fsFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = myZip.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Log(i, "mk:zip", "O", fmt.Sprintf("Zip finished: %s", destinationPath))
|
||||
return nil
|
||||
}
|
||||
func RunMkCmd(i int, line string) error {
|
||||
parts := strings.Split(line, " ")
|
||||
switch parts[0] {
|
||||
case CMD_ZIP:
|
||||
if len(parts) < 3 {
|
||||
return errors.New(fmt.Sprintf("Not enought params: must be zip <dir> <zipfile>. Got: %s", strings.Join(parts, " ")))
|
||||
}
|
||||
return RecursiveZip(i, parts[1], parts[2])
|
||||
case CMD_PACK:
|
||||
var ptz string
|
||||
if len(parts) > 1 {
|
||||
ptz = parts[1]
|
||||
} else {
|
||||
for _, f := range []string{"stage", "deploy", "pack", "release"} {
|
||||
st, err := os.Stat(f)
|
||||
if err == nil && st.IsDir() {
|
||||
ptz = f
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dname := path.Base(dir)
|
||||
ts := time.Now().Format("060102150405")
|
||||
fname := dname + "-" + ts + ".zip"
|
||||
return RecursiveZip(i, ptz, fname)
|
||||
default:
|
||||
Log(i, parts[0], "E", fmt.Sprintf("mk:cmd %s is not known", parts[0]))
|
||||
return errors.New(fmt.Sprintf("mk:cmd %s is not known", parts[0]))
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"os"
|
||||
)
|
||||
|
||||
//go:embed res/.mk.yaml
|
||||
var sample []byte
|
||||
|
||||
//go:embed res/mk.json
|
||||
var validator []byte
|
||||
|
||||
func InitFile() error {
|
||||
return os.WriteFile(CLI.File, sample, 0600)
|
||||
}
|
||||
|
||||
func DumpValidator() error {
|
||||
return os.WriteFile(CLI.File, validator, 0600)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
# SAMPLE mk file
|
||||
default: main
|
||||
env:
|
||||
|
||||
tasks:
|
||||
deploy:
|
||||
pre: [ build,test ]
|
||||
help: Deploys the project
|
||||
cmd: echo deploying
|
||||
test:
|
||||
pre: [ c ]
|
||||
help: Tests project
|
||||
cmd: echo testing
|
||||
build:
|
||||
help: Build binaries
|
||||
cmd: echo building
|
||||
main:
|
||||
help: Main task
|
||||
pre: [ build ]
|
||||
cmd: |-
|
||||
echo main
|
||||
echo done
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"$id": "https://www.digitalticircle.com.br/_schemas/mk.json",
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Digital Circle - Make Instructions",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"$d": {
|
||||
"task": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pre": {
|
||||
"description": "Predecessor tasks",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"cmd": {
|
||||
"description": "Command this task should run",
|
||||
"type": "string"
|
||||
},
|
||||
"help": {
|
||||
"description": "Help for this task",
|
||||
"type": "string"
|
||||
},
|
||||
"onerror": {
|
||||
"description": "What to do in case of error"
|
||||
},
|
||||
"env": {
|
||||
"type": "object",
|
||||
"description": "Global Env Var",
|
||||
"patternProperties": {
|
||||
".{1.}": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"vars": {
|
||||
"type": "object",
|
||||
"description": "Global Text Var",
|
||||
"patternProperties": {
|
||||
".{1.}": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": "false"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"default": {
|
||||
"type": "string",
|
||||
"default": "main",
|
||||
"description": "Default task to run"
|
||||
},
|
||||
"tasks": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
".{1,}": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$d/task"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"description": "Tasks configured for execution"
|
||||
},
|
||||
"env": {
|
||||
"type": "object",
|
||||
"description": "Global Env Var",
|
||||
"patternProperties": {
|
||||
".{1.}": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"vars": {
|
||||
"type": "object",
|
||||
"description": "Global Text Var",
|
||||
"patternProperties": {
|
||||
".{1.}": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+396
@@ -0,0 +1,396 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/fatih/color"
|
||||
"gopkg.in/yaml.v3"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"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, l int) error {
|
||||
|
||||
for k, v := range t.Env {
|
||||
env[k] = v
|
||||
}
|
||||
|
||||
for k, v := range t.Vars {
|
||||
vars[k] = v
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
var c *exec.Cmd
|
||||
if runtime.GOOS == "windows" {
|
||||
c = exec.Command("cmd.exe")
|
||||
} else {
|
||||
c = exec.Command("sh")
|
||||
}
|
||||
for k, v := range env {
|
||||
c.Env = append(c.Env, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
|
||||
pi, _ := c.StdinPipe()
|
||||
|
||||
po, err := c.StdoutPipe()
|
||||
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
return err
|
||||
}
|
||||
scannero := bufio.NewScanner(po)
|
||||
|
||||
pe, err := c.StderrPipe()
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
return err
|
||||
}
|
||||
scannere := bufio.NewScanner(pe)
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
wg.Add(2)
|
||||
|
||||
ch := make(chan string)
|
||||
|
||||
go func() {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
defer wg.Done()
|
||||
for scannero.Scan() {
|
||||
line := scannero.Text()
|
||||
if strings.Contains(line, "__CMD_ENDED__:") {
|
||||
ret := strings.Split(line, "__CMD_ENDED__:")[1]
|
||||
if ret != "%errorlevel%" {
|
||||
ch <- ret
|
||||
}
|
||||
} else {
|
||||
Log(l+1, name, "O", line)
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
go func() {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
defer wg.Done()
|
||||
for scannere.Scan() {
|
||||
Log(l+1, name, "E", scannere.Text())
|
||||
}
|
||||
}()
|
||||
|
||||
err = c.Start()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lines := bufio.NewScanner(strings.NewReader(t.Cmd))
|
||||
|
||||
//if runtime.GOOS == "windows" {
|
||||
// pi.Write([]byte("@echo off\n"))
|
||||
//}
|
||||
|
||||
for lines.Scan() {
|
||||
|
||||
txt := lines.Text()
|
||||
|
||||
for k, v := range vars {
|
||||
txt = strings.Replace(txt, fmt.Sprintf("${%s}", k), v, -1)
|
||||
}
|
||||
if strings.HasPrefix(txt, "@") {
|
||||
txt = strings.TrimLeft(txt, "@")
|
||||
} else {
|
||||
Log(l+1, name, "I", txt)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(txt, "mk:") {
|
||||
txt = strings.Replace(txt, "mk:", "", 1)
|
||||
txt = strings.TrimSpace(txt)
|
||||
err = RunMkCmd(l+1, txt)
|
||||
if err != nil {
|
||||
log.Printf("Error running: %s", txt)
|
||||
}
|
||||
} else {
|
||||
|
||||
_, err = pi.Write([]byte(txt + "\n"))
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
_, err = pi.Write([]byte("echo __CMD_ENDED__:%errorlevel%" + "\n"))
|
||||
} else {
|
||||
_, err = pi.Write([]byte("echo __CMD_ENDED__:$?" + "\n"))
|
||||
}
|
||||
|
||||
ret := <-ch
|
||||
if ret != "0" && t.Onerror == "skip" {
|
||||
Log(l+1, name, "E", "Error Code: "+ret+" will continue, but watch out")
|
||||
|
||||
} else if ret != "0" && t.Onerror != "skip" {
|
||||
Log(l+1, name, "E", "Error Code is: "+ret+" ABORTING")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
_, err = pi.Write([]byte("exit\n"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
err = c.Wait()
|
||||
Log(l, name, "**", "End")
|
||||
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 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"
|
||||
}
|
||||
|
||||
for k, v := range model.Tasks {
|
||||
v.Name = k
|
||||
}
|
||||
|
||||
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 {
|
||||
tasko := ResolveTask(v)
|
||||
if tasko != nil {
|
||||
err = RunTask(v, tasko, 0)
|
||||
} else {
|
||||
return errors.New(fmt.Sprintf("No task named %s found", v))
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package lib
|
||||
|
||||
type MkTask struct {
|
||||
Name string
|
||||
Help string
|
||||
Cmd string `yaml:"cmd"`
|
||||
Pre []string `yaml:"pre"`
|
||||
Onerror string `yaml:"onerror"`
|
||||
Env map[string]string `yaml:"env"`
|
||||
Vars map[string]string `yaml:"vars"`
|
||||
}
|
||||
type MkModel struct {
|
||||
Env map[string]string `yaml:"env"`
|
||||
Vars map[string]string `yaml:"vars"`
|
||||
Tasks map[string]*MkTask `yaml:"tasks"`
|
||||
Default string `yaml:"default"`
|
||||
Stack map[string]string `yaml:"-"`
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package lib
|
||||
|
||||
import "os"
|
||||
|
||||
import _ "embed"
|
||||
|
||||
//go:embed ver.txt
|
||||
var ver string
|
||||
|
||||
func Ver(end bool) {
|
||||
println("DC MK Tool - ver: " + ver)
|
||||
if end {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Sat Sep 18 20:38:27 -03 2021
|
||||
Reference in New Issue
Block a user