From 4113c07643737fddfc0b45ab4d8db4efe086dc40 Mon Sep 17 00:00:00 2001 From: stephen cheng Date: Thu, 30 Jul 2020 23:42:30 +1000 Subject: [PATCH 1/3] add finally/rescue support for func step level --- biz/impl/cmdfunc.go | 5 ++++ biz/impl/runtime.go | 6 +++- biz/impl/step.go | 67 +++++++++++++++++++++++++++++++++++++++++++++ biz/impl/tasker.go | 12 ++++++-- up.yml | 58 +++++++++++++++++++++++---------------- utils/debug.go | 7 +++-- utils/default.go | 1 + 7 files changed, 127 insertions(+), 29 deletions(-) diff --git a/biz/impl/cmdfunc.go b/biz/impl/cmdfunc.go index a8cc380..2021702 100644 --- a/biz/impl/cmdfunc.go +++ b/biz/impl/cmdfunc.go @@ -190,6 +190,11 @@ func (f *CmdFuncAction) Exec() { u.Ptrace("Trace:", cmdRendered) }) + case "panic": + s := "manual trigger a panic cmd" + u.LogWarn("manual panic", s) + panic(s) + case "printObj": u.Dvvvv(cmdItem.Cmd) cmdItem.runCmd("string", func() { diff --git a/biz/impl/runtime.go b/biz/impl/runtime.go index 3bcf62e..effa711 100644 --- a/biz/impl/runtime.go +++ b/biz/impl/runtime.go @@ -24,6 +24,8 @@ const ( UP_RUNTIME_TASK_LAYER_NUMBER = "up_runtime_task_layer_number" UP_RUNTIME_TASKER_LAYER_NUMBER = "up_runtime_tasker_layer_number" UP_RUNTIME_TASK_PIPE_IN_CONTENT = "up_runtime_task_pipe_in_content" + //accessible only during the deferred finally processing + UP_RUNTIME_SHELL_EXEC_RESULT = "up_runtime_shell_exec_result" ) type TaskRuntimeContext struct { @@ -95,11 +97,13 @@ func ConfigRuntime() *utils.UpConfig { } func debugVars() { + u.Ppmsg("UpRunTimeVars", UpRunTimeVars) + if taskRuntime := TaskRuntime(); taskRuntime != nil { u.Ppmsg("ExecbaseVars", taskRuntime.ExecbaseVars) } + if stepRuntime := StepRuntime(); stepRuntime != nil { u.Ppmsg("ExecContextVars", stepRuntime.ContextVars) } - } diff --git a/biz/impl/step.go b/biz/impl/step.go index 85cdbdd..209918f 100644 --- a/biz/impl/step.go +++ b/biz/impl/step.go @@ -41,6 +41,8 @@ type Step struct { RefDir string VarsFile string Timeout int //milli seconds, only for shell func + Finally interface{} + Rescue bool } type Steps []Step @@ -159,6 +161,35 @@ func validation(vars *core.Cache) { func (step *Step) Exec(fromBlock bool) { var action biz.Do + defer func() { + u.PlnBlue("Step Finally:") + u.Ppmsg(StepRuntime().Result) + u.Ppmsg(step.Vars) + paniced := false + //u.Pdebug(step) + if step.Vars == nil { + step.Vars = *core.NewCache() + } + + step.Vars.Put(UP_RUNTIME_SHELL_EXEC_RESULT, StepRuntime().Result) + //debugVars() + if r := recover(); r != nil { + u.Pln("Recovered", r) + paniced = true + } + + if step.Finally != nil && step.Finally != "" { + stepFinally(step.Finally, &step.Vars) + } + + if paniced && step.Rescue == false { + u.InvalidAndExit("No rescued", "please assess the panic problem and cause, fix it before re-run the task") + } else if paniced { + u.LogWarn("Rescued, but not advised!", "setting rescue to yes/true to continue is not recommended\nit is advised to locate root cause of the problem, fix it and re-run the task again\nit is the best practice to test the execution in your ci pipeline to eliminate problems rather than dynamically fix using rescue") + } + step.Vars.Delete(UP_RUNTIME_SHELL_EXEC_RESULT) + }() + var bizErr *ee.Error = ee.New() var stepExecVars *core.Cache @@ -398,6 +429,40 @@ func doElse(elseCalls interface{}, execVars *core.Cache) { } +func stepFinally(finally interface{}, execVars *core.Cache) { + var taskname string + var tasknames []string + var flow Steps + switch finally.(type) { + case string: + taskname = finally.(string) + tasknames = append(tasknames, taskname) + + case []interface{}: + elseStr := u.Spf("%s", finally) + if strings.Index(elseStr, "map") != -1 && strings.Index(elseStr, "func:") != -1 { + err := ms.Decode(finally, &flow) + u.LogErrorAndExit("load steps in finally", err, "steps/flow has configuration problem, please fix it") + BlockFlowRun(&flow, execVars) + } else { + err := ms.Decode(finally, &tasknames) + u.LogErrorAndExit("load task names in finally", err, "please ref to a task name only") + } + + default: + u.LogWarn("finally ..", "Not implemented or void for no action!") + } + + if len(tasknames) > 0 { + for _, tmptaskname := range tasknames { + taskname := Render(tmptaskname, execVars) + u.PpmsgvvvvvhintHigh(u.Spf("finally caller vars to task (%s):", taskname), execVars) + ExecTask(taskname, execVars) + } + } + +} + func (steps *Steps) InspectSteps(tree treeprint.Tree, level *int) bool { for _, step := range *steps { desc := strings.Split(step.Desc, "\n")[0] @@ -486,7 +551,9 @@ func (steps *Steps) Exec(fromBlock bool) { if step.Do == nil && step.Dox != nil { u.LogWarn("*", "Step is deactivated!") } else { + u.Pln("pre step .........", StepRuntime().Stepname) step.Exec(fromBlock) + u.Pln("post step .........", StepRuntime().Stepname) } result := StepRuntime().Result diff --git a/biz/impl/tasker.go b/biz/impl/tasker.go index e2b749f..0e030ae 100644 --- a/biz/impl/tasker.go +++ b/biz/impl/tasker.go @@ -768,11 +768,17 @@ func (t *Tasker) ExecTask(taskname string, callerVars *core.Cache, isExternalCal rtContext.ExecbaseVars = t.RuntimeVarsAndDvarsMerged rtContext.TasknameLayered = taskname } else { - rtContext.ExecbaseVars = callerVars + rtContext.ExecbaseVars = func() *core.Cache { + if *callerVars == nil { + return core.NewCache() + } else { + return callerVars + } + }() + rtContext.TasknameLayered = u.Spf("%s/%s", TaskRuntime().TasknameLayered, taskname) } } - u.Pdebugvvvvvvv(rtContext.ExecbaseVars) func() { @@ -787,7 +793,9 @@ func (t *Tasker) ExecTask(taskname string, callerVars *core.Cache, isExternalCal } }() + u.Pln("pre task .........", TaskRuntime().Taskname) steps.Exec(false) + u.Pln("post task .........", TaskRuntime().Taskname) returnVars := TaskRuntime().ReturnVars diff --git a/up.yml b/up.yml index 81b670a..4f635c3 100644 --- a/up.yml +++ b/up.yml @@ -22,17 +22,7 @@ vars: * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - version_gofile_origin: | - package main - - var version_info = ` - version_info: - Type: bleeding-edge-rolling - Tag: master - Vesion: latest - Source: https://github.com/upcmd/up/commits/master - Note: daily build regular rolling release - ` + latest_tagged_version: 0.9.1 version_gofile_template: | package main @@ -50,6 +40,11 @@ vars: Changes: | {{.releaseNote | indent 4}} + rolling_version_notes: | + bleeding-edge rolling release + based on {{.latest_tagged_version}} with updates + full regression tested and stable + rolling_version_template: | version_info: @@ -59,8 +54,13 @@ vars: SHA: {{.releaseSHA}} Source: https://github.com/upcmd/up/tree/{{.releaseSHA}} ReleaseDate: {{now|date "20060102"}} - Note: regular rolling release + BaseVersion: {{.latest_tagged_version}} + Note: {{.rolling_version_notes}} + latest_version_notes: | + latest bleeding-edge release + based on {{.latest_tagged_version}} with updates + full regression tested and stable latest_version_template: | @@ -71,7 +71,8 @@ vars: SHA: {{.releaseSHA}} Source: https://github.com/upcmd/up/tree/{{.releaseSHA}} ReleaseDate: {{now|date "20060102"}} - Note: regular rolling release - latest + BaseVersion: {{.latest_tagged_version}} + Note: {{.latest_version_notes}} tasks: - @@ -229,13 +230,17 @@ tasks: - func: shell desc: create the latest master release + dvars: + - name: quick_notes + value: '{{.rolling_version_notes}}' + expand: 2 do: | curl -s -d ' { "tag_name": "latest", "target_commitish": "master", "name": "latest", - "body": "latest bleeding-edge release from master: full regression tested and stable", + "body": "{{.quick_notes}}", "draft": false, "prerelease": false } @@ -357,13 +362,17 @@ tasks: - func: shell desc: create the latest rolling release + dvars: + - name: quick_notes + value: '{{.rolling_version_notes}}' + expand: 2 do: | curl -s -d ' { "tag_name": "rolling-{{now|date "20060102"}}", "target_commitish": "master", "name": "rolling-{{now|date "20060102"}}", - "body": "bleeding-edge rolling release: full regression tested and stable", + "body": "{{.quick_notes}}", "draft": false, "prerelease": false } @@ -605,15 +614,6 @@ tasks: - name: print cmd: 'distro list: {{.distList}}' - - name: writeFile - desc: restore original versioning file - cmd: - content: "{{.version_gofile_origin}}" - filename: version.go - dir: ./app/up - - - namex: pause - - func: shell desc: create the tagged release do: @@ -1069,3 +1069,13 @@ tasks: if: '{{ not (regexMatch "MPL/2.0" .content) }}' loop: caselist + + + - + name: how_many_stars + desc: get the number of star of the project + task: + - func: shell + do: | + curl --silent 'https://api.github.com/repos/upcmd/up' -H 'Accept: application/vnd.github.preview' | grep stargazers_count + reg: result diff --git a/utils/debug.go b/utils/debug.go index 0db4942..21dc15f 100644 --- a/utils/debug.go +++ b/utils/debug.go @@ -83,6 +83,10 @@ func PlnInfo(info string) { msg_color_printf("%s\n", info) } +func PlnBlue(info string) { + blue_color_printf("%s\n", info) +} + func PlnInfoHighlight(info string) { hilight_color_printf("%s\n", info) } @@ -246,6 +250,7 @@ func Pfvvvvv(format string, a ...interface{}) { func PStackTrace() { if permitted("vvvvv") { + Pln("-----trace for reference-----") debug.PrintStack() } } @@ -338,7 +343,6 @@ func LogErrorAndExit(mark string, err interface{}, hint string) { func LogError(mark string, err interface{}) { if err != nil { color.Red(" %s -> %s", mark, err) - Pln("-----trace for reference-----") PStackTrace() } } @@ -356,7 +360,6 @@ func LogErrorAndContinue(mark string, err interface{}, hint string) { `) } - Pln("-----trace for reference-----") PStackTrace() } } diff --git a/utils/default.go b/utils/default.go index 430fee9..995e223 100644 --- a/utils/default.go +++ b/utils/default.go @@ -29,6 +29,7 @@ var ( vvvv_color_printf = color.Magenta verror_color_printf = color.Red msg_color_printf = color.Yellow + blue_color_printf = color.Blue hilight_color_printf = color.HiGreen himsg_color_printf = color.HiWhite msg_color_sprintf = color.YellowString From 5ae28733f845cf6d37aa0d26bd4e7561667bd435 Mon Sep 17 00:00:00 2001 From: stephen cheng Date: Thu, 30 Jul 2020 23:43:22 +1000 Subject: [PATCH 2/3] add example/test cases for step level finally support --- tests/functests/c0170.yml | 67 +++++++++++++++++++++++++++++++++++++++ tests/functests/c0171.yml | 45 ++++++++++++++++++++++++++ tests/functests/c0172.yml | 56 ++++++++++++++++++++++++++++++++ tests/functests/c0173.yml | 58 +++++++++++++++++++++++++++++++++ tests/functests/c0174.yml | 66 ++++++++++++++++++++++++++++++++++++++ tests/functests/f0172.yml | 55 ++++++++++++++++++++++++++++++++ 6 files changed, 347 insertions(+) create mode 100644 tests/functests/c0170.yml create mode 100644 tests/functests/c0171.yml create mode 100644 tests/functests/c0172.yml create mode 100644 tests/functests/c0173.yml create mode 100644 tests/functests/c0174.yml create mode 100644 tests/functests/f0172.yml diff --git a/tests/functests/c0170.yml b/tests/functests/c0170.yml new file mode 100644 index 0000000..24e995f --- /dev/null +++ b/tests/functests/c0170.yml @@ -0,0 +1,67 @@ +notes: + - pre/post task without using finally element + +tasks: + - + name: task + task: + - func: call + do: + - pre_task + - my_task + - post_task + + - + name: pre_task + task: + - + func: cmd + do: + - name: print + cmd: pre-task + + - + name: post_task + task: + - + func: cmd + do: + - name: print + cmd: post-task + + - + name: final_task + task: + - + func: cmd + do: + - name: print + cmd: final-task even there is failure/exception + + - + name: my_task + task: + - + func: cmd + do: + - name: print + cmd: my task step 1 + - name: print + cmd: my task step 2 + + - + func: shell + desc: | + task fails + do: + - echo "step a - hello" + - echo "step b - hello" |grep "world" + - echo "step c - after the exception" + + - + func: cmd + do: + - name: print + cmd: my task step 3 + - name: print + cmd: my task step 4 diff --git a/tests/functests/c0171.yml b/tests/functests/c0171.yml new file mode 100644 index 0000000..98f6992 --- /dev/null +++ b/tests/functests/c0171.yml @@ -0,0 +1,45 @@ +doc_meta: | + folder: shell-func + title: finally to ensure clean up/rescue + head: | + ignoreError will make the workflow to continue + + sections: + - title: Demo + log: yes + + related: + refs: + - title: shell func + link: ../../quick-start/c0002/ + - title: error handling + link: ../../test-debug/error_handling/ + +notes: + - pre/post task without using finally element + +tasks: + + - + name: task + task: + + - + func: shell + desc: | + task fails + do: + - echo "open a file ....." + - echo "reading the file" |grep "cause an exception" + finally: close_file + + - + name: close_file + task: + - + func: shell + name: close_file + desc: | + ensure the opened file is closed + do: + - echo "close the file ....." diff --git a/tests/functests/c0172.yml b/tests/functests/c0172.yml new file mode 100644 index 0000000..068c54d --- /dev/null +++ b/tests/functests/c0172.yml @@ -0,0 +1,56 @@ +doc_meta: | +# folder: shell-func +# title: finally to ensure clean up/rescue +# head: | +# ignoreError will make the workflow to continue +# +# sections: +# - title: Demo +# log: yes +# +# related: +# refs: +# - title: shell func +# link: ../../quick-start/c0002/ +# - title: error handling +# link: ../../test-debug/error_handling/ + +notes: + - add feature of finally run code + - add feature to rescue from panic + +tasks: + + - + name: task + task: + + - + func: cmd + desc: step 1 + do: + - name: print + cmd: step 1 + - name: panic + - name: print + cmd: extra step ......... it will never reach here + rescue: true + finally: close_file + + - + func: cmd + desc: step 2 + do: + - name: print + cmd: step 2 + + - + name: close_file + task: + - + func: shell + name: close_file + desc: | + ensure the opened file is closed + do: + - echo "close the file ....." diff --git a/tests/functests/c0173.yml b/tests/functests/c0173.yml new file mode 100644 index 0000000..64b141c --- /dev/null +++ b/tests/functests/c0173.yml @@ -0,0 +1,58 @@ +#doc_meta: | +# folder: shell-func +# title: finally to ensure clean up/rescue +# head: | +# ignoreError will make the workflow to continue +# +# sections: +# - title: Demo +# log: yes +# +# related: +# refs: +# - title: shell func +# link: ../../quick-start/c0002/ +# - title: error handling +# link: ../../test-debug/error_handling/ + +notes: + - test finally block implementation + +tasks: + + - + name: task + task: + + - + func: cmd + desc: step 1 + do: + - name: print + cmd: step 1 + - name: panic + - name: print + cmd: extra step ......... it will never reach here + rescue: true + finally: + - + func: shell + name: close_file + desc: | + ensure the opened file is closed + do: + - echo "close the file ....." + + - + func: cmd + desc: extra steps + do: + - name: print + cmd: extra step + + - + func: cmd + desc: step 2 + do: + - name: print + cmd: step 2 diff --git a/tests/functests/c0174.yml b/tests/functests/c0174.yml new file mode 100644 index 0000000..e439916 --- /dev/null +++ b/tests/functests/c0174.yml @@ -0,0 +1,66 @@ +#doc_meta: | +# folder: shell-func +# title: finally to ensure clean up/rescue +# head: | +# ignoreError will make the workflow to continue +# +# sections: +# - title: Demo +# log: yes +# +# related: +# refs: +# - title: shell func +# link: ../../quick-start/c0002/ +# - title: error handling +# link: ../../test-debug/error_handling/ + +notes: + - all finally block/task to access the failed exec contextual info and conditionally handle the error + +tasks: + + - + name: task + task: + + - + func: shell + do: + - echo "opening file" + - echo "hello"|grep "world" + flags: + - ignoreError + finally: + - + func: shell + desc: | + ensure the opened file is closed + do: + - echo "close the file ....." + - | + echo """ + exec command: {{.up_runtime_shell_exec_result.Cmd}} + error code: {{.up_runtime_shell_exec_result.Code}} + error message: {{.up_runtime_shell_exec_result.ErrMsg}} + error output: {{.up_runtime_shell_exec_result.Output}} + """ + + - + func: cmd + desc: | + see if the exec context result: up_runtime_shell_exec_result is still availabe + conditional do something about the error etc + do: + - name: print + cmd: | + error message: {{.up_runtime_shell_exec_result.ErrMsg}} + if: '{{ne .up_runtime_shell_exec_result.Code 0}}' + + - + func: shell + desc: | + this step will not be reached if the ignoreError flag is not set + try it yourself to remove the ignoreError flag and see difference + do: + - echo "extra step ..." diff --git a/tests/functests/f0172.yml b/tests/functests/f0172.yml new file mode 100644 index 0000000..6308b4e --- /dev/null +++ b/tests/functests/f0172.yml @@ -0,0 +1,55 @@ +doc_meta: | +# folder: shell-func +# title: finally to ensure clean up/rescue +# head: | +# ignoreError will make the workflow to continue +# +# sections: +# - title: Demo +# log: yes +# +# related: +# refs: +# - title: shell func +# link: ../../quick-start/c0002/ +# - title: error handling +# link: ../../test-debug/error_handling/ + +notes: + - pre/post task without using finally element + +tasks: + + - + name: task + task: + + - + func: cmd + desc: step 1 + do: + - name: print + cmd: step 1 + - name: panic + - name: print + cmd: extra step ......... it will never reach here + rescue: false + finally: close_file + + - + func: cmd + desc: step 2 + do: + - name: print + cmd: step 2 + + - + name: close_file + task: + - + func: shell + name: close_file + desc: | + ensure the opened file is closed + do: + - echo "close the file ....." From b53e54e5caf9ccb7d963342ed4fd3d3add1dfff9 Mon Sep 17 00:00:00 2001 From: stephen cheng Date: Sun, 2 Aug 2020 01:09:17 +1000 Subject: [PATCH 3/3] add task level finally code block handling --- app/up/main.go | 2 +- biz/impl/blockfunc.go | 318 +---------------------- biz/impl/callfunc.go | 2 +- biz/impl/cmdfunc.go | 32 +-- biz/impl/dvar.go | 14 +- biz/impl/pipein.go | 2 +- biz/impl/scope.go | 6 +- biz/impl/step.go | 65 ++--- biz/impl/tasker.go | 90 ++++--- biz/impl/template.go | 4 +- model/core/ymlobj.go | 6 +- model/task.go | 14 +- tests/functests/c0175.yml | 57 ++++ tests/functests/{c0171.yml => f0171.yml} | 0 utils/debug.go | 16 +- utils/module.go | 16 +- utils/shell.go | 2 +- utils/upconfig.go | 10 +- 18 files changed, 213 insertions(+), 443 deletions(-) create mode 100644 tests/functests/c0175.yml rename tests/functests/{c0171.yml => f0171.yml} (100%) diff --git a/app/up/main.go b/app/up/main.go index 5696bd4..862b1b5 100644 --- a/app/up/main.go +++ b/app/up/main.go @@ -70,7 +70,7 @@ func main() { if *ngoTaskName != "" { u.Pln("-exec task:", *ngoTaskName) if *instanceName != "" && *execprofile != "" { - u.InvalidAndExit("parameter validation", "instanceid (-i) and execprofile (-p) can not coexist, please only use one of them") + u.InvalidAndPanic("parameter validation", "instanceid (-i) and execprofile (-p) can not coexist, please only use one of them") } t := impl.NewTasker(*instanceName, *execprofile, initConfig) impl.Pipein() diff --git a/biz/impl/blockfunc.go b/biz/impl/blockfunc.go index e0d7aca..e7f06fa 100644 --- a/biz/impl/blockfunc.go +++ b/biz/impl/blockfunc.go @@ -8,15 +8,9 @@ package impl import ( - "github.com/imdario/mergo" ms "github.com/mitchellh/mapstructure" - "github.com/mohae/deepcopy" - "github.com/upcmd/up/biz" "github.com/upcmd/up/model/core" u "github.com/upcmd/up/utils" - ee "github.com/upcmd/up/utils/error" - "reflect" - "strconv" ) type BlockFuncAction struct { @@ -40,7 +34,7 @@ func (f *BlockFuncAction) Adapt() { case []interface{}: //detailed steps err := ms.Decode(f.Do, &flow) - u.LogErrorAndExit("load steps", err, "steps has configuration problem, please fix it") + u.LogErrorAndPanic("load steps", err, "steps has configuration problem, please fix it") default: u.LogWarn("Block func", "Not implemented or void for no action!") @@ -64,313 +58,3 @@ func BlockFlowRun(flow *Steps, execVars *core.Cache) { flow.Exec(true) BlockStack().Pop() } - -func (flow *Steps) ExecFlow() { - - for idx, step := range *flow { - - taskLayerCnt := TaskerRuntime().Tasker.TaskStack.GetLen() - u.LogDesc("block step", idx+1, taskLayerCnt, step.Name, step.Desc) - u.Ppmsgvvvv(step) - - execStep := func() { - rtContext := StepRuntimeContext{ - Stepname: step.Name, - Timeout: step.Timeout, - } - StepStack().Push(&rtContext) - - //TODO: ?? - step.ExecTest() - - result := StepRuntime().Result - taskname := TaskerRuntime().Tasker.TaskStack.GetTop().(*TaskRuntimeContext).Taskname - - //TODO: add support for block - if u.Contains([]string{FUNC_SHELL, FUNC_CALL}, step.Func) { - if step.Reg == "auto" { - TaskRuntime().ExecbaseVars.Put(u.Spf("register_%s_%s", taskname, step.Name), result.Output) - } else if step.Reg != "" { - TaskRuntime().ExecbaseVars.Put(u.Spf("%s", step.Reg), result.Output) - } else { - if step.Func == FUNC_SHELL { - TaskRuntime().ExecbaseVars.Put("last_result", result) - } - } - } - - func() { - result := StepRuntime().Result - - if result != nil && result.Code == 0 { - u.LogOk(".") - } - - if !u.Contains(step.Flags, "ignoreError") { - if result != nil && result.Code != 0 { - u.InvalidAndExit("Failed And Not Ignored!", "You may want to continue and ignore the error") - } - } - - }() - - StepStack().Pop() - } - - if !TaskerRuntime().Tasker.TaskBreak { - execStep() - } else { - TaskerRuntime().Tasker.TaskBreak = false - break - } - - } - -} - -func (step *Step) ExecTest() { - var action biz.Do - - var bizErr *ee.Error = ee.New() - var stepExecVars *core.Cache - stepExecVars = step.getRuntimeExecVarsTest("get plain exec vars") - //u.Ptmpdebug("99", stepExecVars) - validation(stepExecVars) - - if step.Flags != nil && u.Contains(step.Flags, "pause") { - pause(stepExecVars) - } - - routeFuncType := func(loopItem *LoopItem) { - if loopItem != nil { - stepExecVars.Put("loopitem", loopItem.Item) - stepExecVars.Put("loopindex", loopItem.Index) - stepExecVars.Put("loopindex1", loopItem.Index1) - } - - switch step.Func { - case FUNC_SHELL: - funcAction := ShellFuncAction{ - Do: step.Do, - Vars: stepExecVars, - } - action = biz.Do(&funcAction) - - case FUNC_CALL: - funcAction := CallFuncAction{ - Do: step.Do, - Vars: stepExecVars, - } - action = biz.Do(&funcAction) - - case FUNC_BLOCK: - funcAction := BlockFuncAction{ - Do: step.Do, - Vars: stepExecVars, - } - action = biz.Do(&funcAction) - - case FUNC_CMD: - funcAction := CmdFuncAction{ - Do: step.Do, - Vars: stepExecVars, - } - action = biz.Do(&funcAction) - - case "": - u.InvalidAndExit("Step dispatch", "func name is empty and not defined") - bizErr.Mark = "func name not implemented" - - default: - u.InvalidAndExit("Step dispatch", u.Spf("func name(%s) is not recognised and implemented", step.Func)) - bizErr.Mark = "func name not implemented" - } - } - - dryRunOrContinue := func() { - //example to stop further steps - //f := u.MustConditionToContinueFunc(func() bool { - // return action != nil - //}) - // - //u.DryRunOrExit("Step Exec", f, "func name must be valid") - - alloweErrors := []string{ - "func name not implemented", - } - - DryRunAndSkip( - bizErr.Mark, - alloweErrors, - ContinueFunc( - func() { - if step.Loop != nil { - rawUtil := step.Until - func() { - //loop points to a var name which is a slice - if reflect.TypeOf(step.Loop).Kind() == reflect.String { - loopVarName := Render(step.Loop.(string), stepExecVars) - loopObj := stepExecVars.Get(loopVarName) - if loopObj == nil { - u.InvalidAndExit("Evaluating loop var and object", u.Spf("Please use a correct varname:(%s) containing a list of values", loopVarName)) - } - if reflect.TypeOf(loopObj).Kind() == reflect.Slice { - switch loopObj.(type) { - case []interface{}: - for idx, item := range loopObj.([]interface{}) { - routeFuncType(&LoopItem{idx, idx + 1, item}) - if rawUtil != "" { - untilEval := Render(rawUtil, stepExecVars) - toBreak, err := strconv.ParseBool(untilEval) - u.LogErrorAndExit("evaluate until condition", err, u.Spf("please fix until condition evaluation: [%s]", untilEval)) - if toBreak { - u.Pvvvv("loop util conditional break") - break - } else { - chainAction(&action) - } - } else { - chainAction(&action) - } - } - - case []string: - for idx, item := range loopObj.([]string) { - routeFuncType(&LoopItem{idx, idx + 1, item}) - if rawUtil != "" { - untilEval := Render(rawUtil, stepExecVars) - toBreak, err := strconv.ParseBool(untilEval) - u.LogErrorAndExit("evaluate until condition", err, u.Spf("please fix until condition evaluation: [%s]", untilEval)) - if toBreak { - u.Pvvvv("loop util conditional break") - break - } else { - chainAction(&action) - } - } else { - chainAction(&action) - } - } - - case []int64: - for idx, item := range loopObj.([]int64) { - routeFuncType(&LoopItem{idx, idx + 1, item}) - if rawUtil != "" { - untilEval := Render(rawUtil, stepExecVars) - toBreak, err := strconv.ParseBool(untilEval) - u.LogErrorAndExit("evaluate until condition", err, u.Spf("please fix until condition evaluation: [%s]", untilEval)) - if toBreak { - u.Pvvvv("loop util conditional break") - break - } else { - chainAction(&action) - } - } else { - chainAction(&action) - } - } - - default: - u.LogWarn("loop item evaluation", "Loop item type is not supported yet!") - } - } else { - u.InvalidAndExit("evaluate loop var", "loop var is not a array/list/slice") - } - } else if reflect.TypeOf(step.Loop).Kind() == reflect.Slice { - //loop itself is a slice - for idx, item := range step.Loop.([]interface{}) { - routeFuncType(&LoopItem{idx, idx + 1, item}) - if rawUtil != "" { - untilEval := Render(rawUtil, stepExecVars) - toBreak, err := strconv.ParseBool(untilEval) - u.LogErrorAndExit("evaluate until condition", err, u.Spf("please fix until condition evaluation: [%s]", untilEval)) - if toBreak { - u.Pvvvv("loop util conditional break") - break - } else { - chainAction(&action) - } - } else { - chainAction(&action) - } - } - } else { - u.InvalidAndExit("evaluate loop items", "please either use a list or a template evaluation which could result in a value of a list") - } - }() - - } else { - routeFuncType(nil) - chainAction(&action) - } - - }), - nil, - ) - } - - func() { - if step.If != "" { - IfEval := Render(step.If, stepExecVars) - if IfEval != "" { - goahead, err := strconv.ParseBool(IfEval) - u.LogErrorAndExit("evaluate condition", err, u.Spf("please fix if condition evaluation: [%s]", IfEval)) - if goahead { - dryRunOrContinue() - } else { - if step.Else != nil && step.Else != "" { - doElse(step.Else, stepExecVars) - } else { - u.Pvvv("condition failed, skip executing step", step.Name) - } - } - } else { - u.Pvvv("condition failed, skip executing step", step.Name) - } - } else { - dryRunOrContinue() - } - - }() -} - -func (step *Step) getRuntimeExecVarsTest(mark string) *core.Cache { - var execvars core.Cache - var resultVars *core.Cache - - execvars = deepcopy.Copy(*TaskRuntime().ExecbaseVars).(core.Cache) - - taskVars := TaskRuntime().TaskVars - mergo.Merge(&execvars, taskVars, mergo.WithOverride) - //u.Ptmpdebug("33", execvars) - //u.Ptmpdebug("44", step.Vars) - //if IsCalled() { - // u.Ptmpdebug("if", "if") - // if step.Vars != nil { - // mergo.Merge(&step.Vars, &execvars, mergo.WithOverride) - // resultVars = &step.Vars - // } else { - // resultVars = &execvars - // } - //} else { - // u.Ptmpdebug("else", "else") - // mergo.Merge(&execvars, &step.Vars, mergo.WithOverride) - // resultVars = &execvars - //} - blockvars := BlockStack().GetTop().(*BlockRuntimeContext).BlockBaseVars - mergo.Merge(&execvars, blockvars, mergo.WithOverride) - mergo.Merge(&execvars, &step.Vars, mergo.WithOverride) - resultVars = &execvars - - u.Pfvvvv("current exec runtime[%s] vars:", mark) - u.Ppmsgvvvv(resultVars) - //u.Ptmpdebug("55", resultVars) - - //so far the execvars includes: scope vars + scope dvars + global runtime vars + task vars - resultVars = VarsMergedWithDvars("local", resultVars, &step.Dvars, resultVars) - - //so far the resultVars includes: the local vars + dvars rendered using execvars - u.Ppmsgvvvhint("overall final exec vars:", resultVars) - //u.Ptmpdebug("99", resultVars) - return resultVars -} diff --git a/biz/impl/callfunc.go b/biz/impl/callfunc.go index 853430b..54c119a 100644 --- a/biz/impl/callfunc.go +++ b/biz/impl/callfunc.go @@ -36,7 +36,7 @@ func (f *CallFuncAction) Adapt() { case []interface{}: err := ms.Decode(f.Do, &tasknames) - u.LogErrorAndExit("call func adapter", err, "please ref to a task name only") + u.LogErrorAndPanic("call func adapter", err, "please ref to a task name only") default: u.LogWarn("call func", "Not implemented or void for no action!") diff --git a/biz/impl/cmdfunc.go b/biz/impl/cmdfunc.go index 2021702..afc6468 100644 --- a/biz/impl/cmdfunc.go +++ b/biz/impl/cmdfunc.go @@ -47,7 +47,7 @@ func (f *CmdFuncAction) Adapt() { switch f.Do.(type) { case []interface{}: err := ms.Decode(f.Do, &cmds) - u.LogErrorAndExit("Cmd adapter", err, "please fix cmd command configuration") + u.LogErrorAndPanic("Cmd adapter", err, "please fix cmd command configuration") default: u.LogWarn("cmd", "Not implemented or void for no action!") @@ -252,7 +252,7 @@ func (f *CmdFuncAction) Exec() { if failed { doFlag("failFast", func() { - u.InvalidAndExit("Assert Failed", "failFast and STOPS here!!!") + u.InvalidAndPanic("Assert Failed", "failFast and STOPS here!!!") }) } @@ -316,7 +316,7 @@ func (f *CmdFuncAction) Exec() { filepath := path.Join(dir, filename) content, err := ioutil.ReadFile(filepath) - u.LogErrorAndExit("cmd readFile", err, "please fix file path and name issues") + u.LogErrorAndPanic("cmd readFile", err, "please fix file path and name issues") if localOnly { f.Vars.Put(varname, string(content)) @@ -388,7 +388,7 @@ func (f *CmdFuncAction) Exec() { } if dataCnt > 1 { - u.InvalidAndExit("data validation", "only one data source is alllowed") + u.InvalidAndPanic("data validation", "only one data source is alllowed") } if datafile != "" { @@ -402,9 +402,9 @@ func (f *CmdFuncAction) Exec() { rendered = Render(string(tbuf), data) } - u.LogErrorAndExit("read template", err, "please fix file path and name issues") + u.LogErrorAndPanic("read template", err, "please fix file path and name issues") err = ioutil.WriteFile(dest, []byte(rendered), 0644) - u.LogErrorAndExit("write template", err, "please fix file path and name issues") + u.LogErrorAndPanic("write template", err, "please fix file path and name issues") }) @@ -449,13 +449,13 @@ func (f *CmdFuncAction) Exec() { }) if yqpath == "" || reg == "" { - u.InvalidAndExit("query cmd mandatory attribute validation", "path and reg are all mandatory and required") + u.InvalidAndPanic("query cmd mandatory attribute validation", "path and reg are all mandatory and required") } if ymlkey != "" { tmpymlstr := f.Vars.Get(ymlkey) if tmpymlstr == nil { - u.InvalidAndExit("data validation", "ymlkey does not exist, please fix it") + u.InvalidAndPanic("data validation", "ymlkey does not exist, please fix it") } ymlstr := tmpymlstr.(string) if ymlOnly { @@ -523,11 +523,11 @@ func (f *CmdFuncAction) Exec() { }) if yqpath == "" || ymlfile == "" { - u.InvalidAndExit("mandatory attribute validation", "ymlfile and path are mandatory and required") + u.InvalidAndPanic("mandatory attribute validation", "ymlfile and path are mandatory and required") } if inplace == true && reg != "" { - u.InvalidAndExit("ymlDelete criteria validation", "inplace and reg are mutual exclusive") + u.InvalidAndPanic("ymlDelete criteria validation", "inplace and reg are mutual exclusive") } modified, err := yq.UpDeletePathFromFile(path.Join(refdir, ymlfile), yqpath, inplace, verbose) @@ -578,11 +578,11 @@ func (f *CmdFuncAction) Exec() { }) if ymlstr == "" || yqpath == "" || reg == "" { - u.InvalidAndExit("mandatory attribute validation", "ymlstr, path and reg are required") + u.InvalidAndPanic("mandatory attribute validation", "ymlstr, path and reg are required") } if value != "" && nodevalue != "" { - u.InvalidAndExit("value validation", "value and nodevalue are mutual exclusive") + u.InvalidAndPanic("value validation", "value and nodevalue are mutual exclusive") } if value != "" { @@ -624,7 +624,7 @@ func (f *CmdFuncAction) Exec() { }) if varname == "" { - u.InvalidAndExit("validate varname", "the reg varname must not be empty") + u.InvalidAndPanic("validate varname", "the reg varname must not be empty") } if localOnly { f.Vars.Put(varname, varvalue) @@ -705,7 +705,7 @@ func (f *CmdFuncAction) Exec() { srcyml := func() string { if src != "" && fromkey != "" { - u.InvalidAndExit("locate yml string", "you can only use either key or src, but not both") + u.InvalidAndPanic("locate yml string", "you can only use either key or src, but not both") } if src != "" { return src @@ -715,7 +715,7 @@ func (f *CmdFuncAction) Exec() { if t != nil { return t.(string) } else { - u.InvalidAndExit("locate yml string", "please use a valid addressable varkey to locate a yml document") + u.InvalidAndPanic("locate yml string", "please use a valid addressable varkey to locate a yml document") return "" } } @@ -723,7 +723,7 @@ func (f *CmdFuncAction) Exec() { }() obj := new(interface{}) err := yaml.Unmarshal([]byte(srcyml), obj) - u.LogErrorAndExit("cmd toObj:", err, "please validate the ymal content") + u.LogErrorAndPanic("cmd toObj:", err, "please validate the ymal content") if localOnly { (*f.Vars).Put(reg, *obj) diff --git a/biz/impl/dvar.go b/biz/impl/dvar.go index 5464b5e..9dcefe7 100644 --- a/biz/impl/dvar.go +++ b/biz/impl/dvar.go @@ -48,15 +48,15 @@ func (dvars *Dvars) ValidateAndLoading(contextVars *core.Cache) { if strings.Contains(dvar.Name, "-") { identified = true - u.InvalidAndExit("validating dvar name", "dvar name can not contain '-', please use '_' instead") + u.InvalidAndPanic("validating dvar name", "dvar name can not contain '-', please use '_' instead") } if u.CharIsNum(dvar.Name[0:1]) != -1 { identified = true - u.InvalidAndExit("validating dvar name", "dvar name can not start with number") + u.InvalidAndPanic("validating dvar name", "dvar name can not start with number") } if dvar.Ref != "" && dvar.Value != "" { - u.InvalidAndExit("validating dvar ref and value", "ref and value can not both exist at the same time") + u.InvalidAndPanic("validating dvar ref and value", "ref and value can not both exist at the same time") } refdir := ConfigRuntime().RefDir @@ -70,13 +70,13 @@ func (dvars *Dvars) ValidateAndLoading(contextVars *core.Cache) { ref := Render(rawref, contextVars) data, err := ioutil.ReadFile(path.Join(refdir, ref)) - u.LogErrorAndExit("load dvar value from ref file", err, "please fix file loading problem") + u.LogErrorAndPanic("load dvar value from ref file", err, "please fix file loading problem") (*dvars)[idx].Value = string(data) } } if identified { - u.InvalidAndExit("dvar validate", "the dvar name identified above should be fixed before continue") + u.InvalidAndPanic("dvar validate", "the dvar name identified above should be fixed before continue") } } @@ -127,7 +127,7 @@ func (dvars *Dvars) Expand(mark string, contextVars *core.Cache) *core.Cache { var rval string if dvar.DataKey != "" && dvar.DataPath != "" && dvar.DataTemplate != "" { - u.InvalidAndExit("validating datasource", "datakey, datapath and datatemplate can not coexist at the same time") + u.InvalidAndPanic("validating datasource", "datakey, datapath and datatemplate can not coexist at the same time") } //the rendering using the datakey is the post rendering process @@ -187,7 +187,7 @@ func (dvars *Dvars) Expand(mark string, contextVars *core.Cache) *core.Cache { rawyml := dvar.Rendered err := yaml.Unmarshal([]byte(rawyml), objConverted) - u.LogErrorAndExit("dvar conversion to object:", err, u.ContentWithLineNumber(rawyml)) + u.LogErrorAndPanic("dvar conversion to object:", err, u.ContentWithLineNumber(rawyml)) dvarObjName = func() (dvarname string) { if u.Contains(dvar.Flags, "keepName") { diff --git a/biz/impl/pipein.go b/biz/impl/pipein.go index 546813c..5ae68fd 100644 --- a/biz/impl/pipein.go +++ b/biz/impl/pipein.go @@ -17,7 +17,7 @@ import ( func Pipein() { info, err := os.Stdin.Stat() if err != nil { - u.LogErrorAndExit("Pipe in error", err, "please double check you CLI pipe in syntax") + u.LogErrorAndPanic("Pipe in error", err, "please double check you CLI pipe in syntax") } if info.Mode()&os.ModeCharDevice != 0 { diff --git a/biz/impl/scope.go b/biz/impl/scope.go index ebd813a..62f3418 100644 --- a/biz/impl/scope.go +++ b/biz/impl/scope.go @@ -42,12 +42,12 @@ func DecryptAndRegister(securetag *u.SecureSetting, dvar *Dvar, contextVars *cor s := securetag if s == nil { - u.InvalidAndExit("check secure setting", "secure setting has to be explicit in dvar secure node, or as a default setting in upconfig.yml") + u.InvalidAndPanic("check secure setting", "secure setting has to be explicit in dvar secure node, or as a default setting in upconfig.yml") } var encryptionkey string if s.KeyRef != "" { data, err := ioutil.ReadFile(s.KeyRef) - u.LogErrorAndExit("load secure key from ref file", err, "please fix file loading problem") + u.LogErrorAndPanic("load secure key from ref file", err, "please fix file loading problem") encryptionkey = string(data) } @@ -63,7 +63,7 @@ func DecryptAndRegister(securetag *u.SecureSetting, dvar *Dvar, contextVars *cor secureName := u.Spf("%s_%s", "secure", dvar.Name) (*mergeTarget).Put(secureName, decrypted) } else { - u.InvalidAndExit("dvar decrypt", u.Spf("please double check secure settings for [%s]", dvar.Name)) + u.InvalidAndPanic("dvar decrypt", u.Spf("please double check secure settings for [%s]", dvar.Name)) } } diff --git a/biz/impl/step.go b/biz/impl/step.go index 209918f..8e174e9 100644 --- a/biz/impl/step.go +++ b/biz/impl/step.go @@ -144,16 +144,16 @@ func validation(vars *core.Cache) { for k, _ := range *vars { if k == "" { - u.InvalidAndExit("validating var name", "var name can not be empty") + u.InvalidAndPanic("validating var name", "var name can not be empty") } if u.CharIsNum(k[0:1]) != -1 { identified = true - u.InvalidAndExit("validating var name", u.Spf("var name (%s) can not start with number", k)) + u.InvalidAndPanic("validating var name", u.Spf("var name (%s) can not start with number", k)) } } if identified { - u.InvalidAndExit("vars validation", "please fix all validation before continue") + u.InvalidAndPanic("vars validation", "please fix all validation before continue") } } @@ -162,32 +162,37 @@ func (step *Step) Exec(fromBlock bool) { var action biz.Do defer func() { - u.PlnBlue("Step Finally:") - u.Ppmsg(StepRuntime().Result) - u.Ppmsg(step.Vars) + if step.Finally != nil && step.Finally != "" { + u.PlnBlue("Step Finally:") + u.Ppmsg(StepRuntime().Result) + u.Ppmsg(step.Vars) + } paniced := false - //u.Pdebug(step) if step.Vars == nil { step.Vars = *core.NewCache() } step.Vars.Put(UP_RUNTIME_SHELL_EXEC_RESULT, StepRuntime().Result) //debugVars() + var panicInfo interface{} if r := recover(); r != nil { - u.Pln("Recovered", r) + u.PlnBlue(u.Spf("Recovered from: %s", r)) paniced = true + panicInfo = r } if step.Finally != nil && step.Finally != "" { - stepFinally(step.Finally, &step.Vars) + execFinally(step.Finally, &step.Vars) } + step.Vars.Delete(UP_RUNTIME_SHELL_EXEC_RESULT) + if paniced && step.Rescue == false { - u.InvalidAndExit("No rescued", "please assess the panic problem and cause, fix it before re-run the task") + u.LogWarn("No rescued in step level", "please assess the panic problem and cause, fix it before re-run the task") + panic(panicInfo) } else if paniced { - u.LogWarn("Rescued, but not advised!", "setting rescue to yes/true to continue is not recommended\nit is advised to locate root cause of the problem, fix it and re-run the task again\nit is the best practice to test the execution in your ci pipeline to eliminate problems rather than dynamically fix using rescue") + u.LogWarn("Rescued in step level, but not advised!", "setting rescue to yes/true to continue is not recommended\nit is advised to locate root cause of the problem, fix it and re-run the task again\nit is the best practice to test the execution in your ci pipeline to eliminate problems rather than dynamically fix using rescue") } - step.Vars.Delete(UP_RUNTIME_SHELL_EXEC_RESULT) }() var bizErr *ee.Error = ee.New() @@ -237,11 +242,11 @@ func (step *Step) Exec(fromBlock bool) { action = biz.Do(&funcAction) case "": - u.InvalidAndExit("Step dispatch", "func name is empty and not defined") + u.InvalidAndPanic("Step dispatch", "func name is empty and not defined") bizErr.Mark = "func name not implemented" default: - u.InvalidAndExit("Step dispatch", u.Spf("func name(%s) is not recognised and implemented", step.Func)) + u.InvalidAndPanic("Step dispatch", u.Spf("func name(%s) is not recognised and implemented", step.Func)) bizErr.Mark = "func name not implemented" } } @@ -271,7 +276,7 @@ func (step *Step) Exec(fromBlock bool) { loopVarName := Render(step.Loop.(string), stepExecVars) loopObj := stepExecVars.Get(loopVarName) if loopObj == nil { - u.InvalidAndExit("Evaluating loop var and object", u.Spf("Please use a correct varname:(%s) containing a list of values", loopVarName)) + u.InvalidAndPanic("Evaluating loop var and object", u.Spf("Please use a correct varname:(%s) containing a list of values", loopVarName)) } if reflect.TypeOf(loopObj).Kind() == reflect.Slice { switch loopObj.(type) { @@ -281,7 +286,7 @@ func (step *Step) Exec(fromBlock bool) { if rawUtil != "" { untilEval := Render(rawUtil, stepExecVars) toBreak, err := strconv.ParseBool(untilEval) - u.LogErrorAndExit("evaluate until condition", err, u.Spf("please fix until condition evaluation: [%s]", untilEval)) + u.LogErrorAndPanic("evaluate until condition", err, u.Spf("please fix until condition evaluation: [%s]", untilEval)) if toBreak { u.Pvvvv("loop util conditional break") break @@ -299,7 +304,7 @@ func (step *Step) Exec(fromBlock bool) { if rawUtil != "" { untilEval := Render(rawUtil, stepExecVars) toBreak, err := strconv.ParseBool(untilEval) - u.LogErrorAndExit("evaluate until condition", err, u.Spf("please fix until condition evaluation: [%s]", untilEval)) + u.LogErrorAndPanic("evaluate until condition", err, u.Spf("please fix until condition evaluation: [%s]", untilEval)) if toBreak { u.Pvvvv("loop util conditional break") break @@ -317,7 +322,7 @@ func (step *Step) Exec(fromBlock bool) { if rawUtil != "" { untilEval := Render(rawUtil, stepExecVars) toBreak, err := strconv.ParseBool(untilEval) - u.LogErrorAndExit("evaluate until condition", err, u.Spf("please fix until condition evaluation: [%s]", untilEval)) + u.LogErrorAndPanic("evaluate until condition", err, u.Spf("please fix until condition evaluation: [%s]", untilEval)) if toBreak { u.Pvvvv("loop util conditional break") break @@ -333,7 +338,7 @@ func (step *Step) Exec(fromBlock bool) { u.LogWarn("loop item evaluation", "Loop item type is not supported yet!") } } else { - u.InvalidAndExit("evaluate loop var", "loop var is not a array/list/slice") + u.InvalidAndPanic("evaluate loop var", "loop var is not a array/list/slice") } } else if reflect.TypeOf(step.Loop).Kind() == reflect.Slice { //loop itself is a slice @@ -342,7 +347,7 @@ func (step *Step) Exec(fromBlock bool) { if rawUtil != "" { untilEval := Render(rawUtil, stepExecVars) toBreak, err := strconv.ParseBool(untilEval) - u.LogErrorAndExit("evaluate until condition", err, u.Spf("please fix until condition evaluation: [%s]", untilEval)) + u.LogErrorAndPanic("evaluate until condition", err, u.Spf("please fix until condition evaluation: [%s]", untilEval)) if toBreak { u.Pvvvv("loop util conditional break") break @@ -354,7 +359,7 @@ func (step *Step) Exec(fromBlock bool) { } } } else { - u.InvalidAndExit("evaluate loop items", "please either use a list or a template evaluation which could result in a value of a list") + u.InvalidAndPanic("evaluate loop items", "please either use a list or a template evaluation which could result in a value of a list") } }() @@ -373,7 +378,7 @@ func (step *Step) Exec(fromBlock bool) { IfEval := Render(step.If, stepExecVars) if IfEval != NONE_VALUE { goahead, err := strconv.ParseBool(IfEval) - u.LogErrorAndExit("evaluate condition", err, u.Spf("please fix if condition evaluation: [%s]", IfEval)) + u.LogErrorAndPanic("evaluate condition", err, u.Spf("please fix if condition evaluation: [%s]", IfEval)) if goahead { dryRunOrContinue() } else { @@ -408,11 +413,11 @@ func doElse(elseCalls interface{}, execVars *core.Cache) { elseStr := u.Spf("%s", elseCalls) if strings.Index(elseStr, "map") != -1 && strings.Index(elseStr, "func:") != -1 { err := ms.Decode(elseCalls, &flow) - u.LogErrorAndExit("load steps in else", err, "steps has configuration problem, please fix it") + u.LogErrorAndPanic("load steps in else", err, "steps has configuration problem, please fix it") BlockFlowRun(&flow, execVars) } else { err := ms.Decode(elseCalls, &tasknames) - u.LogErrorAndExit("call func alias: else", err, "please ref to a task name only") + u.LogErrorAndPanic("call func alias: else", err, "please ref to a task name only") } default: @@ -429,7 +434,7 @@ func doElse(elseCalls interface{}, execVars *core.Cache) { } -func stepFinally(finally interface{}, execVars *core.Cache) { +func execFinally(finally interface{}, execVars *core.Cache) { var taskname string var tasknames []string var flow Steps @@ -442,11 +447,11 @@ func stepFinally(finally interface{}, execVars *core.Cache) { elseStr := u.Spf("%s", finally) if strings.Index(elseStr, "map") != -1 && strings.Index(elseStr, "func:") != -1 { err := ms.Decode(finally, &flow) - u.LogErrorAndExit("load steps in finally", err, "steps/flow has configuration problem, please fix it") + u.LogErrorAndPanic("load steps in finally", err, "steps/flow has configuration problem, please fix it") BlockFlowRun(&flow, execVars) } else { err := ms.Decode(finally, &tasknames) - u.LogErrorAndExit("load task names in finally", err, "please ref to a task name only") + u.LogErrorAndPanic("load task names in finally", err, "please ref to a task name only") } default: @@ -519,7 +524,7 @@ func (steps *Steps) InspectSteps(tree treeprint.Tree, level *int) bool { //detailed steps var steps Steps err := ms.Decode(step.Do, &steps) - u.LogErrorAndExit("load steps", err, "configuration problem, please fix it") + u.LogErrorAndPanic("load steps", err, "configuration problem, please fix it") steps.InspectSteps(branch, level) default: @@ -551,9 +556,7 @@ func (steps *Steps) Exec(fromBlock bool) { if step.Do == nil && step.Dox != nil { u.LogWarn("*", "Step is deactivated!") } else { - u.Pln("pre step .........", StepRuntime().Stepname) step.Exec(fromBlock) - u.Pln("post step .........", StepRuntime().Stepname) } result := StepRuntime().Result @@ -585,7 +588,7 @@ func (steps *Steps) Exec(fromBlock bool) { if !u.Contains(step.Flags, "ignoreError") { if result != nil && result.Code != 0 { - u.InvalidAndExit("Failed And Not Ignored!", "You may want to continue and ignore the error") + u.InvalidAndPanic("Failed And Not Ignored!", "You may want to continue and ignore the error") } } else { if result != nil && result.Code != 0 { diff --git a/biz/impl/tasker.go b/biz/impl/tasker.go index 0e030ae..013393a 100644 --- a/biz/impl/tasker.go +++ b/biz/impl/tasker.go @@ -123,7 +123,7 @@ func (t *Tasker) loadInstancesContext() { if s.Ref != "" && s.Vars != nil { u.Dvvvvv(s) - u.InvalidAndExit("verify scope ref and member coexistence", "ref and members can not both exist") + u.InvalidAndPanic("verify scope ref and member coexistence", "ref and members can not both exist") } refdir := ConfigRuntime().RefDir if s.Ref != "" { @@ -147,7 +147,7 @@ func (t *Tasker) loadInstancesContext() { for idx, s := range *ss { if s.Name == "global" { if s.Members != nil { - u.InvalidAndExit("scope expand", "global scope should not contains members") + u.InvalidAndPanic("scope expand", "global scope should not contains members") } globalScope = &(*ss)[idx] } @@ -165,7 +165,7 @@ func (t *Tasker) loadInstancesContext() { if s.Members != nil { for _, m := range s.Members { if u.Contains(t.GroupMembersList, m) { - u.InvalidAndExit("scope expand", u.Spfv("duplicated member: %s\n", m)) + u.InvalidAndPanic("scope expand", u.Spfv("duplicated member: %s\n", m)) } t.GroupMembersList = append(t.GroupMembersList, m) t.MemberGroupMap[m] = s.Name @@ -214,7 +214,7 @@ func (t *Tasker) loadExecProfileEnvVars() { if p := t.getExecProfile(t.ExecProfilename); p != nil { if p.Ref != "" && p.Evars != nil { - u.InvalidAndExit("exec proile validation", "You can only setup either ref file to load the env vars or use evars tag to config env vars, but not both") + u.InvalidAndPanic("exec proile validation", "You can only setup either ref file to load the env vars or use evars tag to config env vars, but not both") } refdir := ConfigRuntime().RefDir @@ -374,7 +374,7 @@ func (t *Tasker) ListAllTasks() { func (t *Tasker) LockModules() { if !t.ValidateAllModules() { - u.InvalidAndExit("modules configuration is not valid", "please fix the problem and try again") + u.InvalidAndPanic("modules configuration is not valid", "please fix the problem and try again") } u.Pln("-lock repos:") @@ -403,7 +403,7 @@ func (t *Tasker) LockModules() { func (t *Tasker) CleanModules() { if !t.ValidateAllModules() { - u.InvalidAndExit("modules configuration is not valid", "please fix the problem and try again") + u.InvalidAndPanic("modules configuration is not valid", "please fix the problem and try again") } u.Pln("-clean repos:") //u.Pdebug(u.MainConfig.AbsWorkDir, u.GetDefaultModuleDir()) @@ -412,7 +412,7 @@ func (t *Tasker) CleanModules() { func (t *Tasker) PullModules() { if !t.ValidateAllModules() { - u.InvalidAndExit("modules configuration is not valid", "please fix the problem and try again") + u.InvalidAndPanic("modules configuration is not valid", "please fix the problem and try again") } u.Pln("-pull repos:") @@ -508,7 +508,7 @@ func (tasker *Tasker) ListTask(taskname string) { u.Pf("%s: %s", color.BlueString("%s", task.Name), desc) var steps Steps err := ms.Decode(task.Task, &steps) - u.LogErrorAndExit("decode steps:", err, "please fix data type in yaml config") + u.LogErrorAndPanic("decode steps:", err, "please fix data type in yaml config") steps.InspectSteps(tree, &level) } } @@ -528,7 +528,7 @@ func (tasker *Tasker) InspectTask(taskname string, branch treeprint.Tree, level br := branch.AddMetaBranch(color.BlueString("%s", task.Name), desc) var steps Steps err := ms.Decode(task.Task, &steps) - u.LogErrorAndExit("decode steps:", err, "please fix data type in yaml config") + u.LogErrorAndPanic("decode steps:", err, "please fix data type in yaml config") for _, step := range steps { desc := strings.Split(step.Desc, "\n")[0] @@ -582,7 +582,7 @@ func (tasker *Tasker) InspectTask(taskname string, branch treeprint.Tree, level //detailed steps var steps Steps err := ms.Decode(step.Do, &steps) - u.LogErrorAndExit("load steps", err, "configuration problem, please fix it") + u.LogErrorAndPanic("load steps", err, "configuration problem, please fix it") steps.InspectSteps(branch, level) default: @@ -610,7 +610,7 @@ func ExecTask(fulltaskname string, callerVars *core.Cache) { func() { subnames := strings.Split(fulltaskname, ".") if len(subnames) > 2 { - u.InvalidAndExit("task name validation", "task naming pattern: modulename.taskname") + u.InvalidAndPanic("task name validation", "task naming pattern: modulename.taskname") } if len(subnames) == 1 { @@ -626,13 +626,13 @@ func ExecTask(fulltaskname string, callerVars *core.Cache) { TaskerRuntime().Tasker.ExecTask(taskname, callerVars, false) } else { if modname == GetBaseModuleName() { - u.InvalidAndExit("module name should not be the same as the main caller", "please check your task configuration") + u.InvalidAndPanic("module name should not be the same as the main caller", "please check your task configuration") } else { cwd, err := os.Getwd() if err != nil { - u.LogErrorAndExit("cwd", err, "working directory error") + u.LogErrorAndPanic("cwd", err, "working directory error") } mods := TaskerRuntime().Tasker.Config.Modules @@ -678,10 +678,10 @@ func ExecTask(fulltaskname string, callerVars *core.Cache) { UpRunTimeVars.Put(UP_RUNTIME_TASKER_LAYER_NUMBER, taskerLayer) u.Pvvvv("Executing tasker layer:", taskerLayer) maxLayers, err := strconv.Atoi(u.MainConfig.MaxModuelCallLayers) - u.LogErrorAndExit("evaluate max tasker module call layer", err, "please setup max MaxModuelCallLayers properly for your case") + u.LogErrorAndPanic("evaluate max tasker module call layer", err, "please setup max MaxModuelCallLayers properly for your case") if maxLayers != 0 && taskerLayer > maxLayers { - u.InvalidAndExit("Module call layer check:", u.Spf("Too many layers of recursive module executions, max allowed(%d), please fix your recursive call", maxLayers)) + u.InvalidAndPanic("Module call layer check:", u.Spf("Too many layers of recursive module executions, max allowed(%d), please fix your recursive call", maxLayers)) } }() @@ -690,7 +690,7 @@ func ExecTask(fulltaskname string, callerVars *core.Cache) { os.Chdir(cwd) } else { //TODO: put the reasoning into the doco: not to auto update to avoid evil code injection problem - u.InvalidAndExit(u.Spf("module dir: [%s] does not exist under: [%s]\n", mod.Dir, cwd), "double check if you have change your module configuration, then you will probably need to update module again") + u.InvalidAndPanic(u.Spf("module dir: [%s] does not exist under: [%s]\n", mod.Dir, cwd), "double check if you have change your module configuration, then you will probably need to update module again") } }() } else { @@ -700,7 +700,7 @@ func ExecTask(fulltaskname string, callerVars *core.Cache) { } else { callerName := TaskerRuntime().Tasker.Config.ModuleName - u.InvalidAndExit(u.Spf("caller Module [%s] is not configured,", callerName), u.Spf("module: [%s], task: [%s]", modname, taskname)) + u.InvalidAndPanic(u.Spf("caller Module [%s] is not configured,", callerName), u.Spf("module: [%s], task: [%s]", modname, taskname)) } } @@ -712,12 +712,36 @@ func (t *Tasker) ExecTask(taskname string, callerVars *core.Cache, isExternalCal found := false for idx, task := range *t.Tasks { if taskname == task.Name { + + defer func() { + if task.Finally != nil && task.Finally != "" { + u.PlnBlue("task Finally:") + } + paniced := false + var panicInfo interface{} + if r := recover(); r != nil { + u.Pvvvvv(u.Spf("Recovered from: %s", r)) + paniced = true + panicInfo = r + } + + if task.Finally != nil && task.Finally != "" { + execFinally(task.Finally, core.NewCache()) + } + + if paniced && task.Rescue == false { + u.LogWarn("No rescued in task level", "please assess the panic problem and cause, fix it before re-run the task") + u.PanicExit("task finally", panicInfo) + } else if paniced { + u.LogWarn("Rescued in task level, but not advised!", "setting rescue to yes/true to continue is not recommended\nit is advised to locate root cause of the problem, fix it and re-run the task again\nit is the best practice to test the execution in your ci pipeline to eliminate problems rather than dynamically fix using rescue") + } + + }() + u.Pfvvvv(" located task-> %d [%s]: \n", idx+1, task.Name) var ctxCallerTaskname string - //u.Ptmpdebug("RRR", TaskerStack.GetLen()) - if isExternalCall { ctxCallerTaskname = "TODO: Main Caller Taskname" } else { @@ -734,7 +758,7 @@ func (t *Tasker) ExecTask(taskname string, callerVars *core.Cache, isExternalCal var steps Steps err := ms.Decode(task.Task, &steps) - u.LogErrorAndExit("decode steps:", err, "please fix data type in yaml config") + u.LogErrorAndPanic("decode steps:", err, "please fix data type in yaml config") func() { //step name validation invalidNames := []string{} @@ -745,7 +769,7 @@ func (t *Tasker) ExecTask(taskname string, callerVars *core.Cache, isExternalCal } if len(invalidNames) > 0 { - u.InvalidAndExit(u.Spf("validating step name fails: %s ", invalidNames), "task name can not contain '-', please use '_' instead, failed names:") + u.InvalidAndPanic(u.Spf("validating step name fails: %s ", invalidNames), "task name can not contain '-', please use '_' instead, failed names:") } }() @@ -786,16 +810,14 @@ func (t *Tasker) ExecTask(taskname string, callerVars *core.Cache, isExternalCal TaskerRuntime().Tasker.TaskStack.Push(&rtContext) u.Pvvvv("Executing task stack layer:", TaskerRuntime().Tasker.TaskStack.GetLen()) maxLayers, err := strconv.Atoi(ConfigRuntime().MaxCallLayers) - u.LogErrorAndExit("evaluate max task stack layer", err, "please setup max MaxCallLayers correctly") + u.LogErrorAndPanic("evaluate max task stack layer", err, "please setup max MaxCallLayers correctly") if maxLayers != 0 && TaskerRuntime().Tasker.TaskStack.GetLen() > maxLayers { - u.InvalidAndExit("Task exec stack layer check:", u.Spf("Too many layers of task executions, max allowed(%d), please fix your recursive call", maxLayers)) + u.InvalidAndPanic("Task exec stack layer check:", u.Spf("Too many layers of task executions, max allowed(%d), please fix your recursive call", maxLayers)) } }() - u.Pln("pre task .........", TaskRuntime().Taskname) steps.Exec(false) - u.Pln("post task .........", TaskRuntime().Taskname) returnVars := TaskRuntime().ReturnVars @@ -830,7 +852,7 @@ func (t *Tasker) ExecTask(taskname string, callerVars *core.Cache, isExternalCal if !found { u.Pferror("Task %s is not defined!", taskname) t.ListTasks() - u.InvalidAndExit("Task call failed", "Task does not exist") + u.InvalidAndPanic("Task call failed", "Task does not exist") } } @@ -844,7 +866,7 @@ func (t *Tasker) validateAndLoadTaskRef() { } if task.Task != nil && task.Ref != "" { - u.InvalidAndExit("validate task node and ref", "task and ref can not coexist") + u.InvalidAndPanic("validate task node and ref", "task and ref can not coexist") } //load ref task @@ -866,7 +888,7 @@ func (t *Tasker) validateAndLoadTaskRef() { } if len(invalidNames) > 0 { - u.InvalidAndExit(u.Spf("validating task name fails: %s ", invalidNames), "task name can not contain '-', please use '_' instead, failed names:") + u.InvalidAndPanic(u.Spf("validating task name fails: %s ", invalidNames), "task name can not contain '-', please use '_' instead, failed names:") } } @@ -880,7 +902,7 @@ func (t *Tasker) loadRefTasks() { var tasks model.Tasks tasksData := tasksYmlRoot.Get("tasks") err := ms.Decode(tasksData, &tasks) - u.LogErrorAndExit(u.Spf("decode tasks:%s", tasksYamlName), err, "please fix configuration in tasks yaml file") + u.LogErrorAndPanic(u.Spf("decode tasks:%s", tasksYamlName), err, "please fix configuration in tasks yaml file") for _, task := range tasks { *t.Tasks = append(*t.Tasks, task) } @@ -892,7 +914,7 @@ func (t *Tasker) loadTasks() error { tasksData := t.TaskYmlRoot.Get("tasks") var tasks model.Tasks err := ms.Decode(tasksData, &tasks) - u.LogErrorAndExit("decode tasks:main", err, "please fix configuration in tasks yaml file") + u.LogErrorAndPanic("decode tasks:main", err, "please fix configuration in tasks yaml file") t.Tasks = &tasks t.loadRefTasks() t.validateAndLoadTaskRef() @@ -904,7 +926,7 @@ func loadRefFlow(yamlroot *viper.Viper) *Steps { flowData := yamlroot.Get("flow") var flow Steps err := ms.Decode(flowData, &flow) - u.LogErrorAndExit("load ref flow", err, "flow of the steps has configuration problem, please fix it") + u.LogErrorAndPanic("load ref flow", err, "flow of the steps has configuration problem, please fix it") return &flow } @@ -914,7 +936,7 @@ func (t *Tasker) loadScopes() { err := ms.Decode(scopesData, &scopes) t.ScopeProfiles = &scopes - u.LogErrorAndExit("load full scopes", err, "please assess your scope configuration carefully") + u.LogErrorAndPanic("load full scopes", err, "please assess your scope configuration carefully") } func (t *Tasker) loadExecProfiles() { @@ -923,7 +945,7 @@ func (t *Tasker) loadExecProfiles() { err := ms.Decode(eprofileData, &eprofiles) t.ExecProfiles = &eprofiles - u.LogErrorAndExit("load exec profiles", err, "please assess your exec profiles configuration carefully") + u.LogErrorAndPanic("load exec profiles", err, "please assess your exec profiles configuration carefully") } func (t *Tasker) getExecProfile(pname string) *ExecProfile { @@ -991,7 +1013,7 @@ func (t *Tasker) loadRuntimeGlobalDvars() { dvarsData := t.TaskYmlRoot.Get("dvars") var dvars Dvars err := ms.Decode(dvarsData, &dvars) - u.LogErrorAndExit("loadRuntimeGlobalDvars", + u.LogErrorAndPanic("loadRuntimeGlobalDvars", err, "You must fix the data type to be\n string for a dvar value and try again. possible problems:\nthe name can not be single character 'y' or 'n' ", ) diff --git a/biz/impl/template.go b/biz/impl/template.go index c7d8ace..c9cd2d5 100644 --- a/biz/impl/template.go +++ b/biz/impl/template.go @@ -128,7 +128,7 @@ func FuncMapInit() { }, "validateMandatoryFailIfNone": func(varname, varvalue string) string { if varvalue == "" { - u.InvalidAndExit("validateMandatoryFailIfNone", u.Spf("Required var:(%s) must not be empty, please fix it", varname)) + u.InvalidAndPanic("validateMandatoryFailIfNone", u.Spf("Required var:(%s) must not be empty, please fix it", varname)) } return varvalue }, @@ -156,7 +156,7 @@ func Render(tstr string, obj interface{}) string { tname := "." t, err := template.New(tname).Funcs(templateFuncs).Parse(tstr) - u.LogErrorAndExit("template creating", err, u.ContentWithLineNumber(tstr)) + u.LogErrorAndPanic("template creating", err, u.ContentWithLineNumber(tstr)) var result bytes.Buffer err = t.Execute(&result, obj) diff --git a/model/core/ymlobj.go b/model/core/ymlobj.go index 9e4b947..b41ecb8 100644 --- a/model/core/ymlobj.go +++ b/model/core/ymlobj.go @@ -17,7 +17,7 @@ import ( func ObjToYaml(obj interface{}) string { ymlbytes, err := yaml.Marshal(&obj) - u.LogErrorAndExit("obj to yaml converstion", err, "yml convesion failed") + u.LogErrorAndPanic("obj to yaml converstion", err, "yml convesion failed") yml := string(ymlbytes) //TODO: revist for the extra \n at the end of string cleaned := u.RemoveEmptyLines(yml) @@ -29,7 +29,7 @@ func ObjToYaml(obj interface{}) string { func LoadObjectFromFile(filepath string) interface{} { data, err := ioutil.ReadFile(filepath) - u.LogErrorAndExit("load object", err, "read file error") + u.LogErrorAndPanic("load object", err, "read file error") return YamlToObj(string(data)) } @@ -85,7 +85,7 @@ func GetSubYmlFromCache(cache *Cache, path string, collect bool, verboseLevel st elist := strings.Split(path, ".") func() { if elist[0] == "" { - u.InvalidAndExit("yml path validation", u.Spf("path format is not correct, use format like:\n %s", u.Yq_read_hint)) + u.InvalidAndPanic("yml path validation", u.Spf("path format is not correct, use format like:\n %s", u.Yq_read_hint)) } }() yqpath := strings.Join(elist[1:], ".") diff --git a/model/task.go b/model/task.go index b972d2d..e4a9a49 100644 --- a/model/task.go +++ b/model/task.go @@ -8,13 +8,13 @@ package model type Task struct { - Task interface{} //Steps - Desc string - Name string - Ref string - RefDir string + Task interface{} //Steps + Desc string + Name string + Ref string + RefDir string + Finally interface{} + Rescue bool } type Tasks []Task - - diff --git a/tests/functests/c0175.yml b/tests/functests/c0175.yml new file mode 100644 index 0000000..167aa26 --- /dev/null +++ b/tests/functests/c0175.yml @@ -0,0 +1,57 @@ +#doc_meta: | +# folder: shell-func +# title: finally to ensure clean up/rescue +# head: | +# ignoreError will make the workflow to continue +# +# sections: +# - title: Demo +# log: yes +# +# related: +# refs: +# - title: shell func +# link: ../../quick-start/c0002/ +# - title: error handling +# link: ../../test-debug/error_handling/ + +notes: + - add finally support to tasks level + +tasks: + + - + name: task + task: + + - + func: shell + name: step1 + desc: step 1 + do: + - echo "opening file" + - echo "hello"|grep "world" + + - + func: cmd + name: step2 + desc: | + in this case, since there is no ignoreError, the exception was captured by task level finaly code block + opened file is safely closed + to make the flow to continue to reach step2, use ignoreError + do: + - name: print + cmd: step 2 + + desc: | + without rescue, the execution will return a non-zero return code in shell and also report the error + with rescue, the program will return 0 + rescue: true + finally: + - + func: shell + name: close_file + desc: | + ensure the opened file is closed + do: + - echo "close the file ....." diff --git a/tests/functests/c0171.yml b/tests/functests/f0171.yml similarity index 100% rename from tests/functests/c0171.yml rename to tests/functests/f0171.yml diff --git a/utils/debug.go b/utils/debug.go index 21dc15f..e8c3734 100644 --- a/utils/debug.go +++ b/utils/debug.go @@ -330,16 +330,22 @@ func LogErrorMsg(mark string, reason string) { color.Red(" Error must fix: [%s] - [%s]", mark, reason) } -func LogErrorAndExit(mark string, err interface{}, hint string) { +func LogErrorAndPanic(mark string, err interface{}, hint string) { if err != nil { color.Red(" %s -> %s", mark, err) hiColor := color.New(color.FgHiCyan, color.BgRed) hiColor.Printf("ERROR: \n%s\n", hint) PStackTrace() - os.Exit(-1) + panic(err.(error).Error()) } } +func PanicExit(mark string, err interface{}) { + color.Red("%s -> %s", mark, err) + PStackTrace() + os.Exit(-1) +} + func LogError(mark string, err interface{}) { if err != nil { color.Red(" %s -> %s", mark, err) @@ -364,10 +370,10 @@ func LogErrorAndContinue(mark string, err interface{}, hint string) { } } -func InvalidAndExit(mark string, hint string) { +func InvalidAndPanic(mark string, hint string) { hiColor := color.New(color.FgHiCyan, color.BgRed) - hiColor.Printf(" ERROR: %s [%s]\n", mark, hint) - os.Exit(-3) + e := hiColor.Sprintf(" ERROR: %s [%s]\n", mark, hint) + panic(e) } func GraceExit(mark string, hint string) { diff --git a/utils/module.go b/utils/module.go index 5f3d0d1..50bfa85 100644 --- a/utils/module.go +++ b/utils/module.go @@ -59,10 +59,10 @@ func LoadModuleLockRevs() *ModuleLockMap { lockfile := "./modlock.yml" if _, err := os.Stat(lockfile); !os.IsNotExist(err) { yml, err := ioutil.ReadFile(lockfile) - LogErrorAndExit("load locked file", err, "read file problem, please fix it") + LogErrorAndPanic("load locked file", err, "read file problem, please fix it") revs := ModuleLockMap{} err = yaml.Unmarshal(yml, &revs) - LogErrorAndExit("load locked revs", err, "the lock file has got configuration problem, please fix it") + LogErrorAndPanic("load locked revs", err, "the lock file has got configuration problem, please fix it") return &revs } else { return nil @@ -210,19 +210,19 @@ func (m *Module) PullRepo(revMap *ModuleLockMap, uselock bool) { URL: m.Repo, Progress: os.Stdout, }) - LogErrorAndExit("Clone Module", err, "Clone errored, please fix the issue first and retry") + LogErrorAndPanic("Clone Module", err, "Clone errored, please fix the issue first and retry") } if _, err := os.Stat(clonePath); !os.IsNotExist(err) { if m.PullPolicy == "always" { Pf("removing %s ...", clonePath) err := os.RemoveAll(clonePath) - LogErrorAndExit("Remove directory", err, Spf("removing [%s] failed", clonePath)) + LogErrorAndPanic("Remove directory", err, Spf("removing [%s] failed", clonePath)) clone() } else if m.PullPolicy == "skip" { LogWarn("module repo exist: skipped", Spf("repo: [%s]", clonePath)) } else if m.PullPolicy == "manual" { - InvalidAndExit(Spf("repo: [%s] already exist", clonePath), + InvalidAndPanic(Spf("repo: [%s] already exist", clonePath), `manual resolution need: 1. You can git pull to update the module 2. If you work on the module, then you will need to commit and push your code accordingly, or @@ -251,7 +251,7 @@ You may want to re-pull the repo again to ensure it is up to date to avoid missi func GetHeadRev(repodir string) string { r, err := git.PlainOpen(repodir) - LogErrorAndExit("Open repo", err, Spf("please check repo:[%s]", repodir)) + LogErrorAndPanic("Open repo", err, Spf("please check repo:[%s]", repodir)) h, err := r.ResolveRevision(plumbing.Revision("HEAD")) return (h.String()) } @@ -273,7 +273,7 @@ func (m *Module) ShowDetails() { func (m *Module) Normalize() { if m.Dir != "" && m.Alias == "" { - InvalidAndExit("module validation", Spf("You need to use a alias to name the module: dir [%s]", m.Dir)) + InvalidAndPanic("module validation", Spf("You need to use a alias to name the module: dir [%s]", m.Dir)) } if m.Iid == "" { @@ -291,7 +291,7 @@ func (m *Module) Normalize() { if m.Alias == "" { if m.Subdir != "" { - InvalidAndExit("module validation", Spf("a alias is needed to avoid confusion i use subdir [%s]", m.Subdir)) + InvalidAndPanic("module validation", Spf("a alias is needed to avoid confusion i use subdir [%s]", m.Subdir)) } else { m.Alias = GetGitRepoName(m.Repo) } diff --git a/utils/shell.go b/utils/shell.go index e91d6e6..29fec6a 100644 --- a/utils/shell.go +++ b/utils/shell.go @@ -98,7 +98,7 @@ func RunSimpleCmd(dir string, command string) error { if dir != "" { if _, err := os.Stat(dir); !os.IsNotExist(err) { } else { - LogErrorAndExit("check dir existence", err, "exec path does not exist") + LogErrorAndPanic("check dir existence", err, "exec path does not exist") } } diff --git a/utils/upconfig.go b/utils/upconfig.go index feffc9d..c721631 100644 --- a/utils/upconfig.go +++ b/utils/upconfig.go @@ -35,7 +35,7 @@ func (cfg *UpConfig) SetRefdir(refdir string) { func (cfg *UpConfig) GetWorkdirOld() (wkdir string) { cwd, err := os.Getwd() if err != nil { - LogErrorAndExit("SetAbsWorkdir", err, "working directory error") + LogErrorAndPanic("SetAbsWorkdir", err, "working directory error") } if cfg.WorkDir == "cwd" { @@ -51,7 +51,7 @@ func (cfg *UpConfig) GetWorkdirOld() (wkdir string) { } } } else { - InvalidAndExit("SetAbsWorkdir", "Work dir setup is not proper") + InvalidAndPanic("SetAbsWorkdir", "Work dir setup is not proper") } return } @@ -62,7 +62,7 @@ func (cfg *UpConfig) SetAbsWorkdir() { var wkdir string cwd, err := os.Getwd() if err != nil { - LogErrorAndExit("SetAbsWorkdir", err, "working directory error") + LogErrorAndPanic("SetAbsWorkdir", err, "working directory error") } if cfg.WorkDir == "cwd" { @@ -79,7 +79,7 @@ func (cfg *UpConfig) SetAbsWorkdir() { } } } else { - InvalidAndExit("SetAbsWorkdir", "Work dir setup is not proper") + InvalidAndPanic("SetAbsWorkdir", "Work dir setup is not proper") } cfg.AbsWorkDir = wkdir @@ -118,5 +118,3 @@ func (cfg *UpConfig) ShowCoreConfig(mark string) { } } } - -