Search code examples
azure-devopstfsazure-pipelinesazure-devops-extensions

Catch cancellation request from a custom AzureDevOps task


Azure DevOps pipelines. Can a custom task somehow detect that the user has cancelled the build/release job mid-execution and quit gracefully? I'm wondering about both the Node API and the Powershell API.

To clarify, I'm talking about this kind of cancel:

enter image description here

Not about conditional execution of tasks.

Nothing to that effect at https://github.com/microsoft/azure-pipelines-task-lib/blob/master/node/docs/azure-pipelines-task-lib.md

"Impossible" is an acceptable answer.


EDIT: despite claims that this is impossible, there is some degree of task/job cancellation logic there. If you create a Powershell task with a Start-Sleep cmdlet (so that it deliberately takes a while) and cancel that, the job quits without waiting for the cmdlet to finish waiting. Same if you do a command line task with sleep 30s. Now, this could be good forcible old process termination, but it could be a more high level construct (e. g. emulating a Ctrl+C).

This is unrelated. There is logic in place to terminate all processes spawned by the job process (an instance of Agent.Worker.exe) when that job terminates, but it uses Process.Kill(), which calls TerminateProcess().


Okay, another data point. I've run the sleep 30s task and connected to sleep.exe with a debugger. When Cancel was clicked, here's what Visual Studio threw my way:

Exception thrown at 0x00007FF832C3EA6B (KernelBase.dll) in sleep.exe: 0x40010005: Control-C.

So it's a Ctrl+C situation, which gets translated into SEH exception, code CONTROL_C_EXIT. There are answers on SO about catching that both in Node and Powershell:


Solution

  • When Cancel is clicked, the job runner sends a Ctrl+C to task processes. Internally, it's translated to a Windows kernel level exception with dedicated code CONTROL_C_EXIT. It can be easily caught in native code. But native code is not what custom AzDevOps tasks are written in.

    On *nix, I presume, it becomes a signal.


    By default, in PowerShell, Ctrl+C has a curious effect: the currently executing cmdlet stops as if it throws an exception, but the exception is not caught by the catch handler. Importantly though, the finally block still runs. That's where the cleanup should be.

    There is a way to make Ctrl+C not end the process:

    [Console]::TreatControlCAsInput = $true

    but then you'd need to poll the console for Ctrl+C is you want to see it at all.


    In Node.js, one may react to Ctrl+C by catching the SIGINT signal on the process level:

    process.on("SIGINT", function(){
      console.log("Bye bye");
      process.exit();
    });
    

    The TFS agent ships with Node 6, 10, and 16, and this technique works in all three. The process doesn't have to quit upon receiving a Ctrl+C. Note that Ctrl+Break is a different signal, SIGBREAK.