diff --git a/Assets/Splatter-16x9.png b/Assets/Splatter-16x9.png new file mode 100644 index 0000000..7ad03a6 Binary files /dev/null and b/Assets/Splatter-16x9.png differ diff --git a/Assets/Splatter.png b/Assets/Splatter.png new file mode 100644 index 0000000..b0b8875 Binary files /dev/null and b/Assets/Splatter.png differ diff --git a/Out-Splat.ps1 b/Out-Splat.ps1 index 3255083..e82540e 100644 --- a/Out-Splat.ps1 +++ b/Out-Splat.ps1 @@ -9,6 +9,10 @@ Initialize-Splatter .Example Out-Splat -CommandName Get-Command + .Example + Out-Splat -FunctionName Get-MyProcess -Example Get-MyProcess -CommandName Get-Process -DefaultParameter @{ + Id = '$pid' + } -ExcludeParameter * #> [CmdletBinding(DefaultParameterSetName='JustTheSplatter')] [OutputType([ScriptBlock])] @@ -84,11 +88,39 @@ [string] $Description, + # One or more examples. + # This is used to make comment-based help in a generated function. + [Parameter(ValueFromPipelineByPropertyName,ParameterSetName='FunctionalSplatter')] + [Alias('Examples')] + [string[]] + $Example, + + # One or more links. + # This is used to make comment-based help in a generated function. + [Parameter(ValueFromPipelineByPropertyName,ParameterSetName='FunctionalSplatter')] + [Alias('Links')] + [string[]] + $Link, + + # Some notes. + # This is used to make comment-based help in a generated function. + [Parameter(ValueFromPipelineByPropertyName,ParameterSetName='FunctionalSplatter')] + [Alias('Notes')] + [string] + $Note, + # The CmdletBinding attribute for a new function [Parameter(ValueFromPipelineByPropertyName,ParameterSetName='FunctionalSplatter')] [string] $CmdletBinding, + # The [OutputType()] of a function. + # If the type resolves to a [type], it's value will be provided as a [type]. + # Otherwise, it will be provided as a [string] + [Parameter(ValueFromPipelineByPropertyName,ParameterSetName='FunctionalSplatter')] + [string[]] + $OutputType, + # A set of additional parameter declarations. # The keys are the names of the parameters, and the values can be a type and a string containing parameter binding and inline help. [Parameter(ValueFromPipelineByPropertyName,ParameterSetName='FunctionalSplatter')] @@ -441,13 +473,48 @@ $parameterHelp $paramBlock = $paramBlockParts -join (',' + ([Environment]::NewLine * 2)) -if (-not $Synopsis) { - $Synopsis = "Wraps $CommandName" -} + if (-not $Synopsis) { + $Synopsis = "Wraps $CommandName" + } -if (-not $Description) { - $Description = "Calls $CommandName, using splatting" -} + if (-not $Description) { + $Description = "Calls $CommandName, using splatting" + } + + $exampleText = + if ($Example) { + @(foreach ($ex in $Example) { + " .Example" + foreach ($ln in $ex -split '(?>\r\n|\n)') { + " $ln" + } + }) -join [Environment]::NewLine + } else { ''} + + $noteText = + if ($Note) { + @( + " .Notes" + foreach ($ln in $Note -split '(?>\r\n|\n)') { + " $ln" + } + ) -join [Environment]::NewLine + } else { ''} + + + + $linkText = + if ($Link) { + @(foreach ($lnk in $Link) { + " .Link" + " $lnk" + }) -join [Environment]::NewLine + } else { @" + .Link + $CommandName +"@ + } + [ScriptBlock]::Create("function $FunctionName { @@ -455,9 +522,13 @@ if (-not $Description) { .Synopsis $Synopsis .Description - $Description - .Link - $CommandName + $Description$( + if ($exampleText) { [Environment]::NewLine + $exampleText} + )$( + if ($linkText) { [Environment]::NewLine + $linkText} + )$( + if ($NoteText) { [Environment]::NewLine + $noteText} + ) #>$(if ($CmdletBinding) { if ($CmdletBinding -like "*CmdletBinding*") { [Environment]::NewLine + (' '*4) + $CmdletBinding @@ -466,9 +537,18 @@ if (-not $Description) { } } elseif ($originalCmdletBinding) { [Environment]::NewLine + (' '*4) + $originalCmdletBinding - }) + })$( + if ($OutputType) { + [Environment]::NewLine + (' '*4) + '[OutputType(' + @(foreach ($ot in $outputtype) { + if ($ot -as [type]) {"[$ot]"} else { "'$ot'"} + }) -join ',' + ')]' + } else { + [Environment]::NewLine + (' '*4) + '[OutputType([PSObject])]' + } + ) param( -$paramBlock) +$paramBlock + ) process { $(@(foreach ($line in $coreSplat -split ([Environment]::Newline)) { diff --git a/README.md b/README.md index aa24a42..4522aa0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,11 @@ -## Splatter is a simple Splatting toolkit +
+ +

Simple Scripts to Supercharge Splatting

+
+ +[![Test Build And Publish](https://github.com/StartAutomating/Splatter/actions/workflows/TestBuildAndPublish.yml/badge.svg)](https://github.com/StartAutomating/Splatter/actions/workflows/TestBuildAndPublish.yml) + +## Splatter is a simple Splatting toolkit Splatting is a technique of passing parameters in PowerShell. @@ -179,14 +186,14 @@ If you don't need all of the commands, you can use -Verb ### Generating Splatting Code -You can use Out-Splatter to generate code that splats. +You can use Out-Splat to generate code that splats. - Out-Splatter -CommandName Get-Command -DefaultParameter @{Module='Splatter';CommandType='Alias'} | Invoke-Expression + Out-Splat -CommandName Get-Command -DefaultParameter @{Module='Splatter';CommandType='Alias'} | Invoke-Expression You can use also use Out-Splatter to generate whole functions, including help. $scriptBlock = - Out-Splatter -FunctionName Get-SplatterAlias -CommandName Get-Command -DefaultParameter @{ + Out-Splat -FunctionName Get-SplatterAlias -CommandName Get-Command -DefaultParameter @{ Module='Splatter';CommandType='Alias' } -ExcludeParameter * -Synopsis 'Gets Splatter Aliases' -Description 'Gets aliases from the module Splatter' . ([ScriptBlock]::Create($scriptBlock)) diff --git a/Splatter.psd1 b/Splatter.psd1 index d4b9c94..a5cca26 100644 --- a/Splatter.psd1 +++ b/Splatter.psd1 @@ -3,7 +3,7 @@ Copyright = '2019-2021 Start-Automating' RootModule = 'Splatter.psm1' Description = 'Simple Scripts to Supercharge Splatting' - ModuleVersion = '0.5.2' + ModuleVersion = '0.5.3' AliasesToExport = '*' VariablesToExport = '*' GUID = '033f35ed-f8a7-4911-bb62-2691f505ed43' @@ -12,8 +12,14 @@ PSData = @{ ProjectURI = 'https://github.com/StartAutomating/Splatter' LicenseURI = 'https://github.com/StartAutomating/Splatter/blob/master/LICENSE' + IconURI = 'https://raw.githubusercontent.com/StartAutomating/Splatter/master/Assets/Splatter.png' Tags = 'Splatting' ReleaseNotes = @' +### 0.5.3: +* Out-Splat now supports -Examples, -Links, -Notes, and -OutputTypes (Issue #9) +* Adding logo +* Documentation updates. + ### 0.5.2: * Improved pipeline support (Fixes #6) * Out-Splat -CrossStream will now output all streams in generated commands, not just error and output. diff --git a/Splatter.tests.ps1 b/Splatter.tests.ps1 index 803cc5d..63b11b6 100644 --- a/Splatter.tests.ps1 +++ b/Splatter.tests.ps1 @@ -4,13 +4,13 @@ describe Splatter { context 'Get-Splat makes splatting more gettable' { it 'Will Find Matching Parameters for a Command' { $splatOut = @{id = $pid;Foo='bar'} | Get-Splat -Command Get-Process - $splatOut.Keys.Count | should be 1 + $splatOut.Keys.Count | should -Be 1 } it 'Will remove invalid input' { @{id = $pid;Timeout=65kb} | Get-Splat -Command Wait-Process | Select-Object -ExpandProperty Count | - should be 1 + should -Be 1 @{id="blah"} | Get-Splat -Command Get-Process } @@ -43,7 +43,7 @@ describe Splatter { } (foo -id $pid).id| - should be $pid + should -Be $pid } } @@ -65,7 +65,7 @@ describe Splatter { it '*@ merges splats' { @{a='b'}| Merge-Splat -Add @{c='d'} | - Select-Object -ExpandProperty Keys | should be a,c + Select-Object -ExpandProperty Keys | should -Be a,c } it '.@ (Use-Splat)' { @@ -80,7 +80,7 @@ describe Splatter { $splat =@{Message='hi'} $splat | Use-Splat { param([Parameter(Mandatory=$true)][string]$Message) $Message - } | should be hi + } | should -Be hi } it 'Can find matching scripts for a piece of data' { @@ -108,17 +108,17 @@ describe Splatter { Get-Splat $Fruit,$vegetable $matchedSplat | Select-Object -ExpandProperty Command | - should be $Fruit + should -Be $Fruit - $matchedSplat | Use-Splat | should be 'apricot is a fruit' + $matchedSplat | Use-Splat | should -Be 'apricot is a fruit' } it 'Can pass additional arguments' { $2Splat = @{} | Get-Splat {$args} $123 = $2Splat | Use-Splat -ArgumentList 1,2,3 $Another123 = @{} | Use-Splat {$args} 1 2 3 - $123 | should be 1,2,3 - $Another123 | should be 1,2,3 + $123 | should -Be 1,2,3 + $Another123 | should -Be 1,2,3 } } @@ -127,7 +127,7 @@ describe Splatter { @{a='b'}| Merge-Splat -Add @{c='d'} | Select-Object -ExpandProperty Keys | - should be a,c + should -Be a,c } it 'Is easy to remove keys from a Splat' { @@ -140,7 +140,7 @@ describe Splatter { @{a='b'} | Merge-Splat -Map @{a='b',@{c='d'},{@{e='f'}}} | Select-Object -ExpandProperty Keys | Sort-Object | - should be a,b,c,e + should -Be a,b,c,e } it 'Can -Map back objects,if a key was found' { @@ -153,30 +153,30 @@ describe Splatter { @{a='b';"c$(Get-Random)"='d'} | Merge-Splat -Exclude c* | Select-Object -ExpandProperty Keys | - should be a + should -Be a } it 'Can -Include Keys' { @{a='b';"c$(Get-Random)"='d'} | Merge-Splat -Include c* | Select-Object -ExpandProperty Keys | - should belike c* + should -Belike c* } it 'Will squish collisions' { $merged = @{a='b'},[Ordered]@{a='a';b='b';c='c'} | Merge-Splat - $merged.keys | should be a,b,c - $merged.a | should be b,a + $merged.keys | should -Be a,b,c + $merged.a | should -Be b,a } it 'If passed -Keep, it will Keep the first one' { $merged = @{a='b'},[Ordered]@{a='a';b='b';c='c'} | Merge-Splat -Keep - $merged.keys | should be a,b,c - $merged.a | should be b + $merged.keys | should -Be a,b,c + $merged.a | should -Be b } it 'If passed -Replace, it will Replace collisions with new items' { $merged = @{a='b'},[Ordered]@{a='a';b='b';c='c'} | Merge-Splat -Replace - $merged.keys | should be a,b,c - $merged.a | should be a + $merged.keys | should -Be a,b,c + $merged.a | should -Be a } } @@ -219,7 +219,7 @@ describe Splatter { } (foo -id $pid).id| - should be $pid + should -Be $pid } } @@ -232,21 +232,21 @@ describe Splatter { throw 'Splatter failed to embed' } $splatterModule = Get-Module Splatter - ${.@} | should not be $splatterModule.ExportedVariables['.@'] + ${.@} | should -Not -Be $splatterModule.ExportedVariables['.@'] @{id=$pid} | & ${.@} gps } it 'is pretty small' { $embeddedSplatter = Initialize-Splatter - $embeddedSplatter.Length | should belessthan 30kb + $embeddedSplatter.Length | should -Belessthan 30kb } - it 'can be minified and compressed' { + it 'can -Be minified and compressed' { $embeddedSplatter = Initialize-Splatter -Minify -Compress - $embeddedSplatter.Length | should belessthan 10kb + $embeddedSplatter.Length | should -Belessthan 10kb } - it 'Can be embedded as a functionl' { + it 'Can -Be embedded as a functionl' { $embeddedSplatter = Initialize-Splatter -Verb Get . ([ScriptBlock]::Create($embeddedSplatter)) } @@ -257,9 +257,9 @@ describe Splatter { . ([ScriptBlock]::Create($embeddedSplatter)) if (${??@} -ne $null) { - throw '${??@} should be undefined' + throw '${??@} should -Be undefined' } - $embeddedSplatter.Length | should belessthan 20kb + $embeddedSplatter.Length | should -Belessthan 20kb } } @@ -267,7 +267,7 @@ describe Splatter { it 'Can write you a splatting script' { $splatScript = Out-Splat -CommandName Get-Command -DefaultParameter @{Module='Splatter';CommandType='Alias'} - $splatScript| should belike '*Get-Command*@*' + $splatScript| should -Belike '*Get-Command*@*' } it 'Can write you a splating function' { $splatFunction = @@ -275,11 +275,19 @@ describe Splatter { Module='Splatter';CommandType='Alias' } -ExcludeParameter * -Synopsis 'Gets Splatter Aliases' -Description 'Gets aliases from the module Splatter' - $splatFunction | should belike '*function Get-SplatterAlias*{*Get-Command*@*' + $splatFunction | should -Belike '*function Get-SplatterAlias*{*Get-Command*@*' + } + + it 'Can write -Examples' { + $splatFunction = + Out-Splat -FunctionName Get-SplatterAlias -CommandName Get-Command -DefaultParameter @{ + Module='Splatter';CommandType='Alias' + } -ExcludeParameter * -Synopsis 'Gets Splatter Aliases' -Description 'Gets aliases from the module Splatter' -Example 'Get-SplatterAlias' + $splatFunction | should -Belike '*.Example*Get-SplatterAlias*' } } - context 'Splatter can be smart about pipelines' { + context 'Splatter can -Be smart about pipelines' { it 'Can determine which parameters can pipe' { $r = @{Foo='Bar';Baz='Bing'} | @@ -291,8 +299,8 @@ describe Splatter { $baz ) } - $r.PipelineParameter.Keys | should be foo - $r.NonPipelineParameter.Keys | should be baz + $r.PipelineParameter.Keys | should -Be foo + $r.NonPipelineParameter.Keys | should -Be baz } it 'Can -Stream splats' { @@ -306,29 +314,29 @@ describe Splatter { $baz ) - begin { $baz } + Begin { $baz } process { $foo } - } -Stream | should be bing,bar,foo + } -Stream | should -Be bing,bar,foo } } - context 'Splatter tries to be fault-tolerant' { + context 'Splatter tries to -Be fault-tolerant' { it 'Will complain if Use-Splat is not provided with a -Command' { $problem = $null @{aSplat='IMadeMySelf'} | Use-Splat -ErrorAction SilentlyContinue -ErrorVariable Problem - if (-not $Problem) { throw "There should hae been a problem" } + if (-not $Problem) { throw "There should hae -Been a problem" } } it 'Will output properties containing invalid parameters' { $o = @{Date='akllaksjasklj'} | Get-Splat Get-Date -Force - $o.Invalid.keys | should be Date + $o.Invalid.keys | should -Be Date } - it 'Will mark parameters that could not be turned into a ScriptBlock as invalid' { + it 'Will mark parameters that could not -Be turned into a ScriptBlock as invalid' { $o = @{Command='{"hi"'} | Get-Splat Invoke-Command -Force - $o.Invalid.keys | should be Command + $o.Invalid.keys | should -Be Command } } diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index aa08607..0000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,343 +0,0 @@ - -parameters: - - name: ModulePath - type: string - default: - - name: PesterMaxVersion - type: string - default: '4.99.99' -stages: - - stage: PowerShellStaticAnalysis - displayName: Static Analysis - condition: succeeded() - jobs: - - job: PSScriptAnalyzer - displayName: PSScriptAnalyzer - pool: - vmImage: windows-latest - steps: - - powershell: | - [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - Install-Module -Name PSDevOps -Repository PSGallery -Force -Scope CurrentUser - Import-Module PSDevOps -Force -PassThru - displayName: InstallPSDevOps - - powershell: | - [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - Install-Module -Name PSScriptAnalyzer -Repository PSGallery -Force -Scope CurrentUser - Import-Module PSScriptAnalyzer -Force -PassThru - displayName: InstallPSScriptAnalyzer - - powershell: | - Import-Module PSScriptAnalyzer, PSDevOps - $invokeScriptAnalyzerSplat = @{Path='.\'} - if ($ENV:PSScriptAnalyzer_Recurse) { - $invokeScriptAnalyzerSplat.Recurse = $true - } - $result = Invoke-ScriptAnalyzer @invokeScriptAnalyzerSplat - - foreach ($r in $result) { - if ('information', 'warning' -contains $r.Severity) { - Write-ADOWarning -Message $r.Message -SourcePath $r.ScriptPath -LineNumber $r.Line -ColumnNumber $r.Column - } - elseif ($r.Severity -eq 'Error') { - Write-ADOError -Message $r.Message -SourcePath $r.ScriptPath -LineNumber $r.Line -ColumnNumber $r.Column - } - } - displayName: RunPSScriptAnalyzer - - - stage: TestPowerShellCrossPlatform - displayName: Test - jobs: - - job: Windows - displayName: Windows - pool: - vmImage: windows-latest - steps: - - powershell: | - $Parameters = @{} - $Parameters.PesterMaxVersion = ${{coalesce(format('"{0}"',parameters.PesterMaxVersion), '$null')}}; - foreach ($k in @($parameters.Keys)) { - if ([String]::IsNullOrEmpty($parameters[$k])) { - $parameters.Remove($k) - } - } - & {param( - [string] - $PesterMaxVersion = '4.99.99' - ) - [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - Install-Module -Name Pester -Repository PSGallery -Force -Scope CurrentUser -MaximumVersion $PesterMaxVersion -SkipPublisherCheck -AllowClobber - Import-Module Pester -Force -PassThru -MaximumVersion $PesterMaxVersion} @Parameters - displayName: InstallPester - - powershell: | - $Parameters = @{} - $Parameters.ModulePath = ${{coalesce(format('"{0}"',parameters.ModulePath), '$null')}}; - $Parameters.PesterMaxVersion = ${{coalesce(format('"{0}"',parameters.PesterMaxVersion), '$null')}}; - foreach ($k in @($parameters.Keys)) { - if ([String]::IsNullOrEmpty($parameters[$k])) { - $parameters.Remove($k) - } - } - & {param( - [string] - $ModulePath, - [string] - $PesterMaxVersion = '4.99.99' - ) - - $orgName, $moduleName = $env:BUILD_REPOSITORY_ID -split "/" - if (-not $ModulePath) { - $orgName, $moduleName = $env:BUILD_REPOSITORY_ID -split "/" - $ModulePath = ".\$moduleName.psd1" - } - Import-Module Pester -Force -PassThru -MaximumVersion $PesterMaxVersion | Out-Host - Import-Module $ModulePath -Force -PassThru | Out-Host - $result = - Invoke-Pester -PassThru -Verbose -OutputFile ".\$moduleName.TestResults.xml" -OutputFormat NUnitXml ` - -CodeCoverage "$(Build.SourcesDirectory)\*-*.ps1" -CodeCoverageOutputFile ".\$moduleName.Coverage.xml" - - $psDevOpsImported = Import-Module PSDevOps -Force -PassThru -ErrorAction SilentlyContinue - - if ($psDevOpsImported) { - foreach ($pesterTestResult in $pesterResults.TestResult) { - if ($pesterTestResult.Result -eq 'Failed') { - $foundLineNumber = [Regex]::Match($pesterTestResult.StackTrace, ':\s{0,}(?\d+)\s{0,}\w{1,}\s{0,}(?.+)$', 'Multiline') - $errSplat = @{ - Message = $pesterTestResult.ErrorRecord.Exception.Message - Line = $foundLineNumber.Groups["Line"].Value - SourcePath = $foundLineNumber.Groups["File"].Value - } - - Write-ADOError @errSplat - } - } - } else { - if ($result.FailedCount -gt 0) { - throw "$($result.FailedCount) tests failed." - } - }} @Parameters - displayName: RunPester - - task: PublishTestResults@2 - inputs: - testResultsFormat: NUnit - testResultsFiles: '**/*.TestResults.xml' - mergeTestResults: true - - task: PublishCodeCoverageResults@1 - inputs: - codeCoverageTool: JaCoCo - summaryFileLocation: '**/*.Coverage.xml' - reportDirectory: $(System.DefaultWorkingDirectory) - - job: Linux - displayName: Linux - pool: - vmImage: ubuntu-latest - steps: - - script: | - - curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - - curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list | sudo tee /etc/apt/sources.list.d/microsoft.list - sudo apt-get update - sudo apt-get install -y powershell - - displayName: Install PowerShell Core - - pwsh: | - $Parameters = @{} - $Parameters.PesterMaxVersion = ${{coalesce(format('"{0}"',parameters.PesterMaxVersion), '$null')}}; - foreach ($k in @($parameters.Keys)) { - if ([String]::IsNullOrEmpty($parameters[$k])) { - $parameters.Remove($k) - } - } - & {param( - [string] - $PesterMaxVersion = '4.99.99' - ) - [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - Install-Module -Name Pester -Repository PSGallery -Force -Scope CurrentUser -MaximumVersion $PesterMaxVersion -SkipPublisherCheck -AllowClobber - Import-Module Pester -Force -PassThru -MaximumVersion $PesterMaxVersion} @Parameters - displayName: InstallPester - - pwsh: | - $Parameters = @{} - $Parameters.ModulePath = ${{coalesce(format('"{0}"',parameters.ModulePath), '$null')}}; - $Parameters.PesterMaxVersion = ${{coalesce(format('"{0}"',parameters.PesterMaxVersion), '$null')}}; - foreach ($k in @($parameters.Keys)) { - if ([String]::IsNullOrEmpty($parameters[$k])) { - $parameters.Remove($k) - } - } - & {param( - [string] - $ModulePath, - [string] - $PesterMaxVersion = '4.99.99' - ) - - $orgName, $moduleName = $env:BUILD_REPOSITORY_ID -split "/" - if (-not $ModulePath) { - $orgName, $moduleName = $env:BUILD_REPOSITORY_ID -split "/" - $ModulePath = ".\$moduleName.psd1" - } - Import-Module Pester -Force -PassThru -MaximumVersion $PesterMaxVersion | Out-Host - Import-Module $ModulePath -Force -PassThru | Out-Host - $result = - Invoke-Pester -PassThru -Verbose -OutputFile ".\$moduleName.TestResults.xml" -OutputFormat NUnitXml ` - -CodeCoverage "$(Build.SourcesDirectory)\*-*.ps1" -CodeCoverageOutputFile ".\$moduleName.Coverage.xml" - - $psDevOpsImported = Import-Module PSDevOps -Force -PassThru -ErrorAction SilentlyContinue - - if ($psDevOpsImported) { - foreach ($pesterTestResult in $pesterResults.TestResult) { - if ($pesterTestResult.Result -eq 'Failed') { - $foundLineNumber = [Regex]::Match($pesterTestResult.StackTrace, ':\s{0,}(?\d+)\s{0,}\w{1,}\s{0,}(?.+)$', 'Multiline') - $errSplat = @{ - Message = $pesterTestResult.ErrorRecord.Exception.Message - Line = $foundLineNumber.Groups["Line"].Value - SourcePath = $foundLineNumber.Groups["File"].Value - } - - Write-ADOError @errSplat - } - } - } else { - if ($result.FailedCount -gt 0) { - throw "$($result.FailedCount) tests failed." - } - }} @Parameters - displayName: RunPester - - task: PublishTestResults@2 - inputs: - testResultsFormat: NUnit - testResultsFiles: '**/*.TestResults.xml' - mergeTestResults: true - - task: PublishCodeCoverageResults@1 - inputs: - codeCoverageTool: JaCoCo - summaryFileLocation: '**/*.Coverage.xml' - reportDirectory: $(System.DefaultWorkingDirectory) - - job: MacOS - displayName: MacOS - pool: - vmImage: macos-latest - steps: - - script: | - brew update - brew tap caskroom/cask - brew cask install powershell - displayName: Install PowerShell Core - - pwsh: | - $Parameters = @{} - $Parameters.PesterMaxVersion = ${{coalesce(format('"{0}"',parameters.PesterMaxVersion), '$null')}}; - foreach ($k in @($parameters.Keys)) { - if ([String]::IsNullOrEmpty($parameters[$k])) { - $parameters.Remove($k) - } - } - & {param( - [string] - $PesterMaxVersion = '4.99.99' - ) - [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - Install-Module -Name Pester -Repository PSGallery -Force -Scope CurrentUser -MaximumVersion $PesterMaxVersion -SkipPublisherCheck -AllowClobber - Import-Module Pester -Force -PassThru -MaximumVersion $PesterMaxVersion} @Parameters - displayName: InstallPester - - pwsh: | - $Parameters = @{} - $Parameters.ModulePath = ${{coalesce(format('"{0}"',parameters.ModulePath), '$null')}}; - $Parameters.PesterMaxVersion = ${{coalesce(format('"{0}"',parameters.PesterMaxVersion), '$null')}}; - foreach ($k in @($parameters.Keys)) { - if ([String]::IsNullOrEmpty($parameters[$k])) { - $parameters.Remove($k) - } - } - & {param( - [string] - $ModulePath, - [string] - $PesterMaxVersion = '4.99.99' - ) - - $orgName, $moduleName = $env:BUILD_REPOSITORY_ID -split "/" - if (-not $ModulePath) { - $orgName, $moduleName = $env:BUILD_REPOSITORY_ID -split "/" - $ModulePath = ".\$moduleName.psd1" - } - Import-Module Pester -Force -PassThru -MaximumVersion $PesterMaxVersion | Out-Host - Import-Module $ModulePath -Force -PassThru | Out-Host - $result = - Invoke-Pester -PassThru -Verbose -OutputFile ".\$moduleName.TestResults.xml" -OutputFormat NUnitXml ` - -CodeCoverage "$(Build.SourcesDirectory)\*-*.ps1" -CodeCoverageOutputFile ".\$moduleName.Coverage.xml" - - $psDevOpsImported = Import-Module PSDevOps -Force -PassThru -ErrorAction SilentlyContinue - - if ($psDevOpsImported) { - foreach ($pesterTestResult in $pesterResults.TestResult) { - if ($pesterTestResult.Result -eq 'Failed') { - $foundLineNumber = [Regex]::Match($pesterTestResult.StackTrace, ':\s{0,}(?\d+)\s{0,}\w{1,}\s{0,}(?.+)$', 'Multiline') - $errSplat = @{ - Message = $pesterTestResult.ErrorRecord.Exception.Message - Line = $foundLineNumber.Groups["Line"].Value - SourcePath = $foundLineNumber.Groups["File"].Value - } - - Write-ADOError @errSplat - } - } - } else { - if ($result.FailedCount -gt 0) { - throw "$($result.FailedCount) tests failed." - } - }} @Parameters - displayName: RunPester - - task: PublishTestResults@2 - inputs: - testResultsFormat: NUnit - testResultsFiles: '**/*.TestResults.xml' - mergeTestResults: true - - task: PublishCodeCoverageResults@1 - inputs: - codeCoverageTool: JaCoCo - summaryFileLocation: '**/*.Coverage.xml' - reportDirectory: $(System.DefaultWorkingDirectory) - - condition: succeeded() - - stage: UpdatePowerShellGallery - displayName: Update - condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) - variables: - - group: Gallery - jobs: - - job: Publish - displayName: PowerShell Gallery - pool: - vmImage: windows-latest - steps: - - powershell: | - $orgName, $moduleName = $env:BUILD_REPOSITORY_ID -split "/" - $imported = Import-Module ".\$moduleName.psd1" -Force -PassThru - $foundModule = Find-Module -Name $ModuleName - if ($foundModule.Version -ge $imported.Version) { - Write-Warning "##vso[task.logissue type=warning]Gallery Version of $moduleName is more recent ($($foundModule.Version) >= $($imported.Version))" - } else { - $gk = '$(GalleryKey)' - $stagingDir = '$(Build.ArtifactStagingDirectory)' - $moduleTempPath = Join-Path $stagingDir $moduleName - - Write-Host "Staging Directory: $ModuleTempPath" - - $imported | Split-Path | Copy-Item -Destination $moduleTempPath -Recurse - $moduleGitPath = Join-Path $moduleTempPath '.git' - Write-Host "Removing .git directory" - Remove-Item -Recurse -Force $moduleGitPath - Write-Host "Module Files:" - Get-ChildItem $moduleTempPath -Recurse - Write-Host "Publishing $moduleName [$($imported.Version)] to Gallery" - Publish-Module -Path $moduleTempPath -NuGetApiKey $gk - if ($?) { - Write-Host "Published to Gallery" - } else { - Write-Host "Gallery Publish Failed" - exit 1 - } - } - displayName: PublishPowerShellGallery - -