functions/public.ps1
#These are public functions for the PSReleaseTools module Function Get-PSReleaseCurrent { [cmdletbinding()] [OutputType("PSCustomObject")] Param( [Parameter(HelpMessage = "Get the latest preview release")] [switch]$Preview ) Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting: $($MyInvocation.Mycommand)" } #begin Process { $data = GetData @PSBoundParameters #get the local version from the GitCommitID on v6 platforms #or PSVersion table for everything else if ($PSVersionTable.ContainsKey("GitCommitID")) { $local = $PSVersionTable.GitCommitID } else { $Local = $PSVersionTable.PSVersion } if ($data.tag_name) { [pscustomobject]@{ Name = $data.name Version = $data.tag_name Released = $($data.published_at -as [datetime]) LocalVersion = $local } } } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending: $($MyInvocation.Mycommand)" } #end } Function Get-PSReleaseSummary { [cmdletbinding(DefaultParameterSetName = "default")] [OutputType([System.String[]])] Param( [Parameter(HelpMessage = "Display as a markdown document", ParameterSetName = "md")] [switch]$AsMarkdown, [Parameter(ParameterSetName = "md")] [Parameter(ParameterSetName = "online")] [Parameter(ParameterSetName = "default")] [Parameter(HelpMessage = "Get the latest preview release")] [switch]$Preview ) DynamicParam { if ($IsWindows -OR $PSEdition -eq 'Desktop') { #define a parameter attribute object $attributes = New-Object System.Management.Automation.ParameterAttribute $attributes.ParameterSetName = "online" $attributes.HelpMessage = "Open GitHub release page in your browser" #define a collection for attributes $attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute] $attributeCollection.Add($attributes) #define the dynamic param $dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Online", [switch], $attributeCollection) #create array of dynamic parameters $paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary $paramDictionary.Add("Online", $dynParam1) #use the array return $paramDictionary } #if } #dynamic parameter Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting: $($MyInvocation.Mycommand)" } #begin Process { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Using parameter set $($pscmdlet.ParameterSetName)" $PSBoundParameters | Out-String | Write-Verbose if ($Preview) { $data = GetData -Preview } else { $data = GetData } if ($PSBoundParameters.ContainsKey("online")) { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Opening $($data.html_url)" Start-Process $data.html_url } else { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Displaying locally" $dl = $data.assets | Select-Object @{Name = "Filename"; Expression = {$_.name}}, @{Name = "Updated"; Expression = {$_.updated_at -as [datetime]}}, @{Name = "SizeMB"; Expression = {$_.size / 1MB -as [int]}} if ($AsMarkdown) { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Formatting as markdown" #create a markdown table from download data $tbl = (($DL | ConvertTo-Csv -notypeInformation -delimiter "|").Replace('"', '') -Replace '^', "|") -replace "$", "|`n" $out = @" # $($data.Name.trim()) $($data.body.trim()) ## Downloads $($tbl[0])|---|---|---| $($tbl[1..$($tbl.count)]) Published: $($data.Published_At -as [datetime]) "@ } else { #create a here string for the details $out = @" ----------------------------------------------------------- $($data.Name) Published: $($data.Published_At -as [datetime]) ----------------------------------------------------------- $($data.body) ------------- | Downloads | ------------- $($DL | Out-String) "@ } #write the string to the pipeline $out } } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending: $($MyInvocation.Mycommand)" } #end } Function Save-PSReleaseAsset { [cmdletbinding(DefaultParameterSetName = "All", SupportsShouldProcess)] [OutputType([System.IO.FileInfo])] Param( [Parameter(Position = 0, HelpMessage = "Where do you want to save the files?")] [ValidateScript( { if (Test-Path $_) { $True } else { Throw "Cannot validate path $_" } })] [string]$Path = ".", [Parameter(ParameterSetName = "All")] [switch]$All, [Parameter(ParameterSetName = "Family", Mandatory, HelpMessage = "Limit results to a given platform. The default is all platforms.")] [ValidateSet("Rhel", "Raspbian", "Ubuntu", "Debian", "Windows", "AppImage", "Arm", "MacOS", "Alpine", "FXDependent", "CentOS", "Linux")] [string[]]$Family, [Parameter(ParameterSetName = "Family", HelpMessage = "Limit results to a given format. The default is all formats.")] [ValidateSet('deb', 'gz', 'msi', 'pkg', 'rpm', 'zip')] [string[]]$Format, [Parameter(ParameterSetName = "file", ValueFromPipeline)] [ValidateNotNullOrEmpty()] [object]$Asset, [switch]$Passthru, [Parameter(HelpMessage = "Get the latest preview release")] [switch]$Preview ) Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting: $($MyInvocation.Mycommand)" } #begin Process { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Using Parameter set $($PSCmdlet.ParameterSetName)" if ($PSCmdlet.ParameterSetName -match "All|Family") { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting latest releases from $uri" Try { $data = Get-PSReleaseAsset -Preview:$Preview -ErrorAction Stop } Catch { Write-Warning $_.exception.message #bail out Return } } Switch ($PSCmdlet.ParameterSetName) { "All" { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Downloading all releases to $Path" foreach ($asset in $data) { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] ...$($Asset.filename) [$($asset.hash)]" $target = Join-Path -Path $path -ChildPath $asset.filename DL -source $asset.url -Destination $Target -hash $asset.hash -passthru:$passthru } } #all "Family" { #download individual release files Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Downloading releases for $($family -join ',')" $assets = @() Foreach ($item in $Family) { Switch ($item) { "Windows" { $assets += $data.where( {$_.filename -match 'win-x\d{2}'})} "Rhel" { $assets += $data.where( {$_.filename -match 'rhel'})} "Raspbian" { $assets += $data.where( {$_.filename -match 'linux-arm'})} "Debian" { $assets += $data.where( {$_.filename -match 'debian'})} "MacOS" { $assets += $data.where( {$_.filename -match 'osx'}) } "CentOS" { $assets += $data.where( {$_.filename -match 'centos'}) } "Linux" { $assets += $data.where( {$_.filename -match 'linux-x64'}) } "Ubuntu" { $assets += $data.where( {$_.filename -match 'ubuntu'})} "Arm" { $assets += $data.where( {$_.filename -match '-arm\d{2}'})} "AppImage" { $assets += $data.where( {$_.filename -match 'appimage'})} "FXDependent" { $assets += $data.where( {$_.filename -match 'fxdependent'})} "Alpine" {$assets += $data.where( {$_.filename -match 'alpine' })} } #Switch if ($PSBoundParameters.ContainsKey("Format")) { $type = $PSBoundParameters["format"] -join "|" Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Limiting download to $type files" $assets = $assets.Where( {$_.filename -match "$type$"}) } foreach ($asset in $Assets) { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] ...$($Asset.filename) [$($asset.hash)]" $target = Join-Path -Path $path -ChildPath $asset.fileName DL -source $asset.url -Destination $Target -hash $asset.hash -passthru:$passthru } #foreach asset } #foreach family name } #Family "File" { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] ...$($asset.filename) [$($asset.hash)]" $target = Join-Path -Path $path -ChildPath $asset.fileName DL -source $asset.url -Destination $Target -hash $asset.hash -passthru:$passthru } #file } #switch parameter set name } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending: $($MyInvocation.Mycommand)" } #end } Function Get-PSReleaseAsset { [cmdletbinding()] [OutputType("PSCustomObject")] Param( [Parameter(HelpMessage = "Limit results to a given platform. The default is all platforms.")] [ValidateSet("Rhel", "Raspbian", "Ubuntu", "Debian", "Windows", "AppImage", "Arm", "MacOS", "Alpine", "FXDependent", "CentOS", "Linux")] [string[]]$Family, [ValidateSet('deb', 'gz', 'msi', 'pkg', 'rpm', 'zip', 'msix')] [Parameter(HelpMessage = "Limit results to a given format. The default is all formats.")] [string[]]$Format, [alias("x64")] [switch]$Only64Bit, [Parameter(HelpMessage = "Get the latest preview release.")] [switch]$Preview, [Parameter(HelpMessage = "Only get LTS release-related assets.")] [switch]$LTS ) Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting: $($MyInvocation.Mycommand)" } #begin Process { Try { if ($Preview) { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting preview assets" $data = GetData -Preview -ErrorAction stop } else { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting normal assets" $data = GetData -ErrorAction stop } #parse out file names and hashes #updated pattern 10 March 2020 to capture LTS assets [regex]$rx = "(?<file>[p|P]ower[s|S]hell((-preview)|(-lts))?[-|_]\d.*)\s+-\s+(?<hash>\w+)" # pre GA pattern #"(?<file>[p|P]ower[s|S]hell(-preview)?[-|_]\d.*)\s+-\s+(?<hash>\w+)" # original regex pattern #"(?<file>[p|P]ower[s|S]hell[-|_]\d.*)\s+-\s+(?<hash>\w+)" $r = $rx.Matches($data.body) $r | ForEach-Object -Begin { $h = @{} } -process { #if there is a duplicate entry, assume it is part of a Note $f = $_.groups["file"].value.trim() $v = $_.groups["hash"].value.trim() if (-not ($h.ContainsKey($f))) { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Adding $f [$v]" $h.add($f, $v ) } else { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Ignoring duplicate asset: $f [$v]" } } Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Found $($data.assets.count) downloads" $assets = $data.assets | Select-Object @{Name = "FileName"; Expression = {$_.Name}}, @{Name = "Family"; Expression = { Switch -regex ($_.name) { "Win-x\d{2}" {"Windows" ; break} "arm\d{2}.zip" {"Arm" ; break} "Ubuntu" {"Ubuntu"; break} "osx" {"MacOS"; break} "debian" {"Debian"; break} "appimage" {"AppImage"; break} "rhel" {"Rhel"; break} "linux-arm" {"Raspbian"; break} "alpine" {"Alpine" ; break} "fxdepend" {"FXDependent"; break} "centos" {"CentOS"; break} "linux-x64" {"Linux" ; break} } } }, @{Name = "Format"; Expression = { $_.name.split(".")[-1] } }, @{Name = "SizeMB"; Expression = {$_.size / 1MB -as [int32]}}, @{Name = "Hash"; Expression = {$h.item($_.name)}}, @{Name = "Created"; Expression = {$_.Created_at -as [datetime]}}, @{Name = "Updated"; Expression = {$_.Updated_at -as [datetime]}}, @{Name = "URL"; Expression = {$_.browser_download_Url}}, @{Name = "DownloadCount"; Expression = {$_.download_count}} if ($Family) { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Filtering by family" $assets = $assets.where( {$_.family -match $($family -join "|")}) } if ($Only64Bit) { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Filtering for 64bit" $assets = ($assets).where( {$_.filename -match "(x|amd)64"}) } if ($Format) { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Filtering for format" $assets = $assets.where( {$_.format -match $("^$format$" -join "|")}) } If ($LTS) { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Filtering for LTS assets" $assets = $assets.where( {$_.filename -match "LTS"}) } #write the results to the pipeline if ($assets.filename) { $assets } else { Write-Warning "Get-PSReleaseAsset Failed to find any release assets using the specified critiera." } } #Try catch { Throw $_ } } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending: $($MyInvocation.Mycommand)" } #end } <# Display Options /quiet Quiet mode, no user interaction /passive Unattended mode - progress bar only /q[n|b|r|f] Sets user interface level n - No UI b - Basic UI r - Reduced UI f - Full UI (default) #> Function Install-PSPreview { [cmdletbinding(SupportsShouldProcess)] Param( [Parameter(HelpMessage = "Specify the path to the download folder")] [string]$Path = $env:TEMP, [Parameter(HelpMessage = "Specify what kind of installation you want. The default if a full interactive install.")] [ValidateSet("Full", "Quiet", "Passive")] [string]$Mode = "Full", [Parameter(HelpMessage = "Enable PowerShell Remoting over WSMan.")] [switch]$EnableRemoting, [Parameter(HelpMessage = "Enable the PowerShell context menu in Windows Explorer.")] [switch]$EnableContextMenu ) Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)" } #begin Process { #only run on Windows if (($psedition -eq 'Desktop') -OR ($PSVersionTable.platform -eq 'Win32NT')) { if ($PSBoundParameters.ContainsKey("WhatIf")) { #create a dummy file name is using -Whatif Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Creating a dummy file for WhatIf purposes" $filename = Join-Path -path $Path -ChildPath "whatif-PS7Preview.msi" } else { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Saving download to $Path" $msi = Get-PSReleaseAsset -Preview -Family Windows -Only64Bit -Format msi if ($msi) { $install = $msi | Save-PSReleaseAsset -Path $Path -Passthru $filename = $install.fullname } #if msi found else { Write-Warning "No preview MSI file found to download and install." } } if ($filename) { Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Using $filename" #call the internal helper function $inParams = @{ Path = $filename Mode = $Mode EnableRemoting = $EnableRemoting EnableContextMenu = $EnableContextMenu ErrorAction = "stop" } if ($pscmdlet.ShouldProcess($filename, "Install PowerShell Preview using $mode mode")) { InstallMSI @inParams } } } #if Windows else { Write-Warning "This command will only work on Windows platforms." } } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)" } #end } #close Install-PSPreview Function Install-PowerShell { [cmdletbinding(SupportsShouldProcess)] [alias("Install-PSCore")] Param( [Parameter(HelpMessage = "Specify the path to the download folder")] [string]$Path = $env:TEMP, [Parameter(HelpMessage = "Specify what kind of installation you want. The default if a full interactive install.")] [ValidateSet("Full", "Quiet", "Passive")] [string]$Mode = "Full", [Parameter(HelpMessage = "Enable PowerShell Remoting over WSMan.")] [switch]$EnableRemoting, [Parameter(HelpMessage = "Enable the PowerShell context menu in Windows Explorer.")] [switch]$EnableContextMenu ) Begin { Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)" } #begin Process { #only run on Windows if (($psedition -eq 'Desktop') -OR ($PSVersionTable.platform -eq 'Win32NT')) { if ($PSBoundParameters.ContainsKey("WhatIf")) { #create a dummy file name is using -Whatif Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Creating a dummy file for WhatIf purposes" $filename = Join-Path -path $Path -ChildPath "whatif-ps7.msi" } else { Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Saving download to $Path " $msi = Get-PSReleaseAsset -Family Windows -Only64Bit -Format msi if ($msi) { $install = $msi | Save-PSReleaseAsset -Path $Path -Passthru $filename = $install.fullname } #if msi found else { Write-Warning "No MSI file found to download and install." } } if ($filename) { Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Using $filename" #call the internal helper function $inParams = @{ Path = $filename Mode = $Mode EnableRemoting = $EnableRemoting EnableContextMenu = $EnableContextMenu ErrorAction = "stop" } if ($pscmdlet.ShouldProcess($filename, "Install PowerShell using $mode mode")) { InstallMSI @inParams } } } #if Windows else { Write-Warning "This will only work on Windows platforms." } } #process End { Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)" } #end } #close Install-PSCore |