Import-BuildScript.ps1
<# .SYNOPSIS Import the given build scripts into the current runspace .DESCRIPTION This script will import the tasks defined in the module, the users ~/.stitch directory, and the current project. Before importing, the script will check the 'ExcludeTasksOnImport' list, which is a list of regex to block from being imported by this script #> param( [Parameter()] [string[]]$ExcludeScriptsOnImport = ( Get-BuildProperty ExcludeScriptsOnImport @() ), [Parameter( DontShow )] [string]$InternalScriptPath = "$PSScriptRoot\BuildScripts" ) function Merge-BuildScript { <# .SYNOPSIS Add the scripts to the larger collection, replacing items based on BaseName #> param( [Parameter( Position = 1, ValueFromPipeline )] [ref]$Collection, [Parameter( Position = 0 )] [Array]$Scripts ) begin { } process { foreach ($currentScript in $Scripts) { <# if this script's file name exists in the Collection scripts array, we remove it from the and add this script, otherwise just add the script #> $baseNames = $Collection.Value | Select-Object -ExpandProperty BaseName if ($baseNames -contains $currentScript.BaseName ) { $previousScript = $Collection.Value | Where-Object { $_.BaseName -like $currentScript.BaseName } if ($null -ne $previousScript) { Write-Verbose "Overriding $($currentScript.BaseName)" $index = $Collection.Value.IndexOf( $previousScript ) $Collection.Value[$index] = $currentScript } } else { $Collection.Value += $currentScript } } } end { } } if ($null -eq $script:ImportErrors) { $script:ImportErrors = [ordered]@{} } # build scripts in the current project $projectScripts = [System.Collections.ArrayList]@() # build scripts in ~/.stitch $systemScripts = [System.Collections.ArrayList]@() # build scripts bundled with the stitch module $moduleScripts = [System.Collections.ArrayList]@() # Collated build scripts collection $scriptFiles = [System.Collections.ArrayList]@() <#------------------------------------------------------------------ Start with the bundled scripts ------------------------------------------------------------------#> Write-Debug "`n<$('-' * 80)" Write-Debug "Collecting Invoke-Build build scripts:" if ($null -ne $InternalScriptPath) { if (Test-Path $InternalScriptPath) { Write-Debug " - Looking in the module's directory" $moduleScripts = $InternalScriptPath | Find-InvokeBuildScript if ($moduleScripts.Count -gt 0) { Write-Debug " - Merging $($moduleScripts.Count) scripts" [ref]$scriptFiles | Merge-BuildScript $moduleScripts } } } else { Write-Warning "Path to module task path is not set " } <#------------------------------------------------------------------ Layer on the system scripts ------------------------------------------------------------------#> $systemPath = Find-LocalUserStitchDirectory if ($null -ne $systemPath) { Write-Debug " - Looking in the system path $systemPath" $systemScripts = $systemPath | Find-InvokeBuildScript } else { Write-Debug " - Did not find a local user stitch directory" } if ($systemScripts.Count -gt 0) { Write-Debug " - Merging $($systemScripts.Count) scripts" [ref]$scriptFiles | Merge-BuildScript $systemScripts } <#------------------------------------------------------------------ Layer on the project scripts ------------------------------------------------------------------#> #! hopefully, BuildConfigPath is set by .build.ps1 if ($null -ne $BuildConfigPath) { Write-Debug "Looking in $BuildConfigPath" $projectScripts = $BuildConfigPath | Find-InvokeBuildScript } else { Write-Debug "BuildConfigPath is not set. No build scripts loaded from project" } if ($projectScripts.Count -gt 0) { Write-Debug " - Merging $($projectScripts.Count) scripts" [ref]$scriptFiles | Merge-BuildScript $projectScripts } Write-Debug "Merged all build scripts." Write-Debug "`n$('-' * 80)>" <#------------------------------------------------------------------ Now, Process the merged collection ------------------------------------------------------------------#> Write-Debug "`n<$('-' * 80)" Write-Debug "Importing $($scriptFiles.Count) build scripts" :file foreach ($file in $scriptFiles) { #------------------------------------------------------------------------------- #region Exclusions if (($null -ne $ExcludeScriptsOnImport) -and ($ExcludeScriptsOnImport.Count -gt 0)) { :exclude foreach ($exclude in $ExcludeScriptsOnImport) { # the filename matches at least one exclude, no need to keep checking if ($file.BaseName -match $exclude) { Write-Debug "$($file.BaseName) is excluded by pattern $exclude" #! do not import the script, go to the next file in the list continue file } } } #endregion Exclusions #------------------------------------------------------------------------------- try { Write-Debug " - $($file.Name)" . $file.FullName } catch { <# This rather long catch block is collecting the relavant error information, and passing it up to the $ImportErrors script variable. ! this is because the files are imported in the .build.ps1 file, but we want ! to report the errors after the logs have been initialized and the rest of ! the components have a chance to load. ! The errors are reported in Enter-Build #> $importScriptName = Get-Item $PSCommandPath | Select-Object -ExpandProperty Name $message = [System.Text.StringBuilder]::new() $errorException = $_.Exception <# If there were parse errors in the imported script, then there will be an 'Errors' entry for each #> if ($errorException.Errors.Count -gt 0) { # Format the first line of our processed error message if ($errorException.Errors.Count -eq 1) { $null = $message.Append("There was an error trying to import $($file.Name)") } else { $null = $message.Append("There where $($errorException.Errors.Count) errors trying to import $($file.Name)") } $null = $message.AppendLine(": (") # Collect each of the parse errors and format them foreach ($importError in $errorException.Errors) { $null = $message.AppendJoin( '', " - ", $importError.Extent.File, ':', $importError.Extent.StartLineNumber, ':') $null = $message.AppendLine( $importError.Extent.StartColumnNumber) $null = $message.Append( ' - ') $null = $message.AppendLine($importError.Message) } <# If there aren't any Errors entries, then process the error record. #> } elseif ($errorException.ErrorRecord.Count -gt 0) { if ($errorException.ErrorRecord.Count -eq 1) { $null = $message.AppendLine("There was an error trying to import $($file.Name)") } else { $null = $message.AppendLine("There where $($errorException.ErrorRecord.Count) errors trying to import $($file.Name)") } $null = $message.AppendLine(": (") foreach ($importError in $errorException.ErrorRecord) { $null = $message.Append( ' - ') $null = $message.AppendJoin( '', ' - ', $importError.InvocationInfo.ScriptName, ':', $importError.InvocationInfo.ScriptLineNumber, ':') $null = $message.AppendLine( $importError.InvocationInfo.Offset.InLine) $null = $message.AppendLine($importError.Exception.Message) # Add the position message, unless it just points to this script if (-not($importError.InvocationInfo.PositionMessage -match [regex]::Escape($importScriptName))) { $null = $message.AppendLine($importError.InvocationInfo.PositionMessage) } } } else { $null = $message.AppendLine("There was an error trying to import $($file.Name)") $null = $message.AppendLine($_) } Write-Debug "An error occured importing $($file.Name). $($message.ToString())" if ($null -ne $script:ImportErrors) { $script:ImportErrors.Add($file.Name, $message.ToString()) } else { Write-Debug "ImportErrors was not initialized" $message.ToString() } } } Write-Debug "`n$('-' * 80)>" |