1st ver
parent
85e464c559
commit
481ebc30e0
|
@ -0,0 +1,4 @@
|
|||
/tmp/
|
||||
/deploy/
|
||||
.idea
|
||||
.vscode
|
20
LICENSE
20
LICENSE
|
@ -1,9 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) <year> <copyright holders>
|
||||
Copyright (c) 2021 digitalcircle-com-br
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
110
README.md
110
README.md
|
@ -1,3 +1,111 @@
|
|||
# mk
|
||||
|
||||
mk - Make like tool for building, testing, etc.
|
||||
_Multiplatform make-like tool_
|
||||
|
||||
Ok, ok - you say - why another make tool clone? Oh lord, there we go again....
|
||||
|
||||
Indeed its a valid question... reasons are:
|
||||
|
||||
1 - Cuz I could not find a make tool that fit my needs as simple as this one
|
||||
|
||||
2 - Cuz windows is always a pain when it comes to compiling software
|
||||
|
||||
3 - Why not?
|
||||
|
||||
4 - Did I mention I hate verbose stuff?
|
||||
|
||||
So these are the drivers for writting mk
|
||||
|
||||
## Install
|
||||
|
||||
```shell
|
||||
go install github.com/digitalcircle-com-br/mk@latest
|
||||
```
|
||||
|
||||
## How it works:
|
||||
|
||||
mk will look for mk files (which may be named: mk, mk.yaml .mk or .mk.yaml)
|
||||
|
||||
you may create a new mk file by using ```mk -i``` bingo, thats all...
|
||||
|
||||
The file will look like this one:
|
||||
|
||||
```yaml
|
||||
# SAMPLE mk file - Feel free to add your own header
|
||||
default: a #This is the default task, in case you call command w/o parameters
|
||||
env: # in case you want to add var to env, you may add it here
|
||||
a: 1
|
||||
b: 2
|
||||
|
||||
tasks: #now lets define the tasks
|
||||
a_darwin_arm64: #this is the task name
|
||||
cmd: |- # and this is the command - which may be multiline, no issues.
|
||||
echo \"${TASK} / ${BASETASK}\"
|
||||
ls -larth
|
||||
pwd
|
||||
deploy:
|
||||
pre: [ build,test ] # pre is an array of predecessors
|
||||
help: Deploys the project # help prints the help message
|
||||
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%
|
||||
```
|
||||
|
||||
And thats it.
|
||||
|
||||
## Some gotchas you should notice
|
||||
|
||||
### Name resolution
|
||||
|
||||
Tasks are resolved considering this rule: task_os_arch: In case you have a task with the os name and arch name, it will
|
||||
have higher precedence at resolving it. Suppose you add 2 tasks in your mk file: a_windows_amd64 and a_darwin_arm64. In
|
||||
case youre on a Mac with Apple silicon, and call make a, a_darwin_arm64 will be called. In case you have a task a_darwin
|
||||
and a_windows, and call mk a from a Mac with Intel processor, it will call a_darwing. Lastly, in case you also define a
|
||||
task a, it will be called in case none of these more restrictive rules find math.
|
||||
|
||||
> By adopting this approach same mk file will allow multiple platform compilation.
|
||||
|
||||
### Variables
|
||||
|
||||
You may place ${VAR} anywhere in your command, and it will be replaced by mk. It provides you some var, and also env
|
||||
vars
|
||||
|
||||
## Help reference
|
||||
|
||||
```shell
|
||||
Usage: mk [<tasks> ...]
|
||||
|
||||
Arguments:
|
||||
[<tasks> ...] Tasks to be run - Default is main.
|
||||
|
||||
Flags:
|
||||
-h, --help Show context-sensitive help.
|
||||
-f, --file=STRING File to be used - Defaults are: .mk.yaml, .mk, mk, mk.yaml
|
||||
-i, --init Creates a new empty file (default is .mk.yaml in case no filename is provided)
|
||||
-v, --ver Prints version and exit
|
||||
-l, --list Check file and print tasks
|
||||
-d, --dbg Debugs execution
|
||||
--dump-validator Dumps Validator JSON File
|
||||
-e, --env Dumps env and vars
|
||||
|
||||
```
|
||||
|
||||
# TODO
|
||||
|
||||
- Output is pretty ugly, but the best I could think of so far...
|
||||
- Integrate zip and git into "internal tasks"
|
||||
- allow file to have tasks as strings in case no other props are required
|
||||
- allow file include
|
||||
- allow it to run as server
|
||||
- Accepting recommendations on how to improve it
|
|
@ -0,0 +1,13 @@
|
|||
module go.digitalcircle.com.br/open/mk
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/alecthomas/kong v0.2.17
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
)
|
|
@ -0,0 +1,29 @@
|
|||
github.com/alecthomas/kong v0.2.17 h1:URDISCI96MIgcIlQyoCAlhOmrSw6pZScBNkctg8r0W0=
|
||||
github.com/alecthomas/kong v0.2.17/go.mod h1:ka3VZ8GZNPXv9Ov+j4YNLkI8mTuhXyr/0ktSlqIydQQ=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -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]))
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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:"-"`
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue