diff --git a/.github/workflows/TestBuildAndPublish.yml b/.github/workflows/TestBuildAndPublish.yml index 419c9cf..1f4d131 100644 --- a/.github/workflows/TestBuildAndPublish.yml +++ b/.github/workflows/TestBuildAndPublish.yml @@ -1,8 +1,9 @@ - -name: Test Build And Publish + +name: Analyze, Test, Tag, and Publish on: - workflow_dispatch: push: + pull_request: + workflow_dispatch: jobs: PowerShellStaticAnalysis: runs-on: ubuntu-latest @@ -124,6 +125,8 @@ jobs: $Parameters = @{} $Parameters.ModulePath = ${env:ModulePath} $Parameters.PesterMaxVersion = ${env:PesterMaxVersion} + $Parameters.NoCoverage = ${env:NoCoverage} + $Parameters.NoCoverage = $parameters.NoCoverage -match 'true'; foreach ($k in @($parameters.Keys)) { if ([String]::IsNullOrEmpty($parameters[$k])) { $parameters.Remove($k) @@ -142,7 +145,11 @@ jobs: $ModulePath, # The Pester max version. By default, this is pinned to 4.99.99. [string] - $PesterMaxVersion = '4.99.99' + $PesterMaxVersion = '4.99.99', + + # If set, will not collect code coverage. + [switch] + $NoCoverage ) $global:ErrorActionPreference = 'continue' @@ -154,11 +161,18 @@ jobs: $importedModule = Import-Module $ModulePath -Force -PassThru $importedPester, $importedModule | Out-Host + $codeCoverageParameters = @{ + CodeCoverage = "$($importedModule | Split-Path)\*-*.ps1" + CodeCoverageOutputFile = ".\$moduleName.Coverage.xml" + } + + if ($NoCoverage) { + $codeCoverageParameters = @{} + } $result = - Invoke-Pester -PassThru -Verbose -OutputFile ".\$moduleName.TestResults.xml" -OutputFormat NUnitXml ` - -CodeCoverage "$($importedModule | Split-Path)\*-*.ps1" -CodeCoverageOutputFile ".\$moduleName.Coverage.xml" + Invoke-Pester -PassThru -Verbose -OutputFile ".\$moduleName.TestResults.xml" -OutputFormat NUnitXml @codeCoverageParameters "::set-output name=TotalCount::$($result.TotalCount)", "::set-output name=PassedCount::$($result.PassedCount)", @@ -238,7 +252,7 @@ jobs: if (-not ($gitHubEvent.head_commit.message -match "Merge Pull Request #(?\d+)") -and (-not $gitHubEvent.psobject.properties['inputs'])) { - "::warning::Pull Request has not merged, skipping" | Out-Host + "::warning::Pull Request has not merged, skipping Tagging" | Out-Host return } @@ -291,6 +305,9 @@ jobs: $Parameters.UserEmail = ${env:UserEmail} $Parameters.UserName = ${env:UserName} $Parameters.TagVersionFormat = ${env:TagVersionFormat} + $Parameters.ReleaseNameFormat = ${env:ReleaseNameFormat} + $Parameters.ReleaseAsset = ${env:ReleaseAsset} + $Parameters.ReleaseAsset = $parameters.ReleaseAsset -split ';' -replace '^[''"]' -replace '[''"]$' foreach ($k in @($parameters.Keys)) { if ([String]::IsNullOrEmpty($parameters[$k])) { $parameters.Remove($k) @@ -312,7 +329,15 @@ jobs: # The tag version format (default value: 'v$(imported.Version)') # This can expand variables. $imported will contain the imported module. [string] - $TagVersionFormat = 'v$($imported.Version)' + $TagVersionFormat = 'v$($imported.Version)', + + # The release name format (default value: '$($imported.Name) $($imported.Version)') + [string] + $ReleaseNameFormat = '$($imported.Name) $($imported.Version)', + + # Any assets to attach to the release. Can be a wildcard or file name. + [string[]] + $ReleaseAsset ) @@ -329,7 +354,7 @@ jobs: if (-not ($gitHubEvent.head_commit.message -match "Merge Pull Request #(?\d+)") -and (-not $gitHubEvent.psobject.properties['inputs'])) { - "::warning::Pull Request has not merged, skipping" | Out-Host + "::warning::Pull Request has not merged, skipping GitHub release" | Out-Host return } @@ -348,6 +373,7 @@ jobs: $targetVersion =$ExecutionContext.InvokeCommand.ExpandString($TagVersionFormat) $targetReleaseName = $targetVersion $releasesURL = 'https://api.github.com/repos/${{github.repository}}/releases' + "Release URL: $releasesURL" | Out-Host $listOfReleases = Invoke-RestMethod -Uri $releasesURL -Method Get -Headers @{ "Accept" = "application/vnd.github.v3+json" "Authorization" = 'Bearer ${{ secrets.GITHUB_TOKEN }}' @@ -357,32 +383,80 @@ jobs: if ($releaseExists) { "::warning::Release '$($releaseExists.Name )' Already Exists" | Out-Host - return + $releasedIt = $releaseExists + } else { + $releasedIt = Invoke-RestMethod -Uri $releasesURL -Method Post -Body ( + [Ordered]@{ + owner = '${{github.owner}}' + repo = '${{github.repository}}' + tag_name = $targetVersion + name = $ExecutionContext.InvokeCommand.ExpandString($ReleaseNameFormat) + body = + if ($env:RELEASENOTES) { + $env:RELEASENOTES + } elseif ($imported.PrivateData.PSData.ReleaseNotes) { + $imported.PrivateData.PSData.ReleaseNotes + } else { + "$($imported.Name) $targetVersion" + } + draft = if ($env:RELEASEISDRAFT) { [bool]::Parse($env:RELEASEISDRAFT) } else { $false } + prerelease = if ($env:PRERELEASE) { [bool]::Parse($env:PRERELEASE) } else { $false } + } | ConvertTo-Json + ) -Headers @{ + "Accept" = "application/vnd.github.v3+json" + "Content-type" = "application/json" + "Authorization" = 'Bearer ${{ secrets.GITHUB_TOKEN }}' + } } - Invoke-RestMethod -Uri $releasesURL -Method Post -Body ( - [Ordered]@{ - owner = '${{github.owner}}' - repo = '${{github.repository}}' - tag_name = $targetVersion - name = "$($imported.Name) $targetVersion" - body = - if ($env:RELEASENOTES) { - $env:RELEASENOTES - } elseif ($imported.PrivateData.PSData.ReleaseNotes) { - $imported.PrivateData.PSData.ReleaseNotes - } else { - "$($imported.Name) $targetVersion" + + + + if (-not $releasedIt) { + throw "Release failed" + } else { + $releasedIt | Out-Host + } + + $releaseUploadUrl = $releasedIt.upload_url -replace '\{.+$' + + if ($ReleaseAsset) { + $fileList = Get-ChildItem -Recurse + $filesToRelease = + @(:nextFile foreach ($file in $fileList) { + foreach ($relAsset in $ReleaseAsset) { + if ($relAsset -match '[\*\?]') { + if ($file.Name -like $relAsset) { + $file; continue nextFile + } + } elseif ($file.Name -eq $relAsset -or $file.FullName -eq $relAsset) { + $file; continue nextFile + } } - draft = if ($env:RELEASEISDRAFT) { [bool]::Parse($env:RELEASEISDRAFT) } else { $false } - prerelease = if ($env:PRERELEASE) { [bool]::Parse($env:PRERELEASE) } else { $false } - } | ConvertTo-Json - ) -Headers @{ - "Accept" = "application/vnd.github.v3+json" - "Content-type" = "application/json" - "Authorization" = 'Bearer ${{ secrets.GITHUB_TOKEN }}' + }) + + $releasedFiles = @{} + foreach ($file in $filesToRelease) { + if ($releasedFiles[$file.Name]) { + Write-Warning "Already attached file $($file.Name)" + continue + } else { + $fileBytes = [IO.File]::ReadAllBytes($file.FullName) + $releasedFiles[$file.Name] = + Invoke-RestMethod -Uri "${releaseUploadUrl}?name=$($file.Name)" -Headers @{ + "Accept" = "application/vnd.github+json" + "Authorization" = 'Bearer ${{ secrets.GITHUB_TOKEN }}' + } -Body $fileBytes -ContentType Application/octet-stream + $releasedFiles[$file.Name] + } + } + + "Attached $($releasedFiles.Count) file(s) to release" | Out-Host } + + + } @Parameters - name: PublishPowerShellGallery id: PublishPowerShellGallery @@ -390,6 +464,8 @@ jobs: run: | $Parameters = @{} $Parameters.ModulePath = ${env:ModulePath} + $Parameters.Exclude = ${env:Exclude} + $Parameters.Exclude = $parameters.Exclude -split ';' -replace '^[''"]' -replace '[''"]$' foreach ($k in @($parameters.Keys)) { if ([String]::IsNullOrEmpty($parameters[$k])) { $parameters.Remove($k) @@ -398,12 +474,20 @@ jobs: Write-Host "::debug:: PublishPowerShellGallery $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')" & {param( [string] - $ModulePath + $ModulePath, + + [string[]] + $Exclude = @('*.png', '*.mp4', '*.jpg','*.jpeg', '*.gif', 'docs[/\]*') ) + $gitHubEvent = if ($env:GITHUB_EVENT_PATH) { [IO.File]::ReadAllText($env:GITHUB_EVENT_PATH) | ConvertFrom-Json } else { $null } + if (-not $Exclude) { + $Exclude = @('*.png', '*.mp4', '*.jpg','*.jpeg', '*.gif','docs[/\]*') + } + @" ::group::GitHubEvent @@ -411,9 +495,15 @@ jobs: ::endgroup:: "@ | Out-Host + @" + ::group::PSBoundParameters + $($PSBoundParameters | ConvertTo-Json -Depth 100) + ::endgroup:: + "@ | Out-Host + if (-not ($gitHubEvent.head_commit.message -match "Merge Pull Request #(?\d+)") -and (-not $gitHubEvent.psobject.properties['inputs'])) { - "::warning::Pull Request has not merged, skipping" | Out-Host + "::warning::Pull Request has not merged, skipping Gallery Publish" | Out-Host return } @@ -428,9 +518,9 @@ jobs: if (-not $imported) { return } - $foundModule = try { Find-Module -Name $imported.Name -ErrorAction SilentlyContinue } catch {} + $foundModule = try { Find-Module -Name $imported.Name -ErrorAction SilentlyContinue} catch {} - if ($foundModule -and $foundModule.Version -ge $imported.Version) { + if ($foundModule -and (([Version]$foundModule.Version) -ge ([Version]$imported.Version))) { "::warning::Gallery Version of $moduleName is more recent ($($foundModule.Version) >= $($imported.Version))" | Out-Host } else { @@ -453,9 +543,24 @@ jobs: if (Test-Path $moduleGitPath) { Remove-Item -Recurse -Force $moduleGitPath } + + if ($Exclude) { + "::notice::Attempting to Exlcude $exclude" | Out-Host + Get-ChildItem $moduleTempPath -Recurse | + Where-Object { + foreach ($ex in $exclude) { + if ($_.FullName -like $ex) { + "::notice::Excluding $($_.FullName)" | Out-Host + return $true + } + } + } | + Remove-Item + } + Write-Host "Module Files:" Get-ChildItem $moduleTempPath -Recurse - Write-Host "Publishing $moduleName [$($imported.Version)] to Gallery" + Write-Host "Publishing $moduleName [$($imported.Version)] to Gallery" Publish-Module -Path $moduleTempPath -NuGetApiKey $gk if ($?) { Write-Host "Published to Gallery" @@ -465,4 +570,23 @@ jobs: } } } @Parameters + BuildSplatter: + runs-on: ubuntu-latest + if: ${{ success() }} + steps: + - name: Check out repository + uses: actions/checkout@v2 + - name: GitLogger + uses: GitLogging/GitLoggerAction@main + id: GitLogger + - name: Use PSSVG Action + uses: StartAutomating/PSSVG@main + id: PSSVG + - name: BuildPipeScript + uses: StartAutomating/PipeScript@main + - name: UseEZOut + uses: StartAutomating/EZOut@master + - name: Run HelpOut + uses: StartAutomating/HelpOut@master + id: HelpOut diff --git a/Assets/Splatter-16x9.png b/Assets/Splatter-16x9.png deleted file mode 100644 index 7ad03a6..0000000 Binary files a/Assets/Splatter-16x9.png and /dev/null differ diff --git a/Assets/Splatter.png b/Assets/Splatter.png index b0b8875..fafc917 100644 Binary files a/Assets/Splatter.png and b/Assets/Splatter.png differ diff --git a/Assets/Splatter.svg b/Assets/Splatter.svg new file mode 100644 index 0000000..7064c4a --- /dev/null +++ b/Assets/Splatter.svg @@ -0,0 +1,11 @@ + + + + + + + + + spl@tter + + diff --git a/Assets/Splatter@1080p-Animated.svg b/Assets/Splatter@1080p-Animated.svg new file mode 100644 index 0000000..b0a8929 --- /dev/null +++ b/Assets/Splatter@1080p-Animated.svg @@ -0,0 +1,12 @@ + + + + + + + + + spl@tter + + + diff --git a/Assets/Splatter@1080p.svg b/Assets/Splatter@1080p.svg new file mode 100644 index 0000000..2bbd446 --- /dev/null +++ b/Assets/Splatter@1080p.svg @@ -0,0 +1,11 @@ + + + + + + + + + spl@tter + + diff --git a/Build/Splatter.GitHubWorkflow.PSDevOps.ps1 b/Build/Splatter.GitHubWorkflow.PSDevOps.ps1 new file mode 100644 index 0000000..9dcf041 --- /dev/null +++ b/Build/Splatter.GitHubWorkflow.PSDevOps.ps1 @@ -0,0 +1,8 @@ +#requires -Module PSDevOps +Import-BuildStep -ModuleName Splatter +Push-Location ($PSScriptRoot | Split-Path) +New-GitHubWorkflow -Name "Analyze, Test, Tag, and Publish" -On Push, PullRequest, Demand -Job PowerShellStaticAnalysis, + TestPowerShellOnLinux, + TagReleaseAndPublish, + BuildSplatter -OutputPath .\.github\workflows\TestBuildAndPublish.yml +Pop-Location \ No newline at end of file diff --git a/Build/Splatter.HelpOut.ps1 b/Build/Splatter.HelpOut.ps1 new file mode 100644 index 0000000..f63f044 --- /dev/null +++ b/Build/Splatter.HelpOut.ps1 @@ -0,0 +1,16 @@ +#require -Module HelpOut +Push-Location ($PSScriptRoot | Split-Path) + +$SplatterLoaded = Get-Module Splatter +if (-not $SplatterLoaded) { + $SplatterLoaded = Get-ChildItem -Recurse -Filter "*.psd1" | Where-Object Name -like 'Splatter*' | Import-Module -Name { $_.FullName } -Force -PassThru +} +if ($SplatterLoaded) { + "::notice title=ModuleLoaded::Splatter Loaded" | Out-Host +} else { + "::error:: Splatter not loaded" |Out-Host +} + +Save-MarkdownHelp -Module Splatter -SkipCommandType Alias -PassThru + +Pop-Location \ No newline at end of file diff --git a/Build/Splatter.PSSVG.ps1 b/Build/Splatter.PSSVG.ps1 new file mode 100644 index 0000000..77a72e9 --- /dev/null +++ b/Build/Splatter.PSSVG.ps1 @@ -0,0 +1,64 @@ +#requires -Module PSSVG + +Push-Location ($PSScriptRoot | Split-Path) + +$psChevron = + svg.symbol -Id psChevron -Content @( + svg.polygon -Points (@( + "40,20" + "45,20" + "60,50" + "35,80" + "32.5,80" + "55,50" + ) -join ' ') + ) -ViewBox 100, 100 -PreserveAspectRatio $false + +$assetsPath = Join-Path $pwd Assets + +if (-not (Test-Path $assetsPath)) { + $null = New-item -ItemType Directory -Path $assetsPath +} + +$FontName = 'Dancing Script' +svg -ViewBox 300, 100 @( + $psChevron + svg.use -Href '#psChevron' -Fill '#b00707' -Height 5% -Y 47% -X -1.5% + SVG.GoogleFont -FontName $FontName + + svg.text -X 50% -Y 50% -TextAnchor 'middle' -DominantBaseline 'middle' -Style "font-family: '$FontName', cursive" -Fill '#b00707' -Class 'foreground-fill' -Content @( + SVG.tspan -FontSize .5em -Content 'spl@tter' + # SVG.tspan -FontSize 1em -Content 'git' -Dx -.25em + ) -FontSize 4em -FontWeight 500 +) -OutputPath (Join-Path $assetsPath Splatter.svg) + +svg -ViewBox 1920, 1080 @( + $psChevron + svg.use -Href '#psChevron' -Fill '#b00707' -Height 10% -Y 43% -X -4.5% + SVG.GoogleFont -FontName $FontName + + svg.text -X 50% -Y 50% -TextAnchor 'middle' -DominantBaseline 'middle' -Style "font-family: '$FontName', cursive" -Fill '#b00707' -Class 'foreground-fill' -Content @( + SVG.tspan -FontSize .5em -Content 'spl@tter' + # SVG.tspan -FontSize 1em -Content 'git' -Dx -.25em + ) -FontSize 80em -FontWeight 500 +) -OutputPath (Join-Path $assetsPath 'Splatter@1080p.svg') + + +$AnimationTimeframe = [Ordered]@{ + Dur = '2s' + RepeatCount = 'indefinite' +} + +svg -ViewBox 1920, 1080 @( + $psChevron + svg.use -Href '#psChevron' -Fill '#b00707' -Height 10% -Y 43% -X -4.5% + SVG.GoogleFont -FontName $FontName + + svg.text -X 50% -Y 50% -TextAnchor 'middle' -DominantBaseline 'middle' -Style "font-family: '$FontName', cursive; " -Fill '#b00707' -Class 'foreground-fill' -Content @( + SVG.tspan -FontSize .5em -Content 'spl@tter' + # SVG.tspan -FontSize 1em -Content 'git' -Dx -.25em + SVG.animate -AttributeName fill -dur 10s -Values '#b00707;#b01707;#ed2222;#b01707;#b00707' -RepeatCount indefinite + ) -FontSize 80em -FontWeight 500 +) -OutputPath (Join-Path $assetsPath 'Splatter@1080p-Animated.svg') + +Pop-Location \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d69f734 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +### 0.5.4: + +* New Splatter Logo (#12) +* All splats will become PSObjects consistently (#13) +* Generated Documentation (#14) +* Declaring aliases inline (#16) + +--- + +### 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/Find-Splat.ps1 b/Find-Splat.ps1 index be6270d..bf4a959 100644 --- a/Find-Splat.ps1 +++ b/Find-Splat.ps1 @@ -12,6 +12,7 @@ .Example @{Id=$pid} | Find-Splat -Global #> + [Alias('??@','fSplat')] param( # One or more commands. # If not provided, commands from the current module will be searched. diff --git a/Get-Splat.ps1 b/Get-Splat.ps1 index ed70d30..84cfad3 100644 --- a/Get-Splat.ps1 +++ b/Get-Splat.ps1 @@ -16,6 +16,7 @@ .Example @{id=$pid} | & ${?@} # Get-Splat as a script block #> + [Alias('?@','gSplat')] param( # The command that is being splatted. [Parameter(Mandatory=$true,Position=0)] @@ -40,10 +41,10 @@ if (-not ${script:_@pp}) { ${script:_@pp} = @{} } # * All Pipelined Parameters $ValidateAttributes = { param( - [Parameter(Mandatory)]$value, + [Parameter(Mandatory)]$value, [Parameter(Mandatory)]$attributes ) - + foreach ($attr in $attributes) { $_ = $this = $value if ($attr -is [Management.Automation.ValidateScriptAttribute]) { @@ -52,27 +53,23 @@ $attr } } - elseif ($attr -is [Management.Automation.ValidatePatternAttribute] -and + elseif ($attr -is [Management.Automation.ValidatePatternAttribute] -and (-not [Regex]::new($attr.RegexPattern, $attr.Options, '00:00:05').IsMatch($value)) ) { $attr } - elseif ($attr -is [Management.Automation.ValidateSetAttribute] -and + elseif ($attr -is [Management.Automation.ValidateSetAttribute] -and $attr.ValidValues -notcontains $value) { $attr } elseif ($attr -is [Management.Automation.ValidateRangeAttribute] -and ( ($value -gt $attr.MaxRange) -or ($value -lt $attr.MinRange) - )) {$attr} + )) {$attr} } } } process { - + $ap,$ac,$amp = ${script:_@p},${script:_@c}, ${script:_@mp} #region Turn dictionaries into PSObjects if ($Splat -is [Collections.IDictionary]) { - if ($splat.GetType().Name -ne 'PSBoundParametersDictionary') { - $Splat = [PSCustomObject]$Splat - } else { - $splat = [PSCustomObject]([Ordered]@{} + $Splat) - } + $splat = [PSCustomObject]([Ordered]@{} + $Splat) } #endregion Turn dictionaries into PSObjects @@ -113,15 +110,15 @@ foreach ($param in $cmd.Parameters.Values) { foreach ($attr in $param.Attributes) { if ($attr.ValueFromPipeline) { - $param + $param } } }) } - + $cmdMd = $cmd -as [Management.Automation.CommandMetaData] $problems = @( - + foreach ($vfp in ${script:_@pp}.$cmd) { if ($in -is $vfp.ParameterType -or ($vfp.ParameterType.IsArray -and $in -as $vfp.ParameterType) @@ -133,14 +130,14 @@ } if (-not $badAttributes -or $Force) { $null = $params.Add($vfp.Name) - $pipe[$vfp.Name] = $v + $pipe[$vfp.Name] = $v $outSplat[$vfp.Name] = $v $paramMap[$vfp.Name] = $vfp.Name - $pipelineParameterSets = - @(foreach ($attr in $vfp.Attributes) { + $pipelineParameterSets = + @(foreach ($attr in $vfp.Attributes) { if ($attr.ParameterSetName) { $attr.ParameterSetName} }) - } + } } } @@ -165,14 +162,14 @@ } } - if (-not $param) { + if (-not $param) { $pn - continue + continue } $paramMap[$param.Name] = $pn if ($params -contains $param) { continue } $pt=$param.ParameterType - $paramSets = + $paramSets = @(foreach ($attr in $param.Attributes) { if ($attr.ParameterSetName) { $attr.ParameterSetName } }) @@ -200,10 +197,10 @@ if ($nv -is [PSVariable] -or $Force) { $null = $params.Add($param) :CanItPipe do { - foreach ($attr in $param.Attributes) { - if ($attr.ValueFromPipeline -or $attr.ValueFromPipelineByPropertyName -and + foreach ($attr in $param.Attributes) { + if ($attr.ValueFromPipeline -or $attr.ValueFromPipelineByPropertyName -and ((-not $pipelineParameterSets) -or ($pipelineParameterSets -contains $attr.ParameterSetName)) - ) { + ) { $pipe[$prop.Name] = $v break CanItPipe } @@ -219,17 +216,17 @@ } }) - + $Mandatory = @{} - + foreach ($param in $cmdMd.Parameters.Values) { foreach ($a in $param.Attributes) { if (-not $a.Mandatory) { continue } if ($a -isnot [Management.Automation.ParameterAttribute]) { continue } if (-not $Mandatory[$a.ParameterSetName]) { $Mandatory[$a.ParameterSetName] = [Ordered]@{} } $mp = ($paramMap.($param.Name)) - $Mandatory[$a.ParameterSetName].($param.Name) = - if ($mp) { + $Mandatory[$a.ParameterSetName].($param.Name) = + if ($mp) { if ($pipelineParameterName -contains $param.Name) { $in } else { @@ -238,8 +235,8 @@ } } } - $amp.$cmd = $Mandatory - + $amp.$cmd = $Mandatory + $mandatory = $amp.$cmd $missingMandatory = @{} diff --git a/GitHub/Jobs/BuildSplatter.psd1 b/GitHub/Jobs/BuildSplatter.psd1 new file mode 100644 index 0000000..cf4b7ac --- /dev/null +++ b/GitHub/Jobs/BuildSplatter.psd1 @@ -0,0 +1,27 @@ +@{ + "runs-on" = "ubuntu-latest" + if = '${{ success() }}' + steps = @( + @{ + name = 'Check out repository' + uses = 'actions/checkout@v2' + }, + @{ + name = 'GitLogger' + uses = 'GitLogging/GitLoggerAction@main' + id = 'GitLogger' + }, + @{ + name = 'Use PSSVG Action' + uses = 'StartAutomating/PSSVG@main' + id = 'PSSVG' + }, + 'RunPipeScript', + 'RunEZOut', + @{ + name = 'Run HelpOut' + uses = 'StartAutomating/HelpOut@master' + id = 'HelpOut' + } + ) +} \ No newline at end of file diff --git a/Merge-Splat.ps1 b/Merge-Splat.ps1 index 39b6f1c..2a7518f 100644 --- a/Merge-Splat.ps1 +++ b/Merge-Splat.ps1 @@ -29,15 +29,16 @@ } } #> + [Alias('*@','mSplat')] param( # The splat - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline)] [Alias('InputObject')] [PSObject[]] $Splat, # Splats or objects that will be added to the splat. - [Parameter(Position=0,ValueFromRemainingArguments=$true)] + [Parameter(Position=0,ValueFromRemainingArguments)] [Alias('With', 'W', 'A', '+')] [PSObject[]] $Add, @@ -74,9 +75,11 @@ [Collections.IDictionary] $Map, + # If set, will keep existing values in a splat instead of adding it to a list of values. [switch] $Keep, + # If set, will replace existing values in a splat instead of adding it to a list of values. [switch] $Replace) @@ -155,11 +158,11 @@ } } process { - $isTheEndOfTheLine? = + $isTheEndOfTheLine = $MyInvocation.PipelinePosition -eq $MyInvocation.PipelineLength if ($Splat) { $accumulate.AddRange($Splat) } if ($Add) { $accumulate.AddRange($add) } - if (-not $isTheEndOfTheLine?) { + if (-not $isTheEndOfTheLine) { . $imSplat } } diff --git a/Out-Splat.ps1 b/Out-Splat.ps1 index e82540e..b8c4ae0 100644 --- a/Out-Splat.ps1 +++ b/Out-Splat.ps1 @@ -15,6 +15,7 @@ } -ExcludeParameter * #> [CmdletBinding(DefaultParameterSetName='JustTheSplatter')] + [Alias('=>@','oSplat')] [OutputType([ScriptBlock])] param( # The name of the command that will be splatted diff --git a/README.md b/README.md index 4522aa0..478205d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ 
- +

Simple Scripts to Supercharge Splatting

diff --git a/Splatter.psd1 b/Splatter.psd1 index a5cca26..d1885e1 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.3' + ModuleVersion = '0.5.4' AliasesToExport = '*' VariablesToExport = '*' GUID = '033f35ed-f8a7-4911-bb62-2691f505ed43' @@ -13,8 +13,17 @@ 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' + Tags = 'Splatting', 'PipeScript' ReleaseNotes = @' +### 0.5.4: + +* New Splatter Logo (#12) +* All splats will become PSObjects consistently (#13) +* Generated Documentation (#14) +* Declaring aliases inline (#16) + +--- + ### 0.5.3: * Out-Splat now supports -Examples, -Links, -Notes, and -OutputTypes (Issue #9) * Adding logo diff --git a/Splatter.psm1 b/Splatter.psm1 index ca72383..a52d75a 100644 --- a/Splatter.psm1 +++ b/Splatter.psm1 @@ -8,17 +8,7 @@ param() . $psScriptRoot\Initialize-Splatter.ps1 -# ?@ Get the splat for a given command. -Set-Alias -Name '?@' -Value Get-Splat -Set-Alias -Name 'gSplat' -Value Get-Splat -Set-Alias -Name '??@' -Value Find-Splat -Set-Alias -Name 'fSplat' -Value Find-Splat -Set-Alias -Name '.@' -Value Use-Splat -Set-Alias -Name 'uSplat' -Value Use-Splat -Set-Alias -Name '*@' -Value Merge-Splat -Set-Alias -Name 'mSplat' -Value Merge-Splat -Set-Alias -Name '=>@' -Value Out-Splat - +# Assign each splatter command to a variable for easy internal access ${?@} = $gSplat = $GetSplat = ${function:Get-Splat} ${??@} = $fSplat = $FindSplat = ${function:Find-Splat} ${*@} = $mSplat = $MergeSplat = ${function:Merge-Splat} diff --git a/Use-Splat.ps1 b/Use-Splat.ps1 index 21fc6df..fec9333 100644 --- a/Use-Splat.ps1 +++ b/Use-Splat.ps1 @@ -23,6 +23,7 @@ @{LogName='Application';InstanceId=10000,10005} | .@ Get-EventLog # get a bunch of different log events #> + [Alias('.@','uSplat')] param( # One or more commands [Parameter(Position=0)] @@ -30,7 +31,7 @@ $Command, # Any additional positional arguments that would be passed to the command - [Parameter(Position=1,ValueFromRemainingArguments=$true)] + [Parameter(Position=1,ValueFromRemainingArguments)] [PSObject[]] $ArgumentList = @(), @@ -57,7 +58,7 @@ $Stream) begin { - $pipelines = @{} + $pipelines = @{} } process { $WeTrustTheSplat = $false diff --git a/docs/Assets/Splatter.png b/docs/Assets/Splatter.png new file mode 100644 index 0000000..fafc917 Binary files /dev/null and b/docs/Assets/Splatter.png differ diff --git a/docs/Assets/Splatter.svg b/docs/Assets/Splatter.svg new file mode 100644 index 0000000..7064c4a --- /dev/null +++ b/docs/Assets/Splatter.svg @@ -0,0 +1,11 @@ + + + + + + + + + spl@tter + + diff --git a/docs/Assets/Splatter@1080p-Animated.svg b/docs/Assets/Splatter@1080p-Animated.svg new file mode 100644 index 0000000..b0a8929 --- /dev/null +++ b/docs/Assets/Splatter@1080p-Animated.svg @@ -0,0 +1,12 @@ + + + + + + + + + spl@tter + + + diff --git a/docs/Assets/Splatter@1080p.svg b/docs/Assets/Splatter@1080p.svg new file mode 100644 index 0000000..2bbd446 --- /dev/null +++ b/docs/Assets/Splatter@1080p.svg @@ -0,0 +1,11 @@ + + + + + + + + + spl@tter + + diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md new file mode 100644 index 0000000..d69f734 --- /dev/null +++ b/docs/CHANGELOG.md @@ -0,0 +1,17 @@ +### 0.5.4: + +* New Splatter Logo (#12) +* All splats will become PSObjects consistently (#13) +* Generated Documentation (#14) +* Declaring aliases inline (#16) + +--- + +### 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/docs/Find-Splat.md b/docs/Find-Splat.md new file mode 100644 index 0000000..2241994 --- /dev/null +++ b/docs/Find-Splat.md @@ -0,0 +1,151 @@ +Find-Splat +---------- + + + + +### Synopsis +Finds commands that can be splatted to given an input. + + + +--- + + +### Description + +Finds the commands whose input parameters match an input object, and returns an [ordered] dictionary of parameters. + + + +--- + + +### Related Links +* [Get-Splat](Get-Splat.md) + + + +* [Use-Splat](Use-Splat.md) + + + + + +--- + + +### Examples +#### EXAMPLE 1 +```PowerShell +@{Id=$pid} | Find-Splat -Global +``` + + + +--- + + +### Parameters +#### **Command** + +One or more commands. +If not provided, commands from the current module will be searched. +If there is no current module, all commands will be searched. + + + + + + +|Type |Required|Position|PipelineInput| +|------------|--------|--------|-------------| +|`[String[]]`|false |1 |false | + + + +#### **Splat** + +The splat + + + + + + +|Type |Required|Position|PipelineInput |Aliases | +|------------|--------|--------|--------------|-----------| +|`[PSObject]`|false |2 |true (ByValue)|InputObject| + + + +#### **Global** + +If set, will look for all commands, even if Find-Splat is used within a module. + + + + + + +|Type |Required|Position|PipelineInput|Aliases| +|----------|--------|--------|-------------|-------| +|`[Switch]`|false |named |false |G | + + + +#### **Local** + +If set, will look for commands within the current module. +To make this work within your own module Install-Splat. + + + + + + +|Type |Required|Position|PipelineInput|Aliases| +|----------|--------|--------|-------------|-------| +|`[Switch]`|false |named |false |L | + + + +#### **Module** + +If provided, will look for commands within any number of loaded modules. + + + + + + +|Type |Required|Position|PipelineInput|Aliases| +|------------|--------|--------|-------------|-------| +|`[String[]]`|false |named |false |M | + + + +#### **Force** + +If set, will return regardless of if parameters map, are valid, and have enough mandatory parameters + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + + + + + +--- + + +### Syntax +```PowerShell +Find-Splat [[-Command] ] [[-Splat] ] [-Global] [-Local] [-Module ] [-Force] [] +``` diff --git a/docs/Get-Splat.md b/docs/Get-Splat.md new file mode 100644 index 0000000..c521f24 --- /dev/null +++ b/docs/Get-Splat.md @@ -0,0 +1,113 @@ +Get-Splat +--------- + + + + +### Synopsis +Gets a splat + + + +--- + + +### Description + +Gets a splat for a command + + + +--- + + +### Related Links +* [Find-Splat](Find-Splat.md) + + + +* [Use-Splat](Use-Splat.md) + + + + + +--- + + +### Examples +#### EXAMPLE 1 +```PowerShell +@{id=$pid} | Get-Splat +``` + +#### EXAMPLE 2 +```PowerShell +@{id=$Pid} | ?@ # ?@ is an alias for Get-Splat +``` + +#### EXAMPLE 3 +```PowerShell +@{id=$pid} | & ${?@} # Get-Splat as a script block +``` + + + +--- + + +### Parameters +#### **Command** + +The command that is being splatted. + + + + + + +|Type |Required|Position|PipelineInput| +|--------------|--------|--------|-------------| +|`[PSObject[]]`|true |1 |false | + + + +#### **Splat** + +The input object + + + + + + +|Type |Required|Position|PipelineInput |Aliases | +|------------|--------|--------|--------------|-----------| +|`[PSObject]`|true |2 |true (ByValue)|InputObject| + + + +#### **Force** + +If set, will return regardless of if parameters map, are valid, and have enough mandatory parameters + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + + + + + +--- + + +### Syntax +```PowerShell +Get-Splat [-Command] [-Splat] [-Force] [] +``` diff --git a/docs/Initialize-Splatter.md b/docs/Initialize-Splatter.md new file mode 100644 index 0000000..fbcdefe --- /dev/null +++ b/docs/Initialize-Splatter.md @@ -0,0 +1,194 @@ +Initialize-Splatter +------------------- + + + + +### Synopsis +Initializes an embeddable version of Splatter + + + +--- + + +### Description + +Initialize-Splatter enables you to embed Splatter into any module. + + + +--- + + +### Related Links +* [Get-Splat](Get-Splat.md) + + + +* [Find-Splat](Find-Splat.md) + + + +* [Use-Splat](Use-Splat.md) + + + +* [Merge-Splat](Merge-Splat.md) + + + + + +--- + + +### Examples +#### EXAMPLE 1 +```PowerShell +'@.ps1' # Initialize Splatter +``` + +#### EXAMPLE 2 +```PowerShell +'@.ps1' +``` + +#### EXAMPLE 3 +```PowerShell +'@.ps1' # Initialize splatter +``` + + + +--- + + +### Parameters +#### **Verb** + +The verbs to install. + + + +Valid Values: + +* Get +* Use +* Find +* Merge +* Out + + + + + + +|Type |Required|Position|PipelineInput | +|------------|--------|--------|---------------------| +|`[String[]]`|false |1 |true (ByPropertyName)| + + + +#### **Compress** + +If set, will not compress the definitions + + + + + + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|-------------| +|`[Switch]`|false |named |true (ByPropertyName)|NoCompression| + + + +#### **Minify** + +If set, will not minify the definitions + + + + + + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|--------------| +|`[Switch]`|false |named |true (ByPropertyName)|NoMinification| + + + +#### **NoLogo** + +If set, will not add a line of documentation linking to the module + + + + + + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[Switch]`|false |named |true (ByPropertyName)| + + + +#### **NoHelp** + +If set, will strip inline help from the commands. + + + + + + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[Switch]`|false |named |true (ByPropertyName)| + + + +#### **AsFunction** + +If set, will define the commands as functions and define aliases. +If you use this, please use the manifest or Export-ModuleMember to hide Splatter's commands. +If not set, Splatter will install as ScriptBlocks (these will not be exported from a module) + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + + + +#### **Inline** + +If set, splatter will be defined inline. +This will not preface Splatter with a param() block and PSScriptAnalyzer suppression messages + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + + + + + +--- + + +### Syntax +```PowerShell +Initialize-Splatter [[-Verb] ] [-Compress] [-Minify] [-NoLogo] [-NoHelp] [-AsFunction] [-Inline] [] +``` diff --git a/docs/Merge-Splat.md b/docs/Merge-Splat.md new file mode 100644 index 0000000..f37ffb7 --- /dev/null +++ b/docs/Merge-Splat.md @@ -0,0 +1,226 @@ +Merge-Splat +----------- + + + + +### Synopsis +Merges one or more splats + + + +--- + + +### Description + +Merges one or more hashtables and property bags into one [ordered] hashtable. +Allows you to -Remove specific keys from any object +Allows you to -Include or -Exclude wildcards of keys (or patterns, with -RegularExpression) +Allows you to -Map additional values if a value if found. + + + +--- + + +### Related Links +* [Get-Splat](Get-Splat.md) + + + +* [Use-Splat](Use-Splat.md) + + + + + +--- + + +### Examples +#### EXAMPLE 1 +```PowerShell +@{a='b'}, @{c='d'} | Merge-Splat +``` + +#### EXAMPLE 2 +```PowerShell +[PSCustomOBject]@{a='b'}, @{c='d'} | Merge-Splat -Add @{e='f'} -Remove c +``` + +#### EXAMPLE 3 +```PowerShell +@{id=$pid} | + Use-Splat Get-Process | + Merge-Splat -Include Name +``` + +#### EXAMPLE 4 +```PowerShell +@{n=$(Get-Random) } | + Merge-Splat -Map @{ + N = { + if (-not ($_ % 2)) { @{IsEven=$true;IsOdd=$false} } + else { @{IsEven=$false;IsOdd=$true}} + } + } +``` + + + +--- + + +### Parameters +#### **Splat** + +The splat + + + + + + +|Type |Required|Position|PipelineInput |Aliases | +|--------------|--------|--------|--------------|-----------| +|`[PSObject[]]`|false |named |true (ByValue)|InputObject| + + + +#### **Add** + +Splats or objects that will be added to the splat. + + + + + + +|Type |Required|Position|PipelineInput|Aliases | +|--------------|--------|--------|-------------|----------------------| +|`[PSObject[]]`|false |1 |false |With
W
A
+| + + + +#### **Remove** + +The names of the keys to remove from the splat + + + + + + +|Type |Required|Position|PipelineInput|Aliases | +|------------|--------|--------|-------------|---------------------------------| +|`[String[]]`|false |named |false |Delete
Drop
D
R
-| + + + +#### **Include** + +Patterns of names to include in the splat. +If provided, only keys that match at least one -Include pattern will be kept. +By default, these are wildcards, unlesss -RegularExpression is passed. + + + + + + +|Type |Required|Position|PipelineInput|Aliases | +|------------|--------|--------|-------------|--------| +|`[String[]]`|false |named |false |E
EX| + + + +#### **Exclude** + +Patterns of names to exclude from the splat. +By default, these are wildcards, unlesss -RegularExpression is passed. + + + + + + +|Type |Required|Position|PipelineInput|Aliases | +|------------|--------|--------|-------------|--------| +|`[String[]]`|false |named |false |I
IN| + + + +#### **RegularExpression** + +If set, all patterns matched will be assumed to be RegularExpressions, not wildcards + + + + + + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|------------| +|`[Switch]`|false |named |false |Regex
RX| + + + +#### **Map** + +A map of new data to add. The key is the name of the original property. +The value can be any a string, a hashtable, or a script block. +If the value is a string, it will be treated as a key, and the original property will be copied to this key. +If the value is a hashtable, it will add the values contained in Map. +If the value is a ScriptBlock, it will combine the output of this script block with the splat. + + + + + + +|Type |Required|Position|PipelineInput|Aliases | +|---------------|--------|--------|-------------|-----------| +|`[IDictionary]`|false |named |false |M
ReMap| + + + +#### **Keep** + +If set, will keep existing values in a splat instead of adding it to a list of values. + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + + + +#### **Replace** + +If set, will replace existing values in a splat instead of adding it to a list of values. + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + + + + + +--- + + +### Syntax +```PowerShell +Merge-Splat [-Splat ] [[-Add] ] [-Remove ] [-Include ] [-Exclude ] [-RegularExpression] [-Map ] [-Keep] [-Replace] [] +``` diff --git a/docs/Out-Splat.md b/docs/Out-Splat.md new file mode 100644 index 0000000..7daf7f3 --- /dev/null +++ b/docs/Out-Splat.md @@ -0,0 +1,517 @@ +Out-Splat +--------- + + + + +### Synopsis +Outputs code that splats + + + +--- + + +### Description + +Outputs a function or script that primarily calls another command. This can get messy to write by hand. + + + +--- + + +### Related Links +* [Initialize-Splatter](Initialize-Splatter.md) + + + + + +--- + + +### Examples +#### EXAMPLE 1 +```PowerShell +Out-Splat -CommandName Get-Command +``` + +#### EXAMPLE 2 +```PowerShell +Out-Splat -FunctionName Get-MyProcess -Example Get-MyProcess -CommandName Get-Process -DefaultParameter @{ + Id = '$pid' +} -ExcludeParameter * +``` + + + +--- + + +### Parameters +#### **CommandName** + +The name of the command that will be splatted + + + + + + +|Type |Required|Position|PipelineInput |Aliases| +|----------|--------|--------|---------------------|-------| +|`[String]`|true |1 |true (ByPropertyName)|Name | + + + +#### **DefaultParameter** + +A hashtable of default parameters. These will always be passed to the underlying command by name. + + + + + + +|Type |Required|Position|PipelineInput |Aliases | +|-------------|--------|--------|---------------------|-----------------| +|`[Hashtable]`|false |2 |true (ByPropertyName)|DefaultParameters| + + + +#### **ArgumentList** + +A list of arguments. These will be always be passed to the underlying commands by position. +Items starting with $ will be treated as a variable. + + + + + + +|Type |Required|Position|PipelineInput | +|------------|--------|--------|---------------------| +|`[String[]]`|false |named |true (ByPropertyName)| + + + +#### **InputParameter** + +A list of parameters names that will be inputted from the original command into the splat. +If generating a function, these parameter declarations will be copied from the underlying command. +Help for these parameters will be included as comment-based help + + + + + + +|Type |Required|Position|PipelineInput |Aliases | +|------------|--------|--------|---------------------|----------------| +|`[String[]]`|false |4 |true (ByPropertyName)|IncludeParameter| + + + +#### **ExcludeParameter** + +A list of parameters that will be excluded from the original function. +This is only valid when generating a function. +Wildcards may be used. + + + + + + +|Type |Required|Position|PipelineInput | +|------------|--------|--------|---------------------| +|`[String[]]`|false |5 |true (ByPropertyName)| + + + +#### **DefaultOverride** + +If set, values from input parameters will override default values. + + + + + + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|---------------------------------------------------------| +|`[Switch]`|false |named |true (ByPropertyName)|OverrideDefault
OverwriteDefault
DefaultOverwrite| + + + +#### **VariableInput** + +If set, any variable with a non-null value matching the input parameters will be used to splat. +If not set, only bound parameters will be used to splat. +If no function name is provided, this will automatically be set + + + + + + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[Switch]`|false |named |true (ByPropertyName)| + + + +#### **VariableName** + +The name of the variable used to hold the splatted parameters. By default, ${CommandName}Parameters (e.g. GetHelpP + + + + + + +|Type |Required|Position|PipelineInput |Aliases | +|----------|--------|--------|---------------------|---------| +|`[String]`|false |named |true (ByPropertyName)|SplatName| + + + +#### **FunctionName** + +An optional name of a generated function. +If provided, this function will declare any input parameters specified in -InputParameter + + + + + + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[String]`|true |3 |true (ByPropertyName)| + + + +#### **Synopsis** + +The synopsis. +This is used to make comment-based help in a generated function. +By default, it is : "Wraps $CommandName" + + + + + + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[String]`|false |named |true (ByPropertyName)| + + + +#### **Description** + +The description. +This is used to make comment-based help in a generated function. + + + + + + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[String]`|false |named |true (ByPropertyName)| + + + +#### **Example** + +One or more examples. +This is used to make comment-based help in a generated function. + + + + + + +|Type |Required|Position|PipelineInput |Aliases | +|------------|--------|--------|---------------------|--------| +|`[String[]]`|false |named |true (ByPropertyName)|Examples| + + + +#### **Link** + +One or more links. +This is used to make comment-based help in a generated function. + + + + + + +|Type |Required|Position|PipelineInput |Aliases| +|------------|--------|--------|---------------------|-------| +|`[String[]]`|false |named |true (ByPropertyName)|Links | + + + +#### **Note** + +Some notes. +This is used to make comment-based help in a generated function. + + + + + + +|Type |Required|Position|PipelineInput |Aliases| +|----------|--------|--------|---------------------|-------| +|`[String]`|false |named |true (ByPropertyName)|Notes | + + + +#### **CmdletBinding** + +The CmdletBinding attribute for a new function + + + + + + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[String]`|false |named |true (ByPropertyName)| + + + +#### **OutputType** + +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] + + + + + + +|Type |Required|Position|PipelineInput | +|------------|--------|--------|---------------------| +|`[String[]]`|false |named |true (ByPropertyName)| + + + +#### **AdditionalParameter** + +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. + + + + + + +|Type |Required|Position|PipelineInput | +|-------------|--------|--------|---------------------| +|`[Hashtable]`|false |named |true (ByPropertyName)| + + + +#### **SerializationDepth** + +The serialization depth for default parameters. By default, 2. + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[UInt32]`|false |named |false | + + + +#### **DynamicParameter** + +If set, will generate the code to collect the -CommandName input as dynamic parameters. + + + + + + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|-----------------| +|`[Switch]`|true |named |false |DynamicParameters| + + + +#### **Unpiped** + +If set, will not allow dynamic parameters to use ValueFromPipeline or ValueFromPipelineByPropertyName + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + + + +#### **Offset** + +If provided, will offset the position of any positional parameters. + + + + + + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[Int32]`|false |named |false | + + + +#### **NewParameterSetName** + +If provided, dynamic parameters will be created in a new parameter set, named $NewParameterSetName. + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |false | + + + +#### **CrossStream** + +If set, will cross errors into the output stream. +You SHOULD cross the streams when dealing with console applications, as many of them like to return output on standard error. + + + + + + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|---------------------| +|`[Switch]`|false |named |true (ByPropertyName)| + + + +#### **Where** + +A script block used to filter the results + + + + + + +|Type |Required|Position|PipelineInput| +|---------------|--------|--------|-------------| +|`[ScriptBlock]`|false |named |false | + + + +#### **Begin** + +A script to run before the splatter starts + + + + + + +|Type |Required|Position|PipelineInput| +|---------------|--------|--------|-------------| +|`[ScriptBlock]`|false |named |false | + + + +#### **Process** + +A script to run on each splatter result + + + + + + +|Type |Required|Position|PipelineInput| +|---------------|--------|--------|-------------| +|`[ScriptBlock]`|false |named |false | + + + +#### **End** + +A script to run after the splat is over + + + + + + +|Type |Required|Position|PipelineInput| +|---------------|--------|--------|-------------| +|`[ScriptBlock]`|false |named |false | + + + +#### **PipeTo** + +If provided, will pipe directly into the contents of this script block. +This assumes that the first item in the script block is a command, and it will accept the output of the splat as pipelined input + + + + + + +|Type |Required|Position|PipelineInput|Aliases | +|---------------|--------|--------|-------------|-----------------| +|`[ScriptBlock]`|false |named |false |PipeInto
Pipe| + + + + + +--- + + +### Outputs +* [Management.Automation.ScriptBlock](https://learn.microsoft.com/en-us/dotnet/api/System.Management.Automation.ScriptBlock) + + + + + + +--- + + +### Syntax +```PowerShell +Out-Splat [-CommandName] [[-DefaultParameter] ] [-ArgumentList ] [[-InputParameter] ] [[-ExcludeParameter] ] [-DefaultOverride] [-VariableInput] [-VariableName ] [-SerializationDepth ] [-CrossStream] [-Where ] [-Begin ] [-Process ] [-End ] [-PipeTo ] [] +``` +```PowerShell +Out-Splat [-CommandName] [[-DefaultParameter] ] [-ArgumentList ] [[-InputParameter] ] [[-ExcludeParameter] ] [-DefaultOverride] [-VariableInput] [-VariableName ] [-FunctionName] [-Synopsis ] [-Description ] [-Example ] [-Link ] [-Note ] [-CmdletBinding ] [-OutputType ] [-AdditionalParameter ] [-SerializationDepth ] [-CrossStream] [-Where ] [-Begin ] [-Process ] [-End ] [-PipeTo ] [] +``` +```PowerShell +Out-Splat [-CommandName] [[-DefaultParameter] ] [-ArgumentList ] [[-InputParameter] ] [[-ExcludeParameter] ] [-DefaultOverride] [-VariableInput] [-VariableName ] [-SerializationDepth ] -DynamicParameter [-Unpiped] [-Offset ] [-NewParameterSetName ] [-CrossStream] [-Where ] [-Begin ] [-Process ] [-End ] [-PipeTo ] [] +``` diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..51165c1 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,202 @@ +
+ +

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. + +Splatter makes splatting more powerful, flexible, and easy to use. + +With Splatter you can: +* Splat any object to any command +* Pipe splats to commands +* Validate splats +* Find commands for a splat + +Splatter is tiny, and can be easily embedded into any module, or used to generate splatting code. + +### Using Splatter + +Splatter has four core commands: +* Get-Splat (?@) +* Find-Splat (??@) +* Merge-Splat (*@) +* Use-Splat (.@) + +#### Get-Splat + +| Alias | Variables | +|-----------|----------------------------| +| ?@,gSplat | ${?@}, $gSplat, $GetSplat | + + +Get-Splat returns a Dictionary of parameters, given a command or ScriptBlock. +This only contains parameters for the command, and converts the parameters into the desired types. +Get-Splat can take any object or Dictionary as input. + + @{Id=$pid;Junk='Data'} | Get-Splat Get-Process + # -or + @{Id=$pid;Junk='Data'} | ?@ gps + # -or + @{Id=$pid;Junk='Data'} | & ${?@} gps + + +Get-Splat can take more than one command as input. +If it does, it will return the matching inputs for each command. + + @{FilePath = 'pwsh';ArgumentList = '-noprofile';PassThru=$true} | + Use-Splat Start-Process | + Add-Member NoteProperty TimeOut 15 -PassThru | + Get-Splat Wait-Process, Stop-Process + +Get-Splat will also attach a properties to the Dictionary. + +These property won't be used when calling the splat, but can be peeked at: + +| Property | Description | +|---------------|-----------------------------------------------| +| Command | The Command | +| CouldRun | If the command could run, given the splat | +| Invalid | Parameters that are invalid | +| Missing | Mandatory parameters that are missing | +| PercentFit | % of properties that map to parameters | +| Unmapped | Properties that don't map to parameters | + + + $splat = @{id=$pid;foo='bar'} | ?@ gps + $splat.Command, $splat.PercentFit, $splat.Unmapped + + +#### Find-Splat +| Alias | Variables | +|------------|----------------------------| +| ??@,fSplat | ${??@}, $fSplat, $FindSplat| + + + +Find-Splat will find commands that match a given splat, and return information about a match. + + @{id=$pid} | Find-Splat *-Process + +Find-Splat may also be scoped to a given module + + @{splat=@{}} | Find-Splat -Module Splatter + +#### Merge-Splat +| Alias | Variables | +|------------|----------------------------| +| *@,mSplat | ${*@}, $mSplat, $MergeSplat| + + +Merge splat will merge multiple splats together. + + @{a='b'}, @{c='d'} | Merge-Splat + + +#### Use-Splat + +| Alias | Variables | +|--------------|----------------------------------| +| .@,uSplat | ${.@}, $uSplat, $UseSplat | + + +Use-Splat will run a splat against one or more commands. + @{id=$pid} | Use-Splat Get-Process # Gets the current process + + # Gets the process, and then doesn't stop the process because Stop-Process is passed WhatIf + @{id=$pid;WhatIf=$true} | .@ Get-Process,Stop-Process + + +### Using Splatter with ScriptBlocks + + +In PowerShell, you can treat any ScriptBlock as a command. Splatter makes this simpler. + +Take this example, which takes a little bit of input data and uses it in a few different scripts. + + @{ + Name='James' + Birthday = '12/17/1981' + City = 'Seattle' + State = 'Washington' + } | .@ { + param($Name) + "$name" + }, { + param([DateTime]$Birthday) + $ageTimespan = [DateTime]::Now - $birthday + "Age:" + [Math]::Floor($ageTimespan.TotalDays / 365) + + }, { + param($city, $state) + "$city, $state" + } + +Since Splatter will also convert objects to hashtables, you could also write something like: + + Import-Csv .\People.csv | .@ + { + param($Name) + "$name" + }, { + param([DateTime]$Birthday) + $ageTimespan = [DateTime]::Now - $birthday + "Age:" + [Math]::Floor($ageTimespan.TotalDays / 365) + + } + + +### Embedding Splatter + +Initialize-Splat will output a script containing the core commands for Splatter. +Using this output, you can directly embed Splatter into any script or module. + + Initialize-Splatter + +To install this into a module: + + Get-Module TheNameOfYourModule | Split-Path | Push-Location + Initialize-Splatter > '@.ps1' + Pop-Location + +Then add the following line to your module: + + . $psScriptRoot\@.ps1 + +By default, when Splatter is embedded, it will not export functions or aliases, and you will need to use the variable syntax: + + & ${?@} # Get-Splat + & ${??@} # Find-Splat + & ${*@} # Merge-Splat + & ${.@} # Use-Splat + +You can override this by using -AsFunction + + Initialize-Splatter -AsFunction + +If you don't need all of the commands, you can use -Verb + + Initialize-Splatter -Verb Get, Use + + + +### Generating Splatting Code + +You can use Out-Splat to generate code that splats. + + 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-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)) + + Get-SplatterAlias | Out-String + Get-Help Get-SplatterAlias | Out-String diff --git a/docs/Use-Splat.md b/docs/Use-Splat.md new file mode 100644 index 0000000..e33cc01 --- /dev/null +++ b/docs/Use-Splat.md @@ -0,0 +1,170 @@ +Use-Splat +--------- + + + + +### Synopsis +Uses a splat. + + + +--- + + +### Description + +Uses a splat to call a command. +If passed from Find-Splat,Get-Splat or Test-Splat, the command will be automatically detected. +If called as .@, this will run only provided commands +If called as *@, this will run any found commands + + + +--- + + +### Related Links +* [Get-Splat](Get-Splat.md) + + + +* [Find-Splat](Find-Splat.md) + + + +* [Test-Splat](Test-Splat.md) + + + + + +--- + + +### Examples +#### EXAMPLE 1 +```PowerShell +@{id=$pid} | Use-Splat gps # When calling Use-Splat is globally imported +``` + +#### EXAMPLE 2 +```PowerShell +@{id=$pid} | & ${.@} gps # When calling Use-Splat is nested +``` + +#### EXAMPLE 3 +```PowerShell +@{LogName='System';InstanceId=43,44}, +@{LogName='Application';InstanceId=10000,10005} | + .@ Get-EventLog # get a bunch of different log events +``` + + + +--- + + +### Parameters +#### **Command** + +One or more commands + + + + + + +|Type |Required|Position|PipelineInput| +|--------------|--------|--------|-------------| +|`[PSObject[]]`|false |1 |false | + + + +#### **ArgumentList** + +Any additional positional arguments that would be passed to the command + + + + + + +|Type |Required|Position|PipelineInput| +|--------------|--------|--------|-------------| +|`[PSObject[]]`|false |2 |false | + + + +#### **Splat** + +The splat + + + + + + +|Type |Required|Position|PipelineInput | +|--------------|--------|--------|--------------| +|`[PSObject[]]`|false |named |true (ByValue)| + + + +#### **Force** + +If set, will run regardless of if parameters map, are valid, and have enough mandatory parameters. + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + + + +#### **Best** + +If set, will run the best fit out of multiple commands. +The best fit is the command that will use the most of the input splat. + + + + + + +|Type |Required|Position|PipelineInput|Aliases | +|----------|--------|--------|-------------|------------------------------------------| +|`[Switch]`|false |named |false |BestFit
BestFitFunction
BF
BFF| + + + +#### **Stream** + +If set, will stream input into a single pipeline of each command. +The non-pipeable parameters of the first input splat will be used to start the pipeline. +By default, a command will be run once per input splat. + + + + + + +|Type |Required|Position|PipelineInput|Aliases| +|----------|--------|--------|-------------|-------| +|`[Switch]`|false |named |false |Pipe | + + + + + +--- + + +### Syntax +```PowerShell +Use-Splat [[-Command] ] [[-ArgumentList] ] [-Splat ] [-Force] [-Best] [-Stream] [] +```