diff --git a/docs/troubleshooting/_category_.json b/docs/troubleshooting/_category_.json new file mode 100644 index 0000000..cbabb26 --- /dev/null +++ b/docs/troubleshooting/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Troubleshooting", + "position": 7, + "link": { + "type": "generated-index", + "description": "Find solutions to common psake problems, get answers to frequently asked questions, and learn debugging techniques." + } +} diff --git a/docs/troubleshooting/common-errors.md b/docs/troubleshooting/common-errors.md new file mode 100644 index 0000000..9c4091a --- /dev/null +++ b/docs/troubleshooting/common-errors.md @@ -0,0 +1,504 @@ +--- +title: "Common Errors" +description: "Solutions to the most frequently encountered psake errors including task not found, module loading issues, path resolution, and dependency problems" +--- + +# Common Errors + +This guide covers the most common errors you'll encounter when using psake and provides step-by-step solutions to resolve them. + +## Task Not Found Errors + +### Error: "Task [TaskName] does not exist" + +**Problem:** psake cannot find the task you're trying to execute. + +**Common Causes:** +- Typo in the task name +- Task is defined in a different build file +- Task name is case-sensitive in some scenarios + +**Solution:** + +```powershell +# List all available tasks +Invoke-psake -docs + +# Or get detailed task information +Invoke-psake -buildFile .\psakefile.ps1 -docs + +# Verify task name spelling (case matters) +Task Build { # This is "Build" + # ... +} + +# This will fail: +Invoke-psake -taskList build # Wrong case +``` + +**Best Practice:** + +Always use the exact task name as defined: + +```powershell +# Define task +Task CompileApp { + # Task implementation +} + +# Correct invocation +Invoke-psake -taskList CompileApp +``` + +### Error: "No default task specified" + +**Problem:** You didn't specify which task to run and there's no default task defined. + +**Solution:** + +Define a default task in your psakefile.ps1: + +```powershell +# Option 1: Use the Task Default +Task Default -depends Build, Test + +# Option 2: Specify the task when invoking +Invoke-psake -taskList Build + +# Option 3: Set default in properties +Properties { + $DefaultTask = 'Build' +} +``` + +## Module Loading Issues + +### Error: "The specified module 'psake' was not loaded" + +**Problem:** PowerShell cannot find the psake module. + +**Solution:** + +```powershell +# Check if psake is installed +Get-Module -ListAvailable psake + +# If not installed, install it +Install-Module -Name psake -Scope CurrentUser -Force + +# If installed but not loading, import explicitly +Import-Module psake -Force + +# Check the module path +$env:PSModulePath -split [System.IO.Path]::PathSeparator +``` + +### Error: "Assembly with same name is already loaded" + +**Problem:** psake module or dependencies are already loaded in the session with different versions. + +**Solution:** + +```powershell +# Start a fresh PowerShell session +pwsh + +# Or force reload in a new scope +& { + Import-Module psake -Force + Invoke-psake -buildFile .\psakefile.ps1 +} + +# For persistent issues, remove and reinstall +Remove-Module psake -Force -ErrorAction SilentlyContinue +Uninstall-Module psake -AllVersions +Install-Module psake -Scope CurrentUser -Force +``` + +### Error: "Could not load file or assembly" + +**Problem:** psake or a dependency has a corrupted installation or version mismatch. + +**Solution:** + +```powershell +# Reinstall psake cleanly +Uninstall-Module psake -AllVersions -Force +Remove-Item "$env:LOCALAPPDATA\Microsoft\Windows\PowerShell\PowerShellGet\NuGetPackages\psake*" -Recurse -Force -ErrorAction SilentlyContinue + +# Clear the module cache +Remove-Item "$env:LOCALAPPDATA\Microsoft\Windows\PowerShell\ModuleAnalysisCache" -Force -ErrorAction SilentlyContinue + +# Install fresh +Install-Module psake -Scope CurrentUser -Force + +# Verify installation +Get-Module psake -ListAvailable +``` + +## Path Resolution Problems + +### Error: "Cannot find path" or "Path does not exist" + +**Problem:** psake tasks reference files or directories that don't exist or use incorrect path formats. + +**Solution:** + +```powershell +# Use absolute paths or paths relative to $PSScriptRoot +Properties { + # Bad: Assumes current directory + $BuildDir = '.\build' + + # Good: Relative to script location + $BuildDir = Join-Path $PSScriptRoot 'build' + + # Also good: Use Resolve-Path for existing paths + $SrcDir = Resolve-Path (Join-Path $PSScriptRoot 'src') +} + +Task Build { + # Verify path exists before using + if (-not (Test-Path $BuildDir)) { + New-Item -ItemType Directory -Path $BuildDir -Force | Out-Null + } + + # Use cross-platform path handling + $outputPath = Join-Path $BuildDir 'output' +} +``` + +### Error: Cross-Platform Path Issues + +**Problem:** Build scripts fail on Linux/macOS due to Windows-style paths. + +**Solution:** + +```powershell +Properties { + # Bad: Windows-only backslashes + $BuildDir = "$PSScriptRoot\build\output" + + # Good: Cross-platform path construction + $BuildDir = Join-Path $PSScriptRoot 'build' | Join-Path -ChildPath 'output' + + # Or use forward slashes (works on all platforms) + $BuildDir = "$PSScriptRoot/build/output" + + # Best: Use Path cmdlets + $BuildDir = [System.IO.Path]::Combine($PSScriptRoot, 'build', 'output') +} + +Task Clean { + # Use platform-agnostic commands + if (Test-Path $BuildDir) { + Remove-Item $BuildDir -Recurse -Force + } +} +``` + +### Error: "Access to the path is denied" + +**Problem:** Insufficient permissions or file locks prevent psake from accessing files. + +**Solution:** + +```powershell +Task Clean { + # Handle locked files gracefully + try { + if (Test-Path $BuildDir) { + # Wait and retry if files are locked + $retries = 3 + $delay = 1 + + for ($i = 0; $i -lt $retries; $i++) { + try { + Remove-Item $BuildDir -Recurse -Force -ErrorAction Stop + break + } catch { + if ($i -eq ($retries - 1)) { throw } + Write-Host "Retrying in $delay seconds..." + Start-Sleep -Seconds $delay + $delay *= 2 + } + } + } + } catch { + Write-Warning "Could not remove $BuildDir : $_" + # Optionally continue or fail + # throw + } +} + +# Or run with elevated permissions (Windows) +# Start-Process pwsh -Verb RunAs -ArgumentList "-Command Invoke-psake" +``` + +## PowerShell Version Conflicts + +### Error: "Parameter set cannot be resolved" + +**Problem:** Using syntax or cmdlets not available in the current PowerShell version. + +**Solution:** + +```powershell +# Check PowerShell version in your build script +Properties { + $PSVersion = $PSVersionTable.PSVersion +} + +Task Init { + Write-Host "PowerShell Version: $($PSVersion.ToString())" + + # Require minimum version + if ($PSVersion.Major -lt 5) { + throw "This build requires PowerShell 5.0 or higher. Current version: $($PSVersion.ToString())" + } +} + +# Use version-specific code +Task Build { + if ($PSVersion.Major -ge 7) { + # PowerShell 7+ features + $items = Get-ChildItem -Recurse -File -ErrorAction SilentlyContinue + } else { + # PowerShell 5.x compatible + $items = Get-ChildItem -Recurse | Where-Object { -not $_.PSIsContainer } + } +} +``` + +### Error: "The term 'pwsh' is not recognized" + +**Problem:** Trying to use PowerShell 7+ (pwsh) when only Windows PowerShell (powershell) is installed. + +**Solution:** + +```powershell +# Check which PowerShell is available +if (Get-Command pwsh -ErrorAction SilentlyContinue) { + # PowerShell 7+ is available + $psExe = 'pwsh' +} else { + # Fall back to Windows PowerShell + $psExe = 'powershell' +} + +Task RunInNewSession { + exec { & $psExe -NoProfile -Command "Write-Host 'Running in new session'" } +} +``` + +### Error: ExecutionPolicy Restrictions (Windows PowerShell) + +**Problem:** Script execution is blocked by PowerShell execution policy. + +**Solution:** + +```powershell +# Check current execution policy +Get-ExecutionPolicy -List + +# Set execution policy for current user (doesn't require admin) +Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +# Or bypass for a single session +powershell -ExecutionPolicy Bypass -File .\build.ps1 + +# Or use PowerShell 7+ which has a more permissive default +pwsh -File .\build.ps1 + +# In CI/CD, use pwsh which doesn't have this restriction +# GitHub Actions example: +# - shell: pwsh +# run: Invoke-psake +``` + +## Dependency Cycle Errors + +### Error: "Circular dependency detected" + +**Problem:** Tasks depend on each other in a circular manner. + +**Solution:** + +```powershell +# Bad: Circular dependency +Task A -depends B { + # Task A implementation +} + +Task B -depends A { # Error: B depends on A, which depends on B + # Task B implementation +} + +# Good: Restructure dependencies +Task A -depends Common { + # Task A specific code +} + +Task B -depends Common { + # Task B specific code +} + +Task Common { + # Shared functionality +} +``` + +### Error: "Maximum dependency depth exceeded" + +**Problem:** Too many levels of task dependencies. + +**Solution:** + +```powershell +# Visualize task dependencies +Invoke-psake -buildFile .\psakefile.ps1 -docs -detaileddocs + +# Simplify dependency chain +# Bad: Deep nesting +Task Deploy -depends Package +Task Package -depends Test +Task Test -depends Build +Task Build -depends Restore +Task Restore -depends Clean +Task Clean -depends Init + +# Better: Flatten where possible +Task Deploy -depends Package +Task Package -depends Test +Task Test -depends Build + +Task Build -depends Clean, Restore { + # Build implementation +} + +Task Clean { } +Task Restore { } +``` + +## Script Errors and Failures + +### Error: "Execution of [TaskName] was aborted" + +**Problem:** A task or its dependency failed, stopping execution. + +**Solution:** + +```powershell +# View detailed error information +Invoke-psake -buildFile .\psakefile.ps1 -taskList Build -Verbose + +# Use exec to ensure external commands fail the build +Task Build { + # Bad: Error might be ignored + dotnet build + + # Good: Use exec to capture exit codes + exec { dotnet build } +} + +# Add error handling for specific scenarios +Task Build { + try { + exec { dotnet build -c Release } + } catch { + Write-Host "Build failed: $_" -ForegroundColor Red + Write-Host "Stack trace: $($_.ScriptStackTrace)" + throw + } +} +``` + +### Error: "Cannot bind argument to parameter 'Value'" + +**Problem:** Passing incorrect parameter types to psake or tasks. + +**Solution:** + +```powershell +# Ensure parameters are properly defined +Properties { + $Configuration = 'Debug' + $BuildNumber = 0 + $EnableTests = $true +} + +# Pass parameters correctly +Invoke-psake -buildFile .\psakefile.ps1 ` + -parameters @{ + Configuration = 'Release' + BuildNumber = 42 + EnableTests = $false + } + +# Validate parameter types in tasks +Task Build { + if ($BuildNumber -isnot [int]) { + throw "BuildNumber must be an integer, got: $($BuildNumber.GetType().Name)" + } +} +``` + +## Build File Loading Issues + +### Error: "Could not find build file" + +**Problem:** psake cannot locate the specified build file. + +**Solution:** + +```powershell +# Specify full path to build file +$buildFile = Join-Path $PSScriptRoot 'psakefile.ps1' +Invoke-psake -buildFile $buildFile + +# Or navigate to the directory first +Set-Location $PSScriptRoot +Invoke-psake # Will find psakefile.ps1 in current directory + +# Verify the file exists +if (-not (Test-Path $buildFile)) { + throw "Build file not found: $buildFile" +} +``` + +### Error: Syntax errors in build file + +**Problem:** The psakefile.ps1 has PowerShell syntax errors. + +**Solution:** + +```powershell +# Validate syntax before running +$buildFile = '.\psakefile.ps1' +$errors = $null +$null = [System.Management.Automation.PSParser]::Tokenize( + (Get-Content $buildFile -Raw), + [ref]$errors +) + +if ($errors) { + Write-Error "Syntax errors in build file:" + $errors | ForEach-Object { + Write-Error "$($_.Message) at line $($_.Token.StartLine)" + } +} else { + Invoke-psake -buildFile $buildFile +} + +# Or use PSScriptAnalyzer for better validation +Install-Module PSScriptAnalyzer -Scope CurrentUser +Invoke-ScriptAnalyzer -Path .\psakefile.ps1 +``` + +## See Also + +- [Debugging Guide](/docs/troubleshooting/debugging-guide) - Comprehensive debugging techniques +- [FAQ](/docs/troubleshooting/faq) - Frequently asked questions +- [How to Fail a Build](/docs/tutorial-basics/how-to-fail-a-build) - Controlling build failures +- [Logging Errors](/docs/tutorial-advanced/logging-errors) - Error logging strategies +- [Build Script Resilience](/docs/tutorial-advanced/build-script-resilience) - Writing robust builds diff --git a/docs/troubleshooting/debugging-guide.md b/docs/troubleshooting/debugging-guide.md new file mode 100644 index 0000000..0f09334 --- /dev/null +++ b/docs/troubleshooting/debugging-guide.md @@ -0,0 +1,789 @@ +--- +title: "Debugging Guide" +description: "Complete guide to debugging psake builds using PowerShell debugger, verbose output, logging strategies, and task execution analysis" +--- + +# Debugging Guide + +This comprehensive guide covers debugging techniques for psake build scripts, from basic output inspection to advanced PowerShell debugging. + +## Quick Debugging Checklist + +When your build fails or behaves unexpectedly: + +1. **Enable verbose output:** `Invoke-psake -Verbose` +2. **Check task execution order:** `Invoke-psake -docs -detaileddocs` +3. **Verify properties:** Add `Write-Host` statements in your Properties block +4. **Test individual tasks:** Run tasks one at a time +5. **Check external command output:** Use `exec` with verbose flag +6. **Use the PowerShell debugger:** Set breakpoints for complex issues + +## Verbose Output + +### Basic Verbose Mode + +The simplest debugging approach is enabling verbose output: + +```powershell +# Enable verbose output +Invoke-psake -buildFile .\psakefile.ps1 -Verbose + +# Combine with specific task +Invoke-psake -taskList Build -Verbose + +# Also enable debug output +Invoke-psake -Verbose -Debug +``` + +This shows: +- Task execution order +- Dependency resolution +- Property values +- Detailed execution flow + +### Adding Custom Verbose Messages + +```powershell +Properties { + $Configuration = 'Release' + $BuildDir = Join-Path $PSScriptRoot 'build' + + Write-Verbose "Properties initialized:" + Write-Verbose " Configuration: $Configuration" + Write-Verbose " BuildDir: $BuildDir" +} + +Task Build { + Write-Verbose "Starting build process" + Write-Verbose "Current directory: $PWD" + + exec { dotnet build } -Verbose + + Write-Verbose "Build completed successfully" +} +``` + +### Controlling Output Levels + +```powershell +Properties { + $VerbosePreference = 'Continue' # Always show verbose + # Or: 'SilentlyContinue' to hide verbose +} + +Task Build { + # This will show even without -Verbose flag + Write-Verbose "This is verbose output" + + # Different output levels + Write-Debug "Debug level information" + Write-Information "Informational message" + Write-Warning "Warning message" + Write-Host "Always visible output" +} +``` + +## Using PowerShell Debugger + +### Setting Breakpoints + +**Method 1: Line breakpoints** + +```powershell +# Before running psake +Set-PSBreakpoint -Script .\psakefile.ps1 -Line 25 + +# Run psake (will pause at line 25) +Invoke-psake + +# At the breakpoint: +# - Inspect variables: $Configuration, $BuildDir, etc. +# - Step through: s (step into), v (step over), o (step out) +# - Continue: c +# - Quit debugging: q +``` + +**Method 2: Function/Task breakpoints** + +```powershell +# Break when a specific task runs +Set-PSBreakpoint -Script .\psakefile.ps1 -Command Build + +# Run psake +Invoke-psake -taskList Build +``` + +**Method 3: Variable breakpoints** + +```powershell +# Break when a variable is read or written +Set-PSBreakpoint -Variable Configuration -Mode ReadWrite -Script .\psakefile.ps1 + +Invoke-psake +``` + +**Method 4: Inline breakpoint** + +Add directly in your psakefile.ps1: + +```powershell +Task Build { + # Execution will pause here + Wait-Debugger + + # Or use the older method + Set-PSBreakpoint -Script $PSCommandPath -Line $MyInvocation.ScriptLineNumber + + exec { dotnet build } +} +``` + +### Debugger Commands + +When paused at a breakpoint: + +```powershell +# Inspect variables +$Configuration +$BuildDir +Get-Variable + +# Evaluate expressions +Test-Path $BuildDir +$PSScriptRoot + +# List local variables +Get-Variable -Scope Local + +# View call stack +Get-PSCallStack + +# Step commands +s # Step into +v # Step over (or 'o' in older versions) +o # Step out +c # Continue execution +q # Quit debugger + +# List breakpoints +Get-PSBreakpoint + +# Remove breakpoints +Get-PSBreakpoint | Remove-PSBreakpoint +``` + +### Debugging in VS Code + +If using Visual Studio Code with PowerShell extension: + +1. Open your psakefile.ps1 in VS Code +2. Click in the gutter to set breakpoints (red dots) +3. Press F5 or use "Run and Debug" +4. Add launch configuration in `.vscode/launch.json`: + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug psake Build", + "type": "PowerShell", + "request": "launch", + "script": "${workspaceFolder}/psakefile.ps1", + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} +``` + +Or debug the Invoke-psake call: + +```json +{ + "name": "Debug psake", + "type": "PowerShell", + "request": "launch", + "script": "Invoke-psake", + "args": [ + "-buildFile", "${workspaceFolder}/psakefile.ps1", + "-taskList", "Build" + ] +} +``` + +## Inspecting Task Execution + +### View Task Documentation + +```powershell +# List all tasks with descriptions +Invoke-psake -docs + +# Detailed documentation with dependencies +Invoke-psake -detaileddocs +``` + +**Example output:** + +``` +Name Depends On +---- ---------- +Build Clean, Restore +Test Build +Package Test +Deploy Package +``` + +### Understanding Task Execution Order + +```powershell +# Add task execution logging +FormatTaskName { + param($taskName) + + Write-Host "======================================" -ForegroundColor Cyan + Write-Host "Executing Task: $taskName" -ForegroundColor Cyan + Write-Host "======================================" -ForegroundColor Cyan +} + +Task Build -depends Clean, Restore { + Write-Host "Build task running" +} +``` + +### Tracing Task Dependencies + +```powershell +# Create a helper to trace execution +Properties { + $script:ExecutionTrace = @() +} + +FormatTaskName { + param($taskName) + + $script:ExecutionTrace += @{ + Task = $taskName + Timestamp = Get-Date + } + + Write-Host "[$(Get-Date -Format 'HH:mm:ss')] Starting: $taskName" -ForegroundColor Green +} + +Task Build -depends Test { + # Build logic +} + +Task Cleanup { + # Show execution trace at the end + Write-Host "`nExecution Trace:" -ForegroundColor Yellow + $script:ExecutionTrace | ForEach-Object { + Write-Host " $($_.Timestamp.ToString('HH:mm:ss.fff')) - $($_.Task)" + } +} +``` + +### Testing Task Dependencies Individually + +```powershell +# Test each task independently +Invoke-psake -taskList Clean +Invoke-psake -taskList Restore +Invoke-psake -taskList Build + +# This helps identify which task is failing +``` + +## Logging Strategies + +### Basic File Logging + +```powershell +Properties { + $LogFile = Join-Path $PSScriptRoot 'build.log' + + # Initialize log file + "Build started at $(Get-Date)" | Set-Content $LogFile +} + +function Write-BuildLog { + param([string]$Message) + + $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' + $logMessage = "[$timestamp] $Message" + + # Write to console and file + Write-Host $logMessage + $logMessage | Add-Content $script:LogFile +} + +Task Build { + Write-BuildLog "Starting build for configuration: $Configuration" + + try { + exec { dotnet build } + Write-BuildLog "Build succeeded" + } catch { + Write-BuildLog "Build failed: $_" + throw + } +} +``` + +### Structured Logging + +```powershell +Properties { + $LogFile = Join-Path $PSScriptRoot 'build.json' + $script:BuildLog = @{ + StartTime = Get-Date + Tasks = @() + Properties = @{ + Configuration = $Configuration + BuildNumber = $BuildNumber + } + } +} + +function Add-TaskLog { + param( + [string]$TaskName, + [string]$Status, + [string]$Message = '', + [timespan]$Duration + ) + + $script:BuildLog.Tasks += @{ + Task = $TaskName + Status = $Status + Message = $Message + Duration = $Duration.TotalSeconds + Timestamp = Get-Date + } +} + +FormatTaskName { + param($taskName) + + $script:CurrentTaskStart = Get-Date + $script:CurrentTaskName = $taskName +} + +Task Build { + $taskStart = Get-Date + + try { + exec { dotnet build } + $duration = (Get-Date) - $taskStart + Add-TaskLog -TaskName 'Build' -Status 'Success' -Duration $duration + } catch { + $duration = (Get-Date) - $taskStart + Add-TaskLog -TaskName 'Build' -Status 'Failed' -Message $_.Exception.Message -Duration $duration + throw + } +} + +Task Cleanup { + # Save log to file + $script:BuildLog.EndTime = Get-Date + $script:BuildLog.Duration = ($script:BuildLog.EndTime - $script:BuildLog.StartTime).TotalSeconds + + $script:BuildLog | ConvertTo-Json -Depth 10 | Set-Content $LogFile + Write-Host "Build log saved to: $LogFile" +} +``` + +### Logging External Command Output + +```powershell +Task Build { + $logFile = Join-Path $PSScriptRoot 'build-output.log' + + # Capture and log command output + $output = exec { dotnet build } -PassThru + $output | Set-Content $logFile + + # Or redirect output + exec { dotnet build 2>&1 | Tee-Object -FilePath $logFile } +} +``` + +### Integration with Logging Frameworks + +```powershell +# Using PSFramework (install: Install-Module PSFramework) +Import-Module PSFramework + +Properties { + # Configure logging + Set-PSFLoggingProvider -Name logfile -FilePath './logs/build.log' -Enabled $true +} + +Task Build { + Write-PSFMessage -Level Important "Starting build" + + try { + exec { dotnet build } + Write-PSFMessage -Level Significant "Build completed successfully" + } catch { + Write-PSFMessage -Level Error "Build failed" -ErrorRecord $_ + throw + } +} +``` + +## Debugging External Commands + +### Using exec with Verbose Output + +```powershell +Task Build { + # Show command being executed + exec { dotnet build } -Verbose + + # Or manually log it + $cmd = 'dotnet build' + Write-Host "Executing: $cmd" -ForegroundColor Yellow + exec { & $cmd } +} +``` + +### Capturing Exit Codes + +```powershell +Task Build { + # exec will throw on non-zero exit code + try { + exec { dotnet build } + } catch { + Write-Host "Command failed with exit code: $LASTEXITCODE" + Write-Host "Error: $_" + throw + } +} +``` + +### Debugging Command Failures + +```powershell +Task Build { + # Run command with error handling + $ErrorActionPreference = 'Continue' + + $output = dotnet build 2>&1 + + if ($LASTEXITCODE -ne 0) { + Write-Host "Build failed with exit code: $LASTEXITCODE" -ForegroundColor Red + Write-Host "Output:" -ForegroundColor Red + $output | ForEach-Object { Write-Host $_ -ForegroundColor Red } + + # Optionally save to file for analysis + $output | Set-Content './build-error.log' + + throw "Build failed. See build-error.log for details." + } +} +``` + +### Testing Commands Interactively + +```powershell +Task Build { + # Use -WhatIf pattern for dry runs + if ($WhatIfPreference) { + Write-Host "Would execute: dotnet build -c $Configuration" + return + } + + exec { dotnet build -c $Configuration } +} + +# Invoke with: +# Invoke-psake -WhatIf +``` + +## Common Debugging Scenarios + +### Scenario 1: Task Not Running as Expected + +**Problem:** A task doesn't execute or executes in wrong order. + +**Debug steps:** + +```powershell +# 1. Check if task exists +Invoke-psake -docs + +# 2. View task dependencies +Invoke-psake -detaileddocs + +# 3. Add execution tracing +FormatTaskName { + param($taskName) + Write-Host ">>> Executing: $taskName at $(Get-Date -Format 'HH:mm:ss.fff')" +} + +# 4. Check preconditions +Task Build -precondition { $Configuration -eq 'Release' } { + Write-Host "Precondition passed: Configuration = $Configuration" + # Task logic +} +``` + +### Scenario 2: Properties Have Unexpected Values + +**Problem:** Properties don't have the values you expect. + +**Debug steps:** + +```powershell +Properties { + $Configuration = 'Debug' + $BuildNumber = $env:BUILD_NUMBER ?? '0' + + # Debug output + Write-Host "=== Property Values ===" -ForegroundColor Cyan + Get-Variable -Scope Script | Where-Object { + $_.Name -in @('Configuration', 'BuildNumber') + } | ForEach-Object { + Write-Host "$($_.Name) = $($_.Value)" + } +} + +# Or create a debug task +Task ShowProperties { + Write-Host "Configuration: $Configuration" + Write-Host "BuildNumber: $BuildNumber" + Write-Host "PSScriptRoot: $PSScriptRoot" + + # Show all properties + Get-Variable -Scope Script | Format-Table Name, Value +} + +# Run: Invoke-psake -taskList ShowProperties +``` + +### Scenario 3: Path Issues + +**Problem:** Files or directories not found. + +**Debug steps:** + +```powershell +Task Build { + # Debug paths + Write-Host "Current directory: $PWD" + Write-Host "Script root: $PSScriptRoot" + Write-Host "Build directory: $BuildDir" + + # Verify paths exist + @($BuildDir, $SrcDir, $TestDir) | ForEach-Object { + if (Test-Path $_) { + Write-Host "✓ Found: $_" -ForegroundColor Green + } else { + Write-Host "✗ Missing: $_" -ForegroundColor Red + } + } + + # List contents + Write-Host "`nContents of $SrcDir:" + Get-ChildItem $SrcDir | ForEach-Object { + Write-Host " $($_.Name)" + } +} +``` + +### Scenario 4: Dependency Cycle + +**Problem:** Circular dependency error. + +**Debug steps:** + +```powershell +# Use detailed docs to visualize dependencies +Invoke-psake -detaileddocs + +# Create a dependency graph +Task ShowDependencies { + $tasks = @{ + Build = @('Clean', 'Restore') + Test = @('Build') + Package = @('Test') + Deploy = @('Package') + } + + Write-Host "Task Dependency Graph:" + $tasks.GetEnumerator() | ForEach-Object { + Write-Host "$($_.Key) -> $($_.Value -join ', ')" + } +} +``` + +## Performance Debugging + +### Measuring Task Execution Time + +```powershell +Properties { + $script:TaskTimings = @{} +} + +FormatTaskName { + param($taskName) + + $script:CurrentTask = $taskName + $script:TaskTimings[$taskName] = @{ + Start = Get-Date + } + + Write-Host "[$([Math]::Round(((Get-Date) - $script:BuildStart).TotalSeconds, 2))s] Starting: $taskName" +} + +# Override TaskTearDown to capture end time +TaskTearDown { + $end = Get-Date + $start = $script:TaskTimings[$script:CurrentTask].Start + $duration = ($end - $start).TotalSeconds + + $script:TaskTimings[$script:CurrentTask].Duration = $duration + + Write-Host "[$([Math]::Round(((Get-Date) - $script:BuildStart).TotalSeconds, 2))s] Completed: $script:CurrentTask (took $([Math]::Round($duration, 2))s)" +} + +Properties { + $script:BuildStart = Get-Date +} + +Task Summary { + Write-Host "`n=== Task Performance ===" -ForegroundColor Yellow + $script:TaskTimings.GetEnumerator() | Sort-Object { $_.Value.Duration } -Descending | ForEach-Object { + $duration = [Math]::Round($_.Value.Duration, 2) + Write-Host "$($_.Key): ${duration}s" + } + + $total = [Math]::Round(((Get-Date) - $script:BuildStart).TotalSeconds, 2) + Write-Host "`nTotal build time: ${total}s" -ForegroundColor Green +} +``` + +### Identifying Slow Operations + +```powershell +function Measure-BuildStep { + param( + [string]$Name, + [scriptblock]$ScriptBlock + ) + + Write-Host "Starting: $Name..." -NoNewline + $start = Get-Date + + try { + & $ScriptBlock + $duration = ((Get-Date) - $start).TotalSeconds + Write-Host " completed in $([Math]::Round($duration, 2))s" -ForegroundColor Green + } catch { + $duration = ((Get-Date) - $start).TotalSeconds + Write-Host " failed after $([Math]::Round($duration, 2))s" -ForegroundColor Red + throw + } +} + +Task Build { + Measure-BuildStep "Restore packages" { + exec { dotnet restore } + } + + Measure-BuildStep "Compile code" { + exec { dotnet build --no-restore } + } + + Measure-BuildStep "Run tests" { + exec { dotnet test --no-build } + } +} +``` + +## Advanced Debugging Techniques + +### Remote Debugging + +```powershell +# On remote machine, enable PowerShell remoting +Enable-PSRemoting -Force + +# From local machine +$session = New-PSSession -ComputerName remote-server + +# Run psake remotely with debugging +Invoke-Command -Session $session -ScriptBlock { + Import-Module psake + Set-PSBreakpoint -Script C:\builds\psakefile.ps1 -Line 50 + Invoke-psake -buildFile C:\builds\psakefile.ps1 +} + +# Enter interactive session for debugging +Enter-PSSession $session +``` + +### Debugging in CI/CD + +```powershell +# Add CI-specific debugging +Properties { + $IsCI = $env:CI -eq 'true' +} + +Task Build { + if ($IsCI) { + # More verbose output in CI + $VerbosePreference = 'Continue' + $DebugPreference = 'Continue' + + # Dump environment + Write-Host "=== Environment Variables ===" + Get-ChildItem env: | Sort-Object Name | ForEach-Object { + Write-Host "$($_.Name) = $($_.Value)" + } + } + + exec { dotnet build } -Verbose:$IsCI +} +``` + +### Creating Debug Snapshots + +```powershell +Task CreateDebugSnapshot { + $snapshotDir = Join-Path $PSScriptRoot 'debug-snapshot' + New-Item -ItemType Directory -Path $snapshotDir -Force | Out-Null + + # Capture environment state + @{ + PowerShellVersion = $PSVersionTable + EnvironmentVariables = Get-ChildItem env: | ForEach-Object { @{$_.Name = $_.Value} } + InstalledModules = Get-Module -ListAvailable | Select-Object Name, Version + WorkingDirectory = $PWD + Properties = @{ + Configuration = $Configuration + BuildDir = $BuildDir + } + Timestamp = Get-Date + } | ConvertTo-Json -Depth 10 | Set-Content "$snapshotDir/snapshot.json" + + # Copy relevant files + Copy-Item .\psakefile.ps1 $snapshotDir + Copy-Item .\*.log $snapshotDir -ErrorAction SilentlyContinue + + Write-Host "Debug snapshot saved to: $snapshotDir" +} +``` + +## See Also + +- [Common Errors](/docs/troubleshooting/common-errors) - Solutions to frequent errors +- [FAQ](/docs/troubleshooting/faq) - Frequently asked questions +- [Debug Script](/docs/tutorial-advanced/debug-script) - Basic debugging tutorial +- [Logging Errors](/docs/tutorial-advanced/logging-errors) - Error logging strategies +- [Build Script Resilience](/docs/tutorial-advanced/build-script-resilience) - Writing robust builds diff --git a/docs/troubleshooting/faq.md b/docs/troubleshooting/faq.md new file mode 100644 index 0000000..e4fb98c --- /dev/null +++ b/docs/troubleshooting/faq.md @@ -0,0 +1,554 @@ +--- +title: "Frequently Asked Questions" +description: "Common questions about psake installation, usage, performance, and integration with other tools" +--- + +# Frequently Asked Questions + +Quick answers to common questions about psake. + +## Installation + +### What are the system requirements for psake? + +**psake requires:** +- PowerShell 5.0 or later (Windows PowerShell) +- OR PowerShell 7+ (PowerShell Core) for cross-platform support +- No other external dependencies + +**Recommended:** +- PowerShell 7+ for best cross-platform compatibility +- Latest version of psake from the PowerShell Gallery + +### How do I install psake? + +```powershell +# Install for current user (no admin required) +Install-Module -Name psake -Scope CurrentUser + +# Install globally (requires admin/sudo) +Install-Module -Name psake -Scope AllUsers + +# Install specific version +Install-Module -Name psake -RequiredVersion 4.9.0 +``` + +See the [Installing psake](/docs/tutorial-basics/installing) guide for more details. + +### Can I use psake without installing it? + +Yes, you can use psake without installing it system-wide: + +```powershell +# Option 1: Install to a local directory +Save-Module -Name psake -Path ./modules + +# Then load it +Import-Module ./modules/psake + +# Option 2: Use in a Docker container +docker run -v ${PWD}:/workspace -w /workspace mcr.microsoft.com/powershell:latest pwsh -c "Install-Module psake -Force; Invoke-psake" + +# Option 3: Use PSDepend in your project +# Create requirements.psd1: +@{ + psake = 'latest' +} + +# Install dependencies +Install-Module PSDepend -Scope CurrentUser +Invoke-PSDepend -Path ./requirements.psd1 -Install +``` + +### How do I update psake to the latest version? + +```powershell +# Update to latest version +Update-Module -Name psake + +# Or remove old version and install fresh +Uninstall-Module -Name psake -AllVersions +Install-Module -Name psake +``` + +### Can I run multiple versions of psake? + +Yes, but only one version can be loaded in a PowerShell session at a time: + +```powershell +# Install multiple versions +Install-Module psake -RequiredVersion 4.9.0 +Install-Module psake -RequiredVersion 4.8.0 + +# View installed versions +Get-Module psake -ListAvailable + +# Load specific version +Import-Module psake -RequiredVersion 4.9.0 + +# Or use -MinimumVersion +Import-Module psake -MinimumVersion 4.8 +``` + +## Basic Usage + +### What's the difference between a task, property, and parameter? + +**Task:** A named unit of work in your build script +```powershell +Task Build { + # Work to perform +} +``` + +**Property:** A variable defined in the build script +```powershell +Properties { + $BuildConfiguration = 'Release' +} +``` + +**Parameter:** A value passed from the command line +```powershell +# In psakefile.ps1 +Properties { + $Version = '1.0.0' # Default value +} + +# Invoke with parameter +Invoke-psake -parameters @{ Version = '2.0.0' } +``` + +See [Parameters and Properties](/docs/tutorial-basics/parameters-properties) for details. + +### How do I pass parameters to my psake build? + +```powershell +# Define properties with default values +Properties { + $Configuration = 'Debug' + $OutputPath = './build' +} + +# Pass parameters when invoking +Invoke-psake -parameters @{ + Configuration = 'Release' + OutputPath = 'C:/builds/output' +} + +# Or use environment variables +$env:BUILD_CONFIG = 'Release' + +Properties { + $Configuration = $env:BUILD_CONFIG ?? 'Debug' +} +``` + +### Can I run multiple tasks at once? + +Yes, specify multiple tasks in the taskList: + +```powershell +# Run tasks in order +Invoke-psake -taskList Clean, Build, Test + +# Tasks will execute in the order specified, along with their dependencies +``` + +The tasks will run sequentially in the order you specify, and psake will automatically run any task dependencies. + +### How do I see all available tasks in a build script? + +```powershell +# List all tasks with descriptions +Invoke-psake -docs + +# Detailed documentation including dependencies +Invoke-psake -detaileddocs + +# For a specific build file +Invoke-psake -buildFile ./custom.ps1 -docs +``` + +### What's the difference between `-depends` and calling tasks directly? + +**Using `-depends`:** Declares a dependency relationship. psake ensures dependencies run first and only once: + +```powershell +Task Build -depends Clean, Compile { + # Clean and Compile run first (if not already run) +} + +Task Test -depends Build { + # Build runs first (which also runs Clean and Compile) +} + +# Invoke-psake -taskList Test +# Execution order: Clean → Compile → Build → Test +# Each task runs exactly once +``` + +**Calling tasks directly:** Executes the task every time it's called: + +```powershell +Task Build { + Invoke-Task Clean + Invoke-Task Compile + # These run every time Build is called +} +``` + +**Best Practice:** Use `-depends` for declaring task relationships. + +### Can I use psake with non-.NET projects? + +Absolutely! psake is a general-purpose build tool. It's commonly used for: + +- **Node.js projects** - npm install, webpack builds, testing +- **Python projects** - pip install, pytest, package building +- **Go projects** - go build, go test +- **Docker** - Building and pushing images +- **Documentation** - Static site generators +- **Infrastructure** - Terraform, Ansible automation +- **Any scripting** - File processing, data transformation + +Example for Node.js: + +```powershell +Task Install { + exec { npm install } +} + +Task Build { + exec { npm run build } +} + +Task Test { + exec { npm test } +} +``` + +## Performance + +### Why is my build slow? + +Common causes and solutions: + +**1. Unnecessary dependency resolution** +```powershell +# Slow: Running restore on every build +Task Build { + exec { dotnet restore } + exec { dotnet build } +} + +# Faster: Separate restore task +Task Restore { + exec { dotnet restore } +} + +Task Build -depends Restore { + exec { dotnet build --no-restore } +} +``` + +**2. Not using incremental builds** +```powershell +# Slow: Always clean and rebuild everything +Task Build -depends Clean { + exec { dotnet build } +} + +# Faster: Add an IncrementalBuild task +Task IncrementalBuild { + exec { dotnet build } +} + +Task RebuildAll -depends Clean, Build { + # Full rebuild only when needed +} +``` + +**3. Serial execution of independent tasks** +```powershell +# Slow: Running tests serially +Task Test -depends UnitTest, IntegrationTest + +# Consider: Use parallel execution for independent tasks +Task Test { + # Run tests in parallel using PowerShell jobs or dotnet test + $jobs = @( + Start-Job { dotnet test ./tests/Unit.Tests --no-build } + Start-Job { dotnet test ./tests/Integration.Tests --no-build } + ) + $jobs | Wait-Job | Receive-Job +} +``` + +### How can I speed up my psake builds? + +**1. Use caching in CI/CD** +```yaml +# GitHub Actions example +- uses: actions/cache@v4 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} +``` + +**2. Skip unnecessary tasks** +```powershell +Properties { + $SkipTests = $false +} + +Task Build { + exec { dotnet build } +} + +Task Test -depends Build -precondition { -not $SkipTests } { + exec { dotnet test } +} + +# Invoke-psake -parameters @{ SkipTests = $true } +``` + +**3. Use build output caching** +```powershell +Task Compile { + $needsRebuild = $false + + Get-ChildItem src/*.cs | ForEach-Object { + $source = $_ + $output = Join-Path build "$($_.BaseName).dll" + + if (-not (Test-Path $output) -or $source.LastWriteTime -gt (Get-Item $output).LastWriteTime) { + $needsRebuild = $true + } + } + + if ($needsRebuild) { + exec { dotnet build } + } else { + Write-Host "No rebuild needed" + } +} +``` + +### Does psake support parallel task execution? + +psake executes tasks sequentially to maintain dependency order. However, you can: + +**1. Run independent operations in parallel within a task** +```powershell +Task BuildAll { + # Use PowerShell jobs for parallel execution + $jobs = @( + Start-Job -ScriptBlock { dotnet build ./ProjectA } + Start-Job -ScriptBlock { dotnet build ./ProjectB } + ) + + $jobs | Wait-Job | Receive-Job + $jobs | Remove-Job + + # Or use ForEach-Object -Parallel (PowerShell 7+) + @('ProjectA', 'ProjectB') | ForEach-Object -Parallel { + dotnet build ./$_ + } -ThrottleLimit 4 +} +``` + +**2. Use external tools for parallel builds** +```powershell +Task BuildSolution { + # MSBuild can build projects in parallel + exec { msbuild /m /p:Configuration=Release } + + # Or dotnet build + exec { dotnet build -m } +} +``` + +## Integration + +### Can I use psake with CI/CD systems? + +Yes! psake works with all major CI/CD platforms: + +- **GitHub Actions** - See [GitHub Actions guide](/docs/ci-examples/github-actions) +- **Azure Pipelines** - See [Azure Pipelines guide](/docs/ci-examples/azure-pipelines) +- **GitLab CI** - See [GitLab CI guide](/docs/ci-examples/gitlab-ci) +- **Jenkins, TeamCity, etc.** - Just run PowerShell scripts + +Basic pattern: + +```bash +# Install psake +pwsh -Command "Install-Module psake -Force" + +# Run build +pwsh -Command "Invoke-psake -buildFile ./psakefile.ps1 -taskList Build" +``` + +### How do I integrate psake with MSBuild? + +```powershell +# Call MSBuild from psake +Task Build { + exec { msbuild /t:Build /p:Configuration=Release MySolution.sln } +} + +# Or call psake from MSBuild (in .csproj/.targets) + + + + +# Better: Use psake to orchestrate MSBuild +Properties { + $SolutionFile = 'MySolution.sln' + $Configuration = 'Release' +} + +Task Restore { + exec { msbuild /t:Restore $SolutionFile } +} + +Task Build -depends Restore { + exec { msbuild /t:Build /p:Configuration=$Configuration $SolutionFile } +} + +Task Pack -depends Build { + exec { msbuild /t:Pack /p:Configuration=$Configuration $SolutionFile } +} +``` + +### Can I call psake tasks from another psake script? + +Yes, using several methods: + +**Method 1: Include another build file** +```powershell +# In main psakefile.ps1 +Include .\shared-tasks.ps1 + +Task Build -depends SharedTask { + # SharedTask is from shared-tasks.ps1 +} +``` + +**Method 2: Invoke another build file** +```powershell +Task BuildSubproject { + Invoke-psake -buildFile ./subproject/psakefile.ps1 -taskList Build +} +``` + +**Method 3: Nested build (legacy)** +```powershell +Task BuildAll { + Invoke-psake ./project1/psakefile.ps1 -taskList Build + Invoke-psake ./project2/psakefile.ps1 -taskList Build +} +``` + +See [Nested Builds](/docs/tutorial-basics/nested-build) for details. + +### How do I use psake with Docker? + +```dockerfile +# Dockerfile +FROM mcr.microsoft.com/powershell:latest + +WORKDIR /build + +# Install psake +RUN pwsh -Command "Install-Module -Name psake -Scope CurrentUser -Force" + +# Copy build files +COPY . . + +# Run build +CMD ["pwsh", "-Command", "Invoke-psake -taskList Build"] +``` + +Or in your psakefile.ps1: + +```powershell +Task BuildDockerImage { + exec { docker build -t myapp:latest . } +} + +Task RunInDocker { + exec { docker run --rm -v ${PWD}:/workspace -w /workspace mcr.microsoft.com/powershell pwsh -c "Install-Module psake -Force; Invoke-psake" } +} +``` + +### Can I use psake with Invoke-Build or other build tools? + +Yes, they can coexist: + +```powershell +# Call Invoke-Build from psake +Task RunInvokeBuild { + exec { Invoke-Build -File ./project.build.ps1 } +} + +# Call psake from Invoke-Build +task RunPsake { + exec { Invoke-psake -buildFile ./psakefile.ps1 } +} + +# Use the right tool for each scenario: +# - psake: Simple, declarative builds with task dependencies +# - Invoke-Build: More complex build logic, better for large scripts +``` + +## Troubleshooting + +### How do I debug my build script? + +See the [Debugging Guide](/docs/troubleshooting/debugging-guide) for comprehensive debugging techniques. + +Quick tips: + +```powershell +# Enable verbose output +Invoke-psake -Verbose + +# Add debug output +Task Build { + Write-Host "Configuration: $Configuration" + Write-Host "BuildDir: $BuildDir" + exec { dotnet build } -Verbose +} + +# Use PowerShell debugger +Set-PSBreakpoint -Script .\psakefile.ps1 -Line 42 +Invoke-psake +``` + +### Where can I find more help? + +- **Documentation:** [psake.readthedocs.io](https://psake.readthedocs.io/) +- **GitHub Issues:** [github.com/psake/psake/issues](https://github.com/psake/psake/issues) +- **Source Code:** [github.com/psake/psake](https://github.com/psake/psake) +- **Examples:** Check the `examples/` directory in the psake repository + +### How do I report a bug or request a feature? + +Visit the [psake GitHub repository](https://github.com/psake/psake) and: + +1. Search existing issues to avoid duplicates +2. Create a new issue with: + - Clear description of the problem or feature + - Minimal reproducible example + - Your PowerShell version (`$PSVersionTable`) + - Your psake version (`Get-Module psake | Select-Object Version`) + +## See Also + +- [Common Errors](/docs/troubleshooting/common-errors) - Solutions to frequent problems +- [Debugging Guide](/docs/troubleshooting/debugging-guide) - Debugging techniques +- [Installing psake](/docs/tutorial-basics/installing) - Installation guide +- [Running psake](/docs/tutorial-basics/run-psake) - Basic usage +- [Parameters and Properties](/docs/tutorial-basics/parameters-properties) - Parameter handling diff --git a/sidebars.ts b/sidebars.ts index 9a44c4a..d06f651 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -66,6 +66,15 @@ const sidebars: SidebarsConfig = { label: 'Command Reference', items: commands }, + { + type: 'category', + label: 'Troubleshooting', + items: [ + 'troubleshooting/common-errors', + 'troubleshooting/faq', + 'troubleshooting/debugging-guide', + ] + }, { type: 'doc', label: 'Code of Conduct',