Skip to content

Commit

Permalink
Merge pull request StartAutomating#76 from StartAutomating/PSDevOpsAn…
Browse files Browse the repository at this point in the history
…dDocumentation

Adding Get-PSDevOps and Documentation
  • Loading branch information
StartAutomating authored Apr 13, 2021
2 parents bd55a75 + 89f32fd commit 19e169b
Show file tree
Hide file tree
Showing 4 changed files with 368 additions and 2 deletions.
18 changes: 18 additions & 0 deletions Formatting/PSDevOps.format.ps1
Original file line number Diff line number Diff line change
@@ -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
144 changes: 144 additions & 0 deletions Get-PSDevOps.ps1
Original file line number Diff line number Diff line change
@@ -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 \.(?<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
}
}
}
}
104 changes: 103 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down
Loading

0 comments on commit 19e169b

Please sign in to comment.