diff --git a/Formatting/PSDevOps.format.ps1 b/Formatting/PSDevOps.format.ps1 new file mode 100644 index 00000000..53e32aaf --- /dev/null +++ b/Formatting/PSDevOps.format.ps1 @@ -0,0 +1,18 @@ +Write-FormatView -TypeName PSDevOps -Property Name, ScriptType, Synopsis, Parameters -VirtualProperty @{ + Parameters = { + @(foreach ($kv in ([Management.Automation.CommandMetaData]$_).Parameters.GetEnumerator()) { + @( + . $setOutputStyle -ForegroundColor Verbose + "[$($kv.Value.ParameterType)]" + . $clearOutputStyle + . $setOutputStyle -ForegroundColor Warning + "`$$($kv.Key)" + . $clearOutputStyle + ) -join '' + }) -join [Environment]::NewLine + } +} -Wrap -ColorProperty @{ + "Name" = {"Success"} +} + +Write-FormatView -TypeName PSDevOps -Property Name, Description -Width 30, 0 -Wrap -Name Description \ No newline at end of file diff --git a/Get-PSDevOps.ps1 b/Get-PSDevOps.ps1 new file mode 100644 index 00000000..54838e12 --- /dev/null +++ b/Get-PSDevOps.ps1 @@ -0,0 +1,144 @@ +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 \.(?.+)\.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: + # (?\.\w+.)?\.(?\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 = "(?\.\w+.)?\.(?\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 + } + } + } +} diff --git a/README.md b/README.md index 883498ed..3bf64397 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,32 @@ PowerShell Tools for DevOps __PSDevOps helps you automate DevOps using PowerShell.__ -Using PSDevOps, you can: +### What is PSDevOps? + +PSDevOps is a PowerShell module that makes it easier to automate DevOps with PowerShell. * [Automate Azure DevOps](#Automate-Azure-DevOps) * [Creating Complex Pipelines](#Creating-Complex-Pipelines) +* [Dealing with DevOps](#Dealing-with-DevOps) +* [Get the GitHub API](#PSDevOps-GitHub-API) +* [Write GitHub Actions](#Write-GitHub-Actions) * [Write GitHub Workflows](#Write-GitHub-Workflows) +#### What do you mean 'Easier to Automate'? + +If you're familiar with PowerShell, you might know about the Object Pipeline. +This allows you to pass objects together, instead of parsing text at each step. +While this is great, not many PowerShell commands or REST apis take full advantage of this feature. + +PSDevOps does. + +Almost every command in PSDevOps is pipeline-aware. +Related commands can often be piped together, and command results can often be piped back into a command to return additional information. + +Additionally, PSDevOps output is often 'decorated' with extended type information. + +This allows the PowerShell types and formatting engine to extend the return object and customize it's display. + ### Automate Azure DevOps The Azure DevOps REST API can be challenging to work with, and hard to remember. @@ -54,6 +74,21 @@ Get-ADOAgentPool | # Gets agent pools from the organization ~~~ +#### Invoke-ADORESTApi + +In orer to ensure that you can always work with Azure DevOps, even if there isn't already a function in PSDevOps, there's Invoke-ADORestAPI. + +Invoke-ADORestAPI can be used like Invoke-RESTMethod, and also has a number of additional features. + +For instance: + +* -AsJob (Launch long-running queries as a job) +* -Body (autoconverted to JSON) +* -ExpandProperty (Expand out a particular property from output) +* -PSTypeName (apply decoration to output) +* -UrlParameter (Replace parameters within a URL) +* -QueryParameter (Pass parameters within a querystring) + #### Help Like any good PowerShell module, PSDevOps has help. Run Get-Help on any command to learn more about how to use it. @@ -147,6 +182,73 @@ PSDevOps makes this much nicer by abstracting away this ugliness into easy-to-us * Write-ADOWarning +### Dealing with DevOps + + +DevOps is a hybrid discipline combining the skillset of Devolopers and Operations. +With DevOps, the focus is on automation, and PowerShell is often the language of choice. + +By convention, most developers write their scripts according to a psuedostandard: + +> ```*-*.ps1``` Scripts containing a function +> ```*.*.ps1``` 'Special' Scripts, often used by particular modules +> ```*.ps1``` Simple scripts that are run interactively. + + +PSDevOps has a command, Get-PSDevOps, that helps to identify these scripts and their requirements. + +~~~PowerShell +Get-Module PSDevOps | Get-PSDevOps +~~~ + + +### PSDevOps GitHub API + +PSDevOps also provides a few functions to work with the GitHub API. + +* Connect/Disconnect-GitHub +* Invoke-GitHubRESTAPI + +Invoke-GitHubRESTAPI works like Invoke-ADORestAPI, as a general-purpose wrapper for GitHub REST API calls. + +It also has a number of additional features, for example: + +* -AsJob (Launch long-running queries as a job) +* -Body (autoconverted to JSON) +* -ExpandProperty (Expand out a particular property from output) +* -PSTypeName (apply decoration to output) +* -UrlParameter (Replace parameters within a URL) +* -QueryParameter (Pass parameters within a querystring) + +Because GitHub's REST api is predictable and exposed with OpenAPI, Invoke-GitHubRESTAPI also enables two very interesting scenarios: + +1. Connect-GitHub can automatically create a shortcut for every endpoint in the GitHub API +2. Invoke-GitHubRESTAPI can automatically decorate return values more apporopriately. + +This means that PSDevOps can integrate with GitHub's REST API with a very small amount of code, and easily customize how GitHub output displays and works in PowerShell. + +### Write GitHub Actions + +You can automatically generate GitHub actions off of any PowerShell script or command. + +First, create a /GitHub/Actions folder in your module directory, then put one or more .ps1 files in it. + +Then, +~~~PowerShell +Import-BuildStep -ModuleName YourModule +~~~ + +Then, you can generate your action.yml with some code like this. + +~~~PowerShell +New-GitHubAction -Name "Name Of Action" -Description 'Action Description' -Action MyAction -Icon minimize -ActionOutput ([Ordered]@{ + SomeOutput = [Ordered]@{ + description = "Some Output" + value = '${{steps.MyAction.outputs.SomeOutput}}' + } +}) +~~~ + ### Write GitHub Workflows You can use PSDevOps to write complex Github Workflows using the same techniques as Azure DevOps pipelines: diff --git a/en-us/About_PSDevOps.help.txt b/en-us/About_PSDevOps.help.txt index 883498ed..3bf64397 100644 --- a/en-us/About_PSDevOps.help.txt +++ b/en-us/About_PSDevOps.help.txt @@ -7,12 +7,32 @@ PowerShell Tools for DevOps __PSDevOps helps you automate DevOps using PowerShell.__ -Using PSDevOps, you can: +### What is PSDevOps? + +PSDevOps is a PowerShell module that makes it easier to automate DevOps with PowerShell. * [Automate Azure DevOps](#Automate-Azure-DevOps) * [Creating Complex Pipelines](#Creating-Complex-Pipelines) +* [Dealing with DevOps](#Dealing-with-DevOps) +* [Get the GitHub API](#PSDevOps-GitHub-API) +* [Write GitHub Actions](#Write-GitHub-Actions) * [Write GitHub Workflows](#Write-GitHub-Workflows) +#### What do you mean 'Easier to Automate'? + +If you're familiar with PowerShell, you might know about the Object Pipeline. +This allows you to pass objects together, instead of parsing text at each step. +While this is great, not many PowerShell commands or REST apis take full advantage of this feature. + +PSDevOps does. + +Almost every command in PSDevOps is pipeline-aware. +Related commands can often be piped together, and command results can often be piped back into a command to return additional information. + +Additionally, PSDevOps output is often 'decorated' with extended type information. + +This allows the PowerShell types and formatting engine to extend the return object and customize it's display. + ### Automate Azure DevOps The Azure DevOps REST API can be challenging to work with, and hard to remember. @@ -54,6 +74,21 @@ Get-ADOAgentPool | # Gets agent pools from the organization ~~~ +#### Invoke-ADORESTApi + +In orer to ensure that you can always work with Azure DevOps, even if there isn't already a function in PSDevOps, there's Invoke-ADORestAPI. + +Invoke-ADORestAPI can be used like Invoke-RESTMethod, and also has a number of additional features. + +For instance: + +* -AsJob (Launch long-running queries as a job) +* -Body (autoconverted to JSON) +* -ExpandProperty (Expand out a particular property from output) +* -PSTypeName (apply decoration to output) +* -UrlParameter (Replace parameters within a URL) +* -QueryParameter (Pass parameters within a querystring) + #### Help Like any good PowerShell module, PSDevOps has help. Run Get-Help on any command to learn more about how to use it. @@ -147,6 +182,73 @@ PSDevOps makes this much nicer by abstracting away this ugliness into easy-to-us * Write-ADOWarning +### Dealing with DevOps + + +DevOps is a hybrid discipline combining the skillset of Devolopers and Operations. +With DevOps, the focus is on automation, and PowerShell is often the language of choice. + +By convention, most developers write their scripts according to a psuedostandard: + +> ```*-*.ps1``` Scripts containing a function +> ```*.*.ps1``` 'Special' Scripts, often used by particular modules +> ```*.ps1``` Simple scripts that are run interactively. + + +PSDevOps has a command, Get-PSDevOps, that helps to identify these scripts and their requirements. + +~~~PowerShell +Get-Module PSDevOps | Get-PSDevOps +~~~ + + +### PSDevOps GitHub API + +PSDevOps also provides a few functions to work with the GitHub API. + +* Connect/Disconnect-GitHub +* Invoke-GitHubRESTAPI + +Invoke-GitHubRESTAPI works like Invoke-ADORestAPI, as a general-purpose wrapper for GitHub REST API calls. + +It also has a number of additional features, for example: + +* -AsJob (Launch long-running queries as a job) +* -Body (autoconverted to JSON) +* -ExpandProperty (Expand out a particular property from output) +* -PSTypeName (apply decoration to output) +* -UrlParameter (Replace parameters within a URL) +* -QueryParameter (Pass parameters within a querystring) + +Because GitHub's REST api is predictable and exposed with OpenAPI, Invoke-GitHubRESTAPI also enables two very interesting scenarios: + +1. Connect-GitHub can automatically create a shortcut for every endpoint in the GitHub API +2. Invoke-GitHubRESTAPI can automatically decorate return values more apporopriately. + +This means that PSDevOps can integrate with GitHub's REST API with a very small amount of code, and easily customize how GitHub output displays and works in PowerShell. + +### Write GitHub Actions + +You can automatically generate GitHub actions off of any PowerShell script or command. + +First, create a /GitHub/Actions folder in your module directory, then put one or more .ps1 files in it. + +Then, +~~~PowerShell +Import-BuildStep -ModuleName YourModule +~~~ + +Then, you can generate your action.yml with some code like this. + +~~~PowerShell +New-GitHubAction -Name "Name Of Action" -Description 'Action Description' -Action MyAction -Icon minimize -ActionOutput ([Ordered]@{ + SomeOutput = [Ordered]@{ + description = "Some Output" + value = '${{steps.MyAction.outputs.SomeOutput}}' + } +}) +~~~ + ### Write GitHub Workflows You can use PSDevOps to write complex Github Workflows using the same techniques as Azure DevOps pipelines: