Get-PSDevOps.ps1
function Get-PSDevOps { <# .Synopsis Gets PSDevOps commands. .Description Gets PSDevOps commands. PSDevOps commands are self-contained scripts that complete end-to-end scenarios. They are traditionally named with the patterns *.*.ps1 or *.*.*.ps1. For example: *.psdevops.ps1 files are used to run commands in PSDevOps. *.GitHubAction.PSDevOps.ps1 would indicate creating a GitHubAction. *.tests.ps1 files are used by Pester *.ezout|ezformat|format|view.ps1 files are used by EZOut To name a few examples of where the technique is used. Using Get-PSDevOps will return extended command information and addtional methods. .Example Get-PSDevOps # Get *.*.ps1 commands in the current directory .Example Get-Module PSDevops | Get-PSDevOps # Gets related commands #> param( # The name of the script. [Parameter(ValueFromPipelineByPropertyName)] [string] $Name, # One or more paths to scripts. # If these paths resolve to directories, all files that match \.(?<Type>.+)\.ps1$ # If the paths resolve to scripts or commands [Parameter(ValueFromPipelineByPropertyName)] [Alias('Fullname')] [string[]] $ScriptPath, # One or more modules. This can be passed via the pipeline, for example: # Get-Module PSDevOps | Get-PSDevOps [Parameter(ValueFromPipelineByPropertyName,ValueFromPipeline)] [PSModuleInfo[]] $ModuleInfo, # The Regular Expression Pattern used to search for files. # If a -Pattern is provided, named capture groups in that pattern will become noteproperties of the output object. # By default: # (?<ScriptSubtype>\.\w+.)?\.(?<ScriptType>\w+)\.ps1$ # This roughly translates as: # Any *.*.ps1 file # The Named Capture 'Type' the type of .ps1 # The Optional Named Capture, Subtype, will match an additional '.Something' [string] $Pattern = "(?<ScriptSubtype>\.\w+.)?\.(?<ScriptType>\w+)\.ps1$", # If set, will search directories recursively. [switch] $Recurse ) begin { $regexPattern = [Regex]::new($Pattern, 'IgnoreCase,IgnorePatternWhitespace') $myModuleName = $MyInvocation.MyCommand.Module.Name if (-not $script:KnownDevOpsScriptTypes) { $script:KnownDevOpsScriptTypes = @{} foreach ($loadedModule in Get-Module) { $moduleData = $loadedModule.PrivateData.$myModuleName if (-not $moduleData) { continue } if ($moduleData.ScriptType -isnot [Hashtable]) { continue } foreach ($kv in $moduleData.ScriptType.GetEnumerator()) { $script:KnownDevOpsScriptTypes[$kv.Key] = $kv.Value } } } } process { $MyModuleName = $MyInvocation.MyCommand.Module.Name if (-not $ModuleInfo -and -not $ScriptPath) { $ModuleInfo = @(foreach ($mod in Get-Module) { if ($MyModuleName -and ( ($mod.PrivateData.PSData.Tags -contains $MyModuleName) -or $mod.PrivateData.$MyModuleName )) { $mod } }) } if ($ModuleInfo) { $ScriptPath += try { $ModuleInfo | Split-Path -ErrorAction SilentlyContinue } catch { Write-Verbose "Could not Split-Path for $($ModuleInfo)" } } if (-not $ScriptPath) { $ScriptPath += "$pwd" } $ScriptPath = $ScriptPath | Select-Object -Unique foreach ($sp in $ScriptPath) { $resolvedPath = if ([IO.File]::Exists($sp)) { $sp } else { $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($sp) # Resolve the path } if (-not $resolvedPath) { continue } # ( if we couldn't, keep moving). # Since the path exists, it's either a file or a directory. if ([IO.File]::Exists($resolvedPath)) { # If it's a file $resolvedPathCmd = # get it as a command, $ExecutionContext.SessionState.InvokeCommand.GetCommand("$resolvedPath", 'All') $resolvedPathCmd.pstypenames.clear() $resolvedPathCmd.pstypenames.add('PSDevOps') # then decorate that command as 'PSDevOps', $resolvedPathCmd # output, continue # and continue. } # If it's a directory, recurse matters. Also, .NET will be faster than PowerShell providers $dir = [IO.DirectoryInfo]"$resolvedPath" foreach ($fsi in $dir.GetFileSystemInfos("*", [IO.SearchOption][int][bool]$Recurse)) { $matched = $regexPattern.Match($fsi.Name) if (-not $matched.Success) { continue } $resolvedPathCmd = # get it as a command, $ExecutionContext.SessionState.InvokeCommand.GetCommand($fsi.fullname, 'All') $resolvedPathCmd.pstypenames.clear() $resolvedPathCmd.pstypenames.add('PSDevOps') # then decorate that command as 'PSDevOps', $resolvedPathCmd.pstypenames.add('PSDevOps.Script') foreach ($group in $matched.Groups[1..$($matched.Groups.Count -1)]) { $resolvedPathCmd.psobject.properties.add([PSNoteProperty]::new($group.Name, $group.Value)) } if ($resolvedPathCmd.ScriptType) { $resolvedPathCmd.psobject.properties.add( [PSNoteProperty]::new("ScriptTypeDescription", $script:KnownDevOpsScriptTypes[$resolvedPathCmd.ScriptType] )) } $resolvedPathCmd } } } } |