Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added

- **Stop-Task**: Added progress bar when cancelling multiple tasks, showing current progress and task descriptions for better user feedback.

### Changed

- **Stop-Task**: Enhanced `Task` parameter to accept arrays of tasks using the new `TaskTransformation` class, allowing multiple tasks to be cancelled in a single call or via pipeline.

### Fixed

- **Stop-Task**: Removed `ValueFromPipeline` from the `Regarding` parameter to resolve parameter set ambiguity when piping task objects. Tasks can now be piped directly to the function without conflicts.

## [2.2.1] - 2026-01-14

### Fixed
Expand Down
25 changes: 22 additions & 3 deletions OctopusDeploy/Classes/TransformerClasses.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,29 @@ class TaskSingleTransformation : System.Management.Automation.ArgumentTransforma
if ($item -is [string] -and $item -like "ServerTasks-*") {
$item = Get-Task -TaskID "$item"
}
elseif ($item -is [string]) {
$item = $null
elseif ($item -is [Octopus.Client.Model.TaskResource]) {
return $item
}
return ($item)
throw "Invalid Task input: $($item.toString())"

Copilot AI Jan 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TaskSingleTransformation class now throws an exception for string inputs that don't match the "ServerTasks-*" pattern, whereas the previous implementation returned $null. This is a breaking change that could cause errors in existing code that previously handled null returns gracefully. Consider whether this breaking change is intentional and necessary.

Copilot uses AI. Check for mistakes.
}
}

class TaskTransformation : System.Management.Automation.ArgumentTransformationAttribute {
[object] Transform([System.Management.Automation.EngineIntrinsics]$EngineIntrinsics, [object] $InputData) {
$result = @()
foreach ($item in $InputData) {
if ($item -is [string] -and $item -like "ServerTasks-*") {
$item = Get-Task -TaskID "$item"
}
elseif ($item -is [Octopus.Client.Model.TaskResource]) {
# Already a TaskResource, keep it
}
Comment on lines +283 to +285

Copilot AI Jan 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the item is already a TaskResource (line 283-285), it is not added to the $result array. The code should include $result += $item in the elseif block to ensure TaskResource objects are properly included in the output array.

Copilot uses AI. Check for mistakes.
else {
throw "Invalid Task input: $($item.toString())"
}
$result += ($item)
}
return ($result)
}
}

Expand Down
54 changes: 37 additions & 17 deletions OctopusDeploy/Public/Stop-Task.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ function Stop-Task {
[CmdletBinding()]
param (
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
ValueFromPipeline = $true,
ParameterSetName = 'byTask')]
[TaskSingleTransformation()]
[Octopus.Client.Model.TaskResource]
[TaskTransformation()]
[Octopus.Client.Model.TaskResource[]]
$Task,

[Parameter(Mandatory = $false,
Expand All @@ -57,7 +57,6 @@ function Stop-Task {
$Environment,

[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ParameterSetName = 'byRegarding')]
[ValidateNotNullOrEmpty()]
[Octopus.Client.Model.Resource]
Expand All @@ -77,35 +76,55 @@ function Stop-Task {
$PSCmdlet.ThrowTerminatingError($_)
}

# Initialize counter for progress
$taskCounter = 0
$allTasks = @()
}

process {
# Initialize an empty array to store tasks to cancel
$tasksToCancel = @()
Comment on lines 85 to 86

Copilot AI Jan 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment on line 85-86 states "Initialize an empty array to store tasks to cancel", but this initialization is redundant since $tasksToCancel is immediately reassigned in all code paths (lines 94, 99, 104) without ever using the empty array. This initialization should be removed as it serves no purpose and may cause confusion.

Copilot uses AI. Check for mistakes.

# Combine states into a regex pattern
$stateRegex = ($State -join '|') -replace ' ', ''
Comment on lines 88 to 89

Copilot AI Jan 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The $stateRegex variable is recalculated on every iteration of the process block, even though it depends only on the $State parameter which doesn't change between iterations. This is inefficient when processing multiple piped items. Consider moving this calculation to the begin block to avoid redundant computation.

Copilot uses AI. Check for mistakes.
}

process {

# Check the parameter set name to determine how to retrieve tasks
if ($PSCmdlet.ParameterSetName -eq 'byTask') {
# Cancel a specific task
$tasksToCancel += $Task
$tasksToCancel = $Task
}
elseif ($PSCmdlet.ParameterSetName -eq 'byRegarding') {
# Cancel tasks regarding a specific object
foreach ($r in $Regarding) {
$tasksToCancel += Get-Task -Regarding $r | Where-Object { $_.State -match $stateRegex }
$tasksToCancel = Get-Task -Regarding $r | Where-Object { $_.State -match $stateRegex }

Copilot AI Jan 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assignment on line 99 overwrites $tasksToCancel on each iteration instead of accumulating results. When multiple $Regarding items are provided in the loop, only the tasks from the last item will be retained. This should use += to accumulate tasks across all iterations.

Suggested change
$tasksToCancel = Get-Task -Regarding $r | Where-Object { $_.State -match $stateRegex }
$tasksToCancel += Get-Task -Regarding $r | Where-Object { $_.State -match $stateRegex }

Copilot uses AI. Check for mistakes.
}
}
else {
# Retrieve tasks using Get-Task
$tasksToCancel = Get-Task -TaskType $TaskType -Tenant $Tenant -Environment $Environment | Where-Object { $_.State -match $stateRegex }
}

Write-Verbose "Found $($tasksToCancel.Count) tasks to cancel."
# Add to collection
$allTasks += $tasksToCancel
}

# Cancel each task
# Todo [DNA-327]: Add progress bar for large number of tasks to give user feedback
foreach ($_task in $tasksToCancel) {
end {
Write-Verbose "Found $($allTasks.Count) tasks to cancel."

# Cancel each task with progress bar
$totalTasks = $allTasks.Count
foreach ($_task in $allTasks) {
$taskCounter++

# Show progress bar
if ($totalTasks -gt 0) {
$percentComplete = ($taskCounter / $totalTasks) * 100
Write-Progress -Activity "Cancelling Tasks" `
-Status "Processing task $taskCounter of $totalTasks" `
-CurrentOperation "Cancelling: $($_task.Description)" `
-PercentComplete $percentComplete
}

try {
Write-Verbose "Cancelling task: $($_task.Id) - $($_task.Description)"
$repo._repository.Tasks.Cancel($_task)
Expand All @@ -114,9 +133,10 @@ function Stop-Task {
Write-Warning "Failed to cancel task $($_task.Id): $_"
}
}
}

end {

# Clear the progress bar
if ($totalTasks -gt 0) {
Write-Progress -Activity "Cancelling Tasks" -Completed
}
}
}