<NME> utils.ps1
<BEF> function Ensure-Exists($path) {
    if (-not (Test-Path $path)) {
        Write-Error "Missing required $path! Ensure it is installed"
        exit 1
    }
    return $true > $null
}

function Ensure-Executable($command) {
    try { Get-Command $command -ErrorAction Stop > $null }
    catch {
        if( ($command -eq "7z") -and (Test-Path "$env:programfiles\7-zip\7z.exe") ){
            Set-Alias -Name "7z" -Value "$env:programfiles\7-zip\7z.exe" -Scope script
        }
        elseif( ($command -eq "7z") -and (Test-Path "$env:programw6432\7-zip\7z.exe") ) {
            Set-Alias -Name "7z" -Value "$env:programw6432\7-zip\7z.exe" -Scope script
        }
        else {
            Write-Error "Missing $command! Ensure it is installed and on in the PATH"
            exit 1
        }
    }
}

function Delete-Existing($path) {
    if (Test-Path $path) {
        Write-Verbose "Remove existing $path"
    }
    Remove-Item -Recurse -Force $path -ErrorAction SilentlyContinue
}

function Extract-Archive($source, $target) {
    Write-Verbose $("Extracting Archive '$cmder_root\vendor\" + $source.replace('/','\') + " to '$cmder_root\vendor\$target'")
    Invoke-Expression "7z x -y -o`"$($target)`" `"$source`"  > `$null"
    if ($lastexitcode -ne 0) {
        Write-Error "Extracting of $source failed"
    }
    Remove-Item $source
}

function Create-Archive($source, $target, $params) {
    $command = "7z a -xr@`"$source\packignore`" $params `"$target`" `"$source\*`"  > `$null"
    Write-Verbose "Creating Archive from '$source' in '$target' with parameters '$params'"
    Invoke-Expression $command
    if ($lastexitcode -ne 0) {
        Write-Error "Compressing $source failed"
    }
}

# If directory contains only one child directory
# Flatten it instead
function Flatten-Directory($name) {
    $name = Resolve-Path $name
}

function Digest-MD5 ($path) {
    return Invoke-Expression "md5sum $path"
}

        return (Get-FileHash -Algorithm SHA256 -Path $path).Hash
    }

    return Invoke-Expression "md5sum $path"
}

function Set-GHVariable {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        [Parameter(Mandatory = $true)]
        [string]$Value
    )

    Write-Verbose "Setting CI variable $Name to $Value" -Verbose

    if ($env:GITHUB_ENV) {
        echo "$Name=$Value" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
    }
}

function Get-GHTempPath {
    $temp = [System.IO.Path]::GetTempPath()
    if ($env:RUNNER_TEMP) {
        $temp = $env:RUNNER_TEMP
    }

    Write-Verbose "Get CI Temp path: $temp" -Verbose
    return $temp
}

function Get-VersionStr {
    # Clear existing variable
    if ($string) { Clear-Variable -name string }

    # Determine if git is available
    if (Get-Command "git.exe" -ErrorAction SilentlyContinue) {
        # Determine if the current directory is a git repository
        $GitPresent = Invoke-Expression "git rev-parse --is-inside-work-tree" -ErrorAction SilentlyContinue

        if ( $GitPresent -eq 'true' ) {
            $string = Invoke-Expression "git describe --abbrev=0 --tags"
        }
    }

    # Fallback used when Git is not available
    if ( -not($string) ) {
        $string = Parse-Changelog ($PSScriptRoot + '\..\' + 'CHANGELOG.md')
    }

    # Add build number, if AppVeyor is present
    if ( $Env:APPVEYOR -eq 'True' ) {
        $string = $string + '.' + $Env:APPVEYOR_BUILD_NUMBER
    }
    elseif ( $Env:GITHUB_ACTIONS -eq 'true' ) {
        $string = $string + '.' + $Env:GITHUB_RUN_NUMBER
    }

    # Remove starting 'v' characters
    $string = $string -replace '^v+','' # normalize version string

    return $string
}

function Parse-Changelog($file) {
    # Define the regular expression to match the version string from changelog
    [regex]$regex = '^## \[(?<version>[\w\-\.]+)\]\([^\n()]+\)\s+\([^\n()]+\)$';

    # Find the first match of the version string which means the latest version
    $version = Select-String -Path $file -Pattern $regex | Select-Object -First 1 | % { $_.Matches.Groups[1].Value }

    return $version
}

function Create-RC($string, $path) {
    $version  = $string + '.0.0.0.0' # padding for version string

    if ( !(Test-Path "$path.sample") ) {
        throw "Invalid path provided for resources file."
    }

    $resource = Get-Content -Path "$path.sample"
    $pattern  = @( "Cmder-Major-Version", "Cmder-Minor-Version", "Cmder-Revision-Version", "Cmder-Build-Version" )
    $index    = 0

    # Replace all non-numeric characters to dots and split to array
    $version = $version -replace '[^0-9]+','.' -split '\.'

    foreach ($fragment in $version) {
        if ( !$fragment ) { break }
        elseif ($index -le $pattern.length) {
            $resource = $resource.Replace( "{" + $pattern[$index++] + "}", $fragment )
        }
    }

    # Add the version string
    $resource = $resource.Replace( "{Cmder-Version-Str}", '"' + $string + '"' )

    # Write the results
    Set-Content -Path $path -Value $resource
}

function Register-Cmder() {
    [CmdletBinding()]
    Param
    (
        # Text for the context menu item.
        $MenuText = "Cmder Here"

        , # Defaults to the current cmder directory when run from cmder.
        $PathToExe = (Join-Path $env:CMDER_ROOT "cmder.exe")

        , # Commands the context menu will execute.
        $Command = "%V"

        , # Defaults to the icons folder in the cmder package.
        $icon = (Split-Path $PathToExe | Join-Path -ChildPath 'icons/cmder.ico')
    )
    Begin
    {
        New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT > $null
    }
    Process
    {
        New-Item         -Path "HKCR:\Directory\Shell\Cmder" -Force -Value $MenuText
        New-ItemProperty -Path "HKCR:\Directory\Shell\Cmder" -Force -Name "Icon" -Value `"$icon`"
        New-ItemProperty -Path "HKCR:\Directory\Shell\Cmder" -Force -Name "NoWorkingDirectory"
        New-Item         -Path "HKCR:\Directory\Shell\Cmder\Command" -Force -Value "`"$PathToExe`" `"$Command`" "

        New-Item         -Path "HKCR:\Directory\Background\Shell\Cmder" -Force -Value $MenuText
        New-ItemProperty -Path "HKCR:\Directory\Background\Shell\Cmder" -Force -Name "Icon" -Value `"$icon`"
        New-ItemProperty -Path "HKCR:\Directory\Background\Shell\Cmder" -Force -Name "NoWorkingDirectory"
        New-Item         -Path "HKCR:\Directory\Background\Shell\Cmder\Command" -Force -Value "`"$PathToExe`" `"$Command`" "
    }
    End
    {
        Remove-PSDrive -Name HKCR
    }
}

function Unregister-Cmder {
    Begin
    {
        New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT > $null
    }
    Process
    {
        Remove-Item -Path "HKCR:\Directory\Shell\Cmder" -Recurse
        Remove-Item -Path "HKCR:\Directory\Background\Shell\Cmder" -Recurse
    }
    End
    {
        Remove-PSDrive -Name HKCR
    }
}

function Download-File {
    param (
        $Url,
        $File
    )
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

    $useBitTransfer = $null -ne (Get-Module -Name BitsTransfer -ListAvailable) -and ($PSVersionTable.PSVersion.Major -le 5)

    $File = $File -Replace "/", "\"

    try {
        if ($useBitTransfer) {
            Start-BitsTransfer -Source $Url -Destination $File -DisplayName "Downloading $Url to $File"
            Return
        }
    }
    catch {
        Write-Error "Failed to download file using BITS, reason: $_`nUsing fallback method instead...`n" -ErrorAction:Continue
    }

    Write-Verbose "Downloading from $Url to $File`n"

    $wc = New-Object System.Net.WebClient
    if ($env:https_proxy) {
        $wc.proxy = (New-Object System.Net.WebProxy($env:https_proxy))
    }
    $wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;
    $wc.DownloadFile($Url, $File)
}

<MSG> Merge pull request #430 from Jackbennett/native-ps

Adds support for PS 4.0 native hash command to remove a dependency
<DFF> @@ -54,6 +54,10 @@ function Flatten-Directory ($name) {
 }
 
 function Digest-MD5 ($path) {
+    if(Get-Command Get-FileHash -ErrorAction SilentlyContinue){
+        return (Get-FileHash -Algorithm MD5 -Path $path).Hash
+    }
+
     return Invoke-Expression "md5sum $path"
 }
 

{'commit': 'diff --git a/scripts/utils.ps1 b/scripts/utils.ps1\nindex f8a0f44..e914c04 100644\n--- a/scripts/utils.ps1\n+++ b/scripts/utils.ps1\n@@ -54,6 +54,10 @@ function Flatten-Directory ($name) {\n }\n \n function Digest-MD5 ($path) {\n+    if(Get-Command Get-FileHash -ErrorAction SilentlyContinue){\n+        return (Get-FileHash -Algorithm MD5 -Path $path).Hash\n+    }\n+\n     return Invoke-Expression "md5sum $path"\n }\n \n', 'commit_subject': 'Merge pull request #430 from Jackbennett/native-ps', 'repo_name': 'cmderdev/cmder', 'committer': {'name': 'Martin Kemp', 'email': '54669547a225ff20cba8b75a4adca540eef25858@martinke.mp', 'time_sec': '1426690471', 'tz_offset': '0', 'date': {'seconds': '1426690471'}}, 'commit_tree': '33e4e196f143e65c4bd876509d6f32d1685e2fdf', 'commit_message': 'Merge pull request #430 from Jackbennett/native-ps\n\nAdds support for PS 4.0 native hash command to remove a dependency', 'language_name': 'C', 'license': 'mit', 'hunks': '@@ -54,6 +54,10 @@ function Flatten-Directory ($name) {\n }\n \n function Digest-MD5 ($path) {\n+    if(Get-Command Get-FileHash -ErrorAction SilentlyContinue){\n+        return (Get-FileHash -Algorithm MD5 -Path $path).Hash\n+    }\n+\n     return Invoke-Expression "md5sum $path"\n }\n \n', 'addition_count': 4, 'deletion_count': 0, 'src_file': 'a/scripts/utils.ps1', 'tgt_file': 'b/scripts/utils.ps1', 'file_extension': '.ps1', 'before_file': 'function Ensure-Exists($path) {\n    if (-not (Test-Path $path)) {\n        Write-Error "Missing required $path! Ensure it is installed"\n        exit 1\n    }\n    return $true > $null\n}\n\nfunction Ensure-Executable($command) {\n    try { Get-Command $command -ErrorAction Stop > $null }\n    catch {\n        if( ($command -eq "7z") -and (Test-Path "$env:programfiles\\7-zip\\7z.exe") ){\n            Set-Alias -Name "7z" -Value "$env:programfiles\\7-zip\\7z.exe" -Scope script\n        }\n        elseif( ($command -eq "7z") -and (Test-Path "$env:programw6432\\7-zip\\7z.exe") ) {\n            Set-Alias -Name "7z" -Value "$env:programw6432\\7-zip\\7z.exe" -Scope script\n        }\n        else {\n            Write-Error "Missing $command! Ensure it is installed and on in the PATH"\n            exit 1\n        }\n    }\n}\n\nfunction Delete-Existing($path) {\n    if (Test-Path $path) {\n        Write-Verbose "Remove existing $path"\n    }\n    Remove-Item -Recurse -Force $path -ErrorAction SilentlyContinue\n}\n\nfunction Extract-Archive($source, $target) {\n    Write-Verbose $("Extracting Archive \'$cmder_root\\vendor\\" + $source.replace(\'/\',\'\\\') + " to \'$cmder_root\\vendor\\$target\'")\n    Invoke-Expression "7z x -y -o`"$($target)`" `"$source`"  > `$null"\n    if ($lastexitcode -ne 0) {\n        Write-Error "Extracting of $source failed"\n    }\n    Remove-Item $source\n}\n\nfunction Create-Archive($source, $target, $params) {\n    $command = "7z a -xr@`"$source\\packignore`" $params `"$target`" `"$source\\*`"  > `$null"\n    Write-Verbose "Creating Archive from \'$source\' in \'$target\' with parameters \'$params\'"\n    Invoke-Expression $command\n    if ($lastexitcode -ne 0) {\n        Write-Error "Compressing $source failed"\n    }\n}\n\n# If directory contains only one child directory\n# Flatten it instead\nfunction Flatten-Directory($name) {\n    $name = Resolve-Path $name\n}\n\nfunction Digest-MD5 ($path) {\n    return Invoke-Expression "md5sum $path"\n}\n\n        return (Get-FileHash -Algorithm SHA256 -Path $path).Hash\n    }\n\n    return Invoke-Expression "md5sum $path"\n}\n\nfunction Set-GHVariable {\n    param(\n        [Parameter(Mandatory = $true)]\n        [string]$Name,\n        [Parameter(Mandatory = $true)]\n        [string]$Value\n    )\n\n    Write-Verbose "Setting CI variable $Name to $Value" -Verbose\n\n    if ($env:GITHUB_ENV) {\n        echo "$Name=$Value" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8\n    }\n}\n\nfunction Get-GHTempPath {\n    $temp = [System.IO.Path]::GetTempPath()\n    if ($env:RUNNER_TEMP) {\n        $temp = $env:RUNNER_TEMP\n    }\n\n    Write-Verbose "Get CI Temp path: $temp" -Verbose\n    return $temp\n}\n\nfunction Get-VersionStr {\n    # Clear existing variable\n    if ($string) { Clear-Variable -name string }\n\n    # Determine if git is available\n    if (Get-Command "git.exe" -ErrorAction SilentlyContinue) {\n        # Determine if the current directory is a git repository\n        $GitPresent = Invoke-Expression "git rev-parse --is-inside-work-tree" -ErrorAction SilentlyContinue\n\n        if ( $GitPresent -eq \'true\' ) {\n            $string = Invoke-Expression "git describe --abbrev=0 --tags"\n        }\n    }\n\n    # Fallback used when Git is not available\n    if ( -not($string) ) {\n        $string = Parse-Changelog ($PSScriptRoot + \'\\..\\\' + \'CHANGELOG.md\')\n    }\n\n    # Add build number, if AppVeyor is present\n    if ( $Env:APPVEYOR -eq \'True\' ) {\n        $string = $string + \'.\' + $Env:APPVEYOR_BUILD_NUMBER\n    }\n    elseif ( $Env:GITHUB_ACTIONS -eq \'true\' ) {\n        $string = $string + \'.\' + $Env:GITHUB_RUN_NUMBER\n    }\n\n    # Remove starting \'v\' characters\n    $string = $string -replace \'^v+\',\'\' # normalize version string\n\n    return $string\n}\n\nfunction Parse-Changelog($file) {\n    # Define the regular expression to match the version string from changelog\n    [regex]$regex = \'^## \\[(?<version>[\\w\\-\\.]+)\\]\\([^\\n()]+\\)\\s+\\([^\\n()]+\\)$\';\n\n    # Find the first match of the version string which means the latest version\n    $version = Select-String -Path $file -Pattern $regex | Select-Object -First 1 | % { $_.Matches.Groups[1].Value }\n\n    return $version\n}\n\nfunction Create-RC($string, $path) {\n    $version  = $string + \'.0.0.0.0\' # padding for version string\n\n    if ( !(Test-Path "$path.sample") ) {\n        throw "Invalid path provided for resources file."\n    }\n\n    $resource = Get-Content -Path "$path.sample"\n    $pattern  = @( "Cmder-Major-Version", "Cmder-Minor-Version", "Cmder-Revision-Version", "Cmder-Build-Version" )\n    $index    = 0\n\n    # Replace all non-numeric characters to dots and split to array\n    $version = $version -replace \'[^0-9]+\',\'.\' -split \'\\.\'\n\n    foreach ($fragment in $version) {\n        if ( !$fragment ) { break }\n        elseif ($index -le $pattern.length) {\n            $resource = $resource.Replace( "{" + $pattern[$index++] + "}", $fragment )\n        }\n    }\n\n    # Add the version string\n    $resource = $resource.Replace( "{Cmder-Version-Str}", \'"\' + $string + \'"\' )\n\n    # Write the results\n    Set-Content -Path $path -Value $resource\n}\n\nfunction Register-Cmder() {\n    [CmdletBinding()]\n    Param\n    (\n        # Text for the context menu item.\n        $MenuText = "Cmder Here"\n\n        , # Defaults to the current cmder directory when run from cmder.\n        $PathToExe = (Join-Path $env:CMDER_ROOT "cmder.exe")\n\n        , # Commands the context menu will execute.\n        $Command = "%V"\n\n        , # Defaults to the icons folder in the cmder package.\n        $icon = (Split-Path $PathToExe | Join-Path -ChildPath \'icons/cmder.ico\')\n    )\n    Begin\n    {\n        New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT > $null\n    }\n    Process\n    {\n        New-Item         -Path "HKCR:\\Directory\\Shell\\Cmder" -Force -Value $MenuText\n        New-ItemProperty -Path "HKCR:\\Directory\\Shell\\Cmder" -Force -Name "Icon" -Value `"$icon`"\n        New-ItemProperty -Path "HKCR:\\Directory\\Shell\\Cmder" -Force -Name "NoWorkingDirectory"\n        New-Item         -Path "HKCR:\\Directory\\Shell\\Cmder\\Command" -Force -Value "`"$PathToExe`" `"$Command`" "\n\n        New-Item         -Path "HKCR:\\Directory\\Background\\Shell\\Cmder" -Force -Value $MenuText\n        New-ItemProperty -Path "HKCR:\\Directory\\Background\\Shell\\Cmder" -Force -Name "Icon" -Value `"$icon`"\n        New-ItemProperty -Path "HKCR:\\Directory\\Background\\Shell\\Cmder" -Force -Name "NoWorkingDirectory"\n        New-Item         -Path "HKCR:\\Directory\\Background\\Shell\\Cmder\\Command" -Force -Value "`"$PathToExe`" `"$Command`" "\n    }\n    End\n    {\n        Remove-PSDrive -Name HKCR\n    }\n}\n\nfunction Unregister-Cmder {\n    Begin\n    {\n        New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT > $null\n    }\n    Process\n    {\n        Remove-Item -Path "HKCR:\\Directory\\Shell\\Cmder" -Recurse\n        Remove-Item -Path "HKCR:\\Directory\\Background\\Shell\\Cmder" -Recurse\n    }\n    End\n    {\n        Remove-PSDrive -Name HKCR\n    }\n}\n\nfunction Download-File {\n    param (\n        $Url,\n        $File\n    )\n    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\n\n    $useBitTransfer = $null -ne (Get-Module -Name BitsTransfer -ListAvailable) -and ($PSVersionTable.PSVersion.Major -le 5)\n\n    $File = $File -Replace "/", "\\"\n\n    try {\n        if ($useBitTransfer) {\n            Start-BitsTransfer -Source $Url -Destination $File -DisplayName "Downloading $Url to $File"\n            Return\n        }\n    }\n    catch {\n        Write-Error "Failed to download file using BITS, reason: $_`nUsing fallback method instead...`n" -ErrorAction:Continue\n    }\n\n    Write-Verbose "Downloading from $Url to $File`n"\n\n    $wc = New-Object System.Net.WebClient\n    if ($env:https_proxy) {\n        $wc.proxy = (New-Object System.Net.WebProxy($env:https_proxy))\n    }\n    $wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;\n    $wc.DownloadFile($Url, $File)\n}\n', 'message': 'Merge pull request #430 from Jackbennett/native-ps\n\nAdds support for PS 4.0 native hash command to remove a dependency', 'author_name': 'Martin Kemp', 'lang': 'ps1'}