Public/Get-TaskDefinition.ps1
|
function Get-TaskDefinition { <# .SYNOPSIS Retrieves task definitions from the Toolbox task library. .DESCRIPTION Discovers and retrieves task definitions following the resolution order: 1. Built-in tasks (module Tasks\BuiltIn) 2. System tasks (ProgramData\Toolbox\Tasks) 3. User tasks (AppData\Toolbox\Tasks) User tasks override system tasks, system tasks override built-in tasks. .PARAMETER TaskName Name of the task to retrieve (e.g., "File.TestPathExists"). Supports wildcards for listing multiple tasks. .PARAMETER Category Filter tasks by category (e.g., "File", "Process", "Network"). .PARAMETER Tag Filter tasks by tag. .PARAMETER ListAvailable Lists all available tasks. .PARAMETER Refresh Clears the task cache and reloads task definitions. .EXAMPLE Get-TaskDefinition -TaskName "File.TestPathExists" Retrieves the File.TestPathExists task definition. .EXAMPLE Get-TaskDefinition -ListAvailable Lists all available tasks. .EXAMPLE Get-TaskDefinition -Category "File" Lists all File category tasks. .EXAMPLE Get-TaskDefinition -TaskName "File.*" Lists all tasks in the File category using wildcards. .EXAMPLE Get-TaskDefinition -Tag "Network" -Verbose Lists all tasks tagged with "Network" with verbose output. .EXAMPLE Get-TaskDefinition -ListAvailable | Where-Object { $_.RequiresElevation -eq $false } Lists all tasks that don"t require elevation. .EXAMPLE Get-TaskDefinition -TaskName "System.GetUptime" | Select-Object Name, Version, Description, Timeout Gets detailed information about a specific task. .OUTPUTS PSCustomObject containing task definition metadata and script path. .NOTES Task definitions are cached for performance. Use -Refresh to reload from disk. #> [CmdletBinding(DefaultParameterSetName = "Name")] param( [Parameter(Position = 0, ParameterSetName = "Name")] [SupportsWildcards()] [string]$TaskName, [Parameter(ParameterSetName = "Filter")] [string]$Category, [Parameter(ParameterSetName = "Filter")] [string]$Tag, [Parameter(ParameterSetName = "List")] [switch]$ListAvailable, [Parameter()] [switch]$Refresh ) # Initialize task cache if needed if ($Refresh -or -not $script:TaskCache) { Initialize-TaskCache } try { $results = @() if ($ListAvailable -or $PSCmdlet.ParameterSetName -eq "Filter") { # Return all or filtered tasks $results = $script:TaskCache.Values if ($Category) { $results = $results | Where-Object { $_.Name -like "$Category.*" } } if ($Tag) { $results = $results | Where-Object { $_.Tags -contains $Tag } } } elseif ($TaskName) { # Handle wildcards if ([System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($TaskName)) { $results = $script:TaskCache.Values | Where-Object { $_.Name -like $TaskName } } else { # Direct lookup if ($script:TaskCache.ContainsKey($TaskName)) { $results = @($script:TaskCache[$TaskName]) } else { Write-Error "Task "$TaskName" not found. Use -ListAvailable to see available tasks." return } } } else { Write-Error "Please specify -TaskName, -ListAvailable, or filter parameters." return } return $results | Sort-Object Name } catch { Write-Error "Failed to get task definition: $_" } } function Get-TaskMetadataFromScript { <# .SYNOPSIS Parses task metadata from a PowerShell script"s comment-based help. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$ScriptPath ) $content = Get-Content -Path $ScriptPath -Raw # Extract comment-based help block if ($content -match "(?s)<#(.*?)#>") { $helpBlock = $Matches[1] # Extract .NOTES section which contains our task metadata if ($helpBlock -match "(?s)\.NOTES\s+(.*?)(?=\.EXAMPLE|\.LINK|$)") { $notesSection = $Matches[1].Trim() # Parse key-value pairs from NOTES $metadata = @{} $notesSection -split "`n" | ForEach-Object { if ($_ -match "^\s*(\w+):\s*(.+)$") { $key = $Matches[1].Trim() $value = $Matches[2].Trim() $metadata[$key] = $value } } # Extract SYNOPSIS $synopsis = if ($helpBlock -match "\.SYNOPSIS\s+(.*?)(?=\.DESCRIPTION|\.)") { $Matches[1].Trim() } else { "" } # Extract DESCRIPTION $description = if ($helpBlock -match "\.DESCRIPTION\s+(.*?)(?=\.PARAMETER|\.NOTES)") { $Matches[1].Trim() -replace "\s+", " " } else { $synopsis } # Parse parameters from .PARAMETER sections $parameters = @() $paramMatches = [regex]::Matches($helpBlock, "\.PARAMETER\s+(\w+)\s+(.*?)(?=\.PARAMETER|\.NOTES|\.EXAMPLE|$)", "Singleline") foreach ($match in $paramMatches) { $parameters += [PSCustomObject]@{ Name = $match.Groups[1].Value.Trim() Description = $match.Groups[2].Value.Trim() -replace "\s+", " " } } # Build task definition object $taskDef = [PSCustomObject]@{ Name = $metadata["TaskName"] Version = $metadata["Version"] Description = $description Author = $metadata["Author"] Synopsis = $synopsis Parameters = $parameters Tags = if ($metadata["Tags"]) { $metadata["Tags"] -split ",\s*" } else { @() } RequiresElevation = [bool]($metadata["RequiresElevation"] -eq "True") SupportedOS = if ($metadata["SupportedOS"]) { $metadata["SupportedOS"] -split ",\s*" } else { @() } PSEdition = if ($metadata["PSEdition"]) { $metadata["PSEdition"] -split ",\s*" } else { @() } MinPSVersion = $metadata["MinPSVersion"] Timeout = if ($metadata["Timeout"]) { [int]$metadata["Timeout"] } else { 60 } } return $taskDef } } throw "Unable to parse task metadata from script. Ensure .NOTES section contains TaskName." } function Initialize-TaskCache { <# .SYNOPSIS Initializes the task definition cache. #> [CmdletBinding()] param() Write-Verbose "Initializing task cache..." $config = Get-TbConfig -Section Tasks $script:TaskCache = @{} # Define search paths in resolution order (reverse, so later entries override earlier) $searchPaths = @( @{ Path = $config.BuiltInTaskPath; Source = "BuiltIn" } @{ Path = $config.SystemTaskPath; Source = "System" } @{ Path = $config.UserTaskPath; Source = "User" } ) foreach ($pathInfo in $searchPaths) { if (-not (Test-Path $pathInfo.Path)) { Write-Verbose "Task path not found: $($pathInfo.Path)" continue } # Find all .ps1 files in category subdirectories $taskFiles = Get-ChildItem -Path $pathInfo.Path -Filter "*.ps1" -Recurse -File foreach ($taskFile in $taskFiles) { try { # Parse metadata from comment-based help and .NOTES section $taskDef = Get-TaskMetadataFromScript -ScriptPath $taskFile.FullName # Validate task definition if (-not $taskDef.Name) { Write-Warning "Task definition missing TaskName in .NOTES section: $($taskFile.FullName)" continue } # Add metadata $taskDef | Add-Member -NotePropertyName "FullScriptPath" -NotePropertyValue $taskFile.FullName -Force $taskDef | Add-Member -NotePropertyName "Source" -NotePropertyValue $pathInfo.Source -Force # Add or update in cache (later sources override earlier ones) $script:TaskCache[$taskDef.Name] = $taskDef Write-Verbose "Loaded task: $($taskDef.Name) from $($pathInfo.Source)" } catch { Write-Warning "Failed to load task from $($taskFile.FullName): $_" } } } Write-Verbose "Task cache initialized with $($script:TaskCache.Count) task(s)" } function Test-TaskCompatibility { <# .SYNOPSIS Tests if a task is compatible with the current environment. #> [CmdletBinding()] param( [Parameter(Mandatory)] [PSCustomObject]$TaskDefinition ) $issues = @() # Check PowerShell version if ($TaskDefinition.MinPSVersion) { $minVersion = [version]$TaskDefinition.MinPSVersion if ($PSVersionTable.PSVersion -lt $minVersion) { $issues += "Requires PowerShell $minVersion or higher (current: $($PSVersionTable.PSVersion))" } } # Check PowerShell edition if ($TaskDefinition.PSEdition) { $currentEdition = $PSVersionTable.PSEdition if ($currentEdition -notin $TaskDefinition.PSEdition) { $issues += "Requires PowerShell edition: $($TaskDefinition.PSEdition -join " or ") (current: $currentEdition)" } } # Check OS compatibility if ($TaskDefinition.SupportedOS) { $currentOS = if ($IsWindows -or $PSVersionTable.PSVersion.Major -le 5) { "Windows" } elseif ($IsLinux) { "Linux" } elseif ($IsMacOS) { "MacOS" } else { "Unknown" } if ($currentOS -notin $TaskDefinition.SupportedOS) { $issues += "Not supported on $currentOS (supported: $($TaskDefinition.SupportedOS -join ", "))" } } if ($issues.Count -gt 0) { Write-Warning "Task "$($TaskDefinition.Name)" compatibility issues:" $issues | ForEach-Object { Write-Warning " - $_" } return $false } return $true } |