guaranteed final step to ensure clean up/rescue

Introduce to use finally/rescue in task step for a guranteed cleanup when task step has got panic

Demo

source

Main task yaml file
    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 ....."
    
Main log file
    loading [Config]:  ./tests/functests/upconfig.yml
    Main config:
                 Version -> 1.0.0
                  RefDir -> ./tests/functests
                 WorkDir -> cwd
              AbsWorkDir -> /up_project/up
                TaskFile -> c0172
                 Verbose -> vvv
              ModuleName -> self
               ShellType -> /bin/sh
           MaxCallLayers -> 8
                 Timeout -> 3600000
     MaxModuelCallLayers -> 256
               EntryTask -> task
      ModRepoUsernameRef -> 
      ModRepoPasswordRef -> 
    work dir: /up_project/up
    -exec task: task
    loading [Task]:  ./tests/functests/c0172
    module: [self], instance id: [dev], exec profile: []
    profile -  envVars:
    
    (*core.Cache)({
    })
    
    Task1: [task ==> task:  ]
    -Step1: [: step 1 ]
    self: final context exec vars:
    
    (*core.Cache)({
      "up_runtime_task_layer_number": 0
    })
    
    ~SubStep1: [print:  ]
    step 1
    ~SubStep2: [panic:  ]
     WARN: [manual panic] - [manual trigger a panic cmd]
    Step Finally:
    Recovered from: manual trigger a panic cmd
    =Task2: [task ==> close_file:  ]
    --Step1: [
    close_fileensure the opened file is closed
    ]
    self: final context exec vars:
    
    (*core.Cache)({
      "up_runtime_task_layer_number": 1,
      "up_runtime_shell_exec_result": (*utils.ExecResult)(<nil>)
    })
    
    cmd( 1):
    echo "close the file ....."
    
    -
    close the file .....
    
    -
     .. ok
    . ok
     WARN: [Rescued in step level, but not advised!] - [setting rescue to yes/true to continue is not recommended
    it is advised to locate root cause of the problem, fix it and re-run the task again
    it is the best practice to test the execution in your ci pipeline to eliminate problems rather than dynamically fix using rescue]
    -Step2: [: step 2 ]
    self: final context exec vars:
    
    (*core.Cache)({
      "up_runtime_task_layer_number": 1
    })
    
    ~SubStep1: [print:  ]
    step 2
    
Logs with different verbose level
Raw logs with different verbose level

what will happen if there is no rescue for the task without rescue

This showcases that the finally step invokes the close_file task to ensure the opened file is closed

The task will fail if there is no rescue to be “true”, in this case, user choose to fail the task, or if rescue is set, then the execution will continue to the next func step call

Other references: