From 44f56a5bd05b6d0331cd2c2c5895eac6fb886086 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 13 Jan 2018 17:36:41 +0000 Subject: [PATCH 1/5] Move SSH utils to separate file --- src/GitUtils.ps1 | 191 --------------------------------------------- src/SshUtils.ps1 | 194 ++++++++++++++++++++++++++++++++++++++++++++++ src/posh-git.psm1 | 1 + 3 files changed, 195 insertions(+), 191 deletions(-) create mode 100644 src/SshUtils.ps1 diff --git a/src/GitUtils.ps1 b/src/GitUtils.ps1 index 1e6bef8f6..042e80c68 100644 --- a/src/GitUtils.ps1 +++ b/src/GitUtils.ps1 @@ -386,197 +386,6 @@ function Get-TempEnvPath($key){ return $path } -# Retrieve the current SSH agent PID (or zero). Can be used to determine if there -# is a running agent. -function Get-SshAgent() { - if ($env:GIT_SSH -imatch 'plink') { - $pageantPid = Get-Process | Where-Object { $_.Name -eq 'pageant' } | Select-Object -ExpandProperty Id -First 1 - if ($null -ne $pageantPid) { return $pageantPid } - } - else { - $agentPid = $Env:SSH_AGENT_PID - if ($agentPid) { - $sshAgentProcess = Get-Process | Where-Object { ($_.Id -eq $agentPid) -and ($_.Name -eq 'ssh-agent') } - if ($null -ne $sshAgentProcess) { - return $agentPid - } - else { - setenv 'SSH_AGENT_PID' $null - setenv 'SSH_AUTH_SOCK' $null - } - } - } - - return 0 -} - -# Attempt to guess Pageant's location -function Find-Pageant() { - Write-Verbose "Pageant not in path. Trying to guess location." - - $gitSsh = $env:GIT_SSH - if ($gitSsh -and (test-path $gitSsh)) { - $pageant = join-path (split-path $gitSsh) pageant - } - - if (!(get-command $pageant -Erroraction SilentlyContinue)) { - return # Guessing failed. - } - else { - return $pageant - } -} - -# Attempt to guess $program's location. For ssh-agent/ssh-add. -function Find-Ssh($program = 'ssh-agent') { - Write-Verbose "$program not in path. Trying to guess location." - $gitItem = Get-Command git -CommandType Application -Erroraction SilentlyContinue | Get-Item - if ($null -eq $gitItem) { - Write-Warning 'git not in path' - return - } - - $sshLocation = join-path $gitItem.directory.parent.fullname bin/$program - if (get-command $sshLocation -Erroraction SilentlyContinue) { - return $sshLocation - } - - $sshLocation = join-path $gitItem.directory.parent.fullname usr/bin/$program - if (get-command $sshLocation -Erroraction SilentlyContinue) { - return $sshLocation - } -} - -# Loosely based on bash script from http://help.github.com/ssh-key-passphrases/ -function Start-SshAgent([switch]$Quiet) { - [int]$agentPid = Get-SshAgent - if ($agentPid -gt 0) { - if (!$Quiet) { - $agentName = Get-Process -Id $agentPid | Select-Object -ExpandProperty Name - if (!$agentName) { $agentName = "SSH Agent" } - Write-Host "$agentName is already running (pid $($agentPid))" - } - return - } - - if ($env:GIT_SSH -imatch 'plink') { - Write-Host "GIT_SSH set to $($env:GIT_SSH), using Pageant as SSH agent." - - $pageant = Get-Command pageant -TotalCount 1 -Erroraction SilentlyContinue - $pageant = if ($pageant) { $pageant } else { Find-Pageant } - if (!$pageant) { - if (!$Quiet) { - Write-Warning 'Could not find Pageant' - } - return - } - - Start-Process -NoNewWindow $pageant - } - else { - $sshAgent = Get-Command ssh-agent -TotalCount 1 -ErrorAction SilentlyContinue - $sshAgent = if ($sshAgent) { $sshAgent } else { Find-Ssh('ssh-agent') } - if (!$sshAgent) { - if (!$Quiet) { - Write-Warning 'Could not find ssh-agent' - } - return - } - - & $sshAgent | ForEach-Object { - if ($_ -match '(?[^=]+)=(?[^;]+);') { - setenv $Matches['key'] $Matches['value'] - } - } - } - - Add-SshKey -Quiet:$Quiet -} - -function Get-SshPath($File = 'id_rsa') { - # Avoid paths with path separator char since it is different on Linux/macOS. - # Also avoid ~ as it is invalid if the user is cd'd into say cert:\ or hklm:\. - # Also, apparently using the PowerShell built-in $HOME variable may not cut it for msysGit with has different - # ideas about the path to the user's home dir e.g. /c/Users/Keith - $homePath = Invoke-NullCoalescing $Env:HOME $Home - Join-Path $homePath (Join-Path .ssh $File) -} - -<# -.SYNOPSIS - Add a key to the SSH agent -.DESCRIPTION - Adds one or more SSH keys to the SSH agent. -.EXAMPLE - PS C:\> Add-SshKey - Adds ~\.ssh\id_rsa to the SSH agent. -.EXAMPLE - PS C:\> Add-SshKey ~\.ssh\mykey, ~\.ssh\myotherkey - Adds ~\.ssh\mykey and ~\.ssh\myotherkey to the SSH agent. -.INPUTS - None. - You cannot pipe input to this cmdlet. -#> -function Add-SshKey([switch]$Quiet) { - if ($env:GIT_SSH -imatch 'plink') { - $pageant = Get-Command pageant -Erroraction SilentlyContinue | Select-Object -First 1 -ExpandProperty Name - $pageant = if ($pageant) { $pageant } else { Find-Pageant } - if (!$pageant) { - if (!$Quiet) { - Write-Warning 'Could not find Pageant' - } - return - } - - if ($args.Count -eq 0) { - $keyPath = Join-Path $Env:HOME .ssh - $keys = Get-ChildItem $keyPath/*.ppk -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName - if ($keys) { - & $pageant $keys - } - } - else { - foreach ($value in $args) { - & $pageant $value - } - } - } - else { - $sshAdd = Get-Command ssh-add -TotalCount 1 -ErrorAction SilentlyContinue - $sshAdd = if ($sshAdd) { $sshAdd } else { Find-Ssh('ssh-add') } - if (!$sshAdd) { - if (!$Quiet) { - Write-Warning 'Could not find ssh-add' - } - return - } - - if ($args.Count -eq 0) { - & $sshAdd - } - else { - foreach ($value in $args) { - & $sshAdd $value - } - } - } -} - -# Stop a running SSH agent -function Stop-SshAgent() { - [int]$agentPid = Get-SshAgent - if ($agentPid -gt 0) { - # Stop agent process - $proc = Get-Process -Id $agentPid -ErrorAction SilentlyContinue - if ($null -ne $proc) { - Stop-Process $agentPid - } - - setenv 'SSH_AGENT_PID' $null - setenv 'SSH_AUTH_SOCK' $null - } -} - function Update-AllBranches($Upstream = 'master', [switch]$Quiet) { $head = git rev-parse --abbrev-ref HEAD git checkout -q $Upstream diff --git a/src/SshUtils.ps1 b/src/SshUtils.ps1 new file mode 100644 index 000000000..5521c46ff --- /dev/null +++ b/src/SshUtils.ps1 @@ -0,0 +1,194 @@ +# +# SshUtils.ps1 +# + +# Retrieve the current SSH agent PID (or zero). Can be used to determine if there +# is a running agent. +function Get-SshAgent() { + if ($env:GIT_SSH -imatch 'plink') { + $pageantPid = Get-Process | Where-Object { $_.Name -eq 'pageant' } | Select-Object -ExpandProperty Id -First 1 + if ($null -ne $pageantPid) { return $pageantPid } + } + else { + $agentPid = $Env:SSH_AGENT_PID + if ($agentPid) { + $sshAgentProcess = Get-Process | Where-Object { ($_.Id -eq $agentPid) -and ($_.Name -eq 'ssh-agent') } + if ($null -ne $sshAgentProcess) { + return $agentPid + } + else { + setenv 'SSH_AGENT_PID' $null + setenv 'SSH_AUTH_SOCK' $null + } + } + } + + return 0 +} + +# Attempt to guess Pageant's location +function Find-Pageant() { + Write-Verbose "Pageant not in path. Trying to guess location." + + $gitSsh = $env:GIT_SSH + if ($gitSsh -and (test-path $gitSsh)) { + $pageant = join-path (split-path $gitSsh) pageant + } + + if (!(get-command $pageant -Erroraction SilentlyContinue)) { + return # Guessing failed. + } + else { + return $pageant + } +} + +# Attempt to guess $program's location. For ssh-agent/ssh-add. +function Find-Ssh($program = 'ssh-agent') { + Write-Verbose "$program not in path. Trying to guess location." + $gitItem = Get-Command git -CommandType Application -Erroraction SilentlyContinue | Get-Item + if ($null -eq $gitItem) { + Write-Warning 'git not in path' + return + } + + $sshLocation = join-path $gitItem.directory.parent.fullname bin/$program + if (get-command $sshLocation -Erroraction SilentlyContinue) { + return $sshLocation + } + + $sshLocation = join-path $gitItem.directory.parent.fullname usr/bin/$program + if (get-command $sshLocation -Erroraction SilentlyContinue) { + return $sshLocation + } +} + +# Loosely based on bash script from http://help.github.com/ssh-key-passphrases/ +function Start-SshAgent([switch]$Quiet) { + [int]$agentPid = Get-SshAgent + if ($agentPid -gt 0) { + if (!$Quiet) { + $agentName = Get-Process -Id $agentPid | Select-Object -ExpandProperty Name + if (!$agentName) { $agentName = "SSH Agent" } + Write-Host "$agentName is already running (pid $($agentPid))" + } + return + } + + if ($env:GIT_SSH -imatch 'plink') { + Write-Host "GIT_SSH set to $($env:GIT_SSH), using Pageant as SSH agent." + + $pageant = Get-Command pageant -TotalCount 1 -Erroraction SilentlyContinue + $pageant = if ($pageant) { $pageant } else { Find-Pageant } + if (!$pageant) { + if (!$Quiet) { + Write-Warning 'Could not find Pageant' + } + return + } + + Start-Process -NoNewWindow $pageant + } + else { + $sshAgent = Get-Command ssh-agent -TotalCount 1 -ErrorAction SilentlyContinue + $sshAgent = if ($sshAgent) { $sshAgent } else { Find-Ssh('ssh-agent') } + if (!$sshAgent) { + if (!$Quiet) { + Write-Warning 'Could not find ssh-agent' + } + return + } + + & $sshAgent | ForEach-Object { + if ($_ -match '(?[^=]+)=(?[^;]+);') { + setenv $Matches['key'] $Matches['value'] + } + } + } + + Add-SshKey -Quiet:$Quiet +} + +function Get-SshPath($File = 'id_rsa') { + # Avoid paths with path separator char since it is different on Linux/macOS. + # Also avoid ~ as it is invalid if the user is cd'd into say cert:\ or hklm:\. + # Also, apparently using the PowerShell built-in $HOME variable may not cut it for msysGit with has different + # ideas about the path to the user's home dir e.g. /c/Users/Keith + $homePath = Invoke-NullCoalescing $Env:HOME $Home + Join-Path $homePath (Join-Path .ssh $File) +} + +<# +.SYNOPSIS + Add a key to the SSH agent +.DESCRIPTION + Adds one or more SSH keys to the SSH agent. +.EXAMPLE + PS C:\> Add-SshKey + Adds ~\.ssh\id_rsa to the SSH agent. +.EXAMPLE + PS C:\> Add-SshKey ~\.ssh\mykey, ~\.ssh\myotherkey + Adds ~\.ssh\mykey and ~\.ssh\myotherkey to the SSH agent. +.INPUTS + None. + You cannot pipe input to this cmdlet. +#> +function Add-SshKey([switch]$Quiet) { + if ($env:GIT_SSH -imatch 'plink') { + $pageant = Get-Command pageant -Erroraction SilentlyContinue | Select-Object -First 1 -ExpandProperty Name + $pageant = if ($pageant) { $pageant } else { Find-Pageant } + if (!$pageant) { + if (!$Quiet) { + Write-Warning 'Could not find Pageant' + } + return + } + + if ($args.Count -eq 0) { + $keyPath = Join-Path $Env:HOME .ssh + $keys = Get-ChildItem $keyPath/*.ppk -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName + if ($keys) { + & $pageant $keys + } + } + else { + foreach ($value in $args) { + & $pageant $value + } + } + } + else { + $sshAdd = Get-Command ssh-add -TotalCount 1 -ErrorAction SilentlyContinue + $sshAdd = if ($sshAdd) { $sshAdd } else { Find-Ssh('ssh-add') } + if (!$sshAdd) { + if (!$Quiet) { + Write-Warning 'Could not find ssh-add' + } + return + } + + if ($args.Count -eq 0) { + & $sshAdd + } + else { + foreach ($value in $args) { + & $sshAdd $value + } + } + } +} + +# Stop a running SSH agent +function Stop-SshAgent() { + [int]$agentPid = Get-SshAgent + if ($agentPid -gt 0) { + # Stop agent process + $proc = Get-Process -Id $agentPid -ErrorAction SilentlyContinue + if ($null -ne $proc) { + Stop-Process $agentPid + } + + setenv 'SSH_AGENT_PID' $null + setenv 'SSH_AUTH_SOCK' $null + } +} diff --git a/src/posh-git.psm1 b/src/posh-git.psm1 index 057ea9e9c..1955c7bcf 100644 --- a/src/posh-git.psm1 +++ b/src/posh-git.psm1 @@ -11,6 +11,7 @@ param([switch]$NoVersionWarn, [switch]$ForcePoshGitPrompt) . $PSScriptRoot\GitParamTabExpansion.ps1 . $PSScriptRoot\GitTabExpansion.ps1 . $PSScriptRoot\TortoiseGit.ps1 +. $PSScriptRoot\SshUtils.ps1 if (!$Env:HOME) { $Env:HOME = "$Env:HOMEDRIVE$Env:HOMEPATH" } if (!$Env:HOME) { $Env:HOME = "$Env:USERPROFILE" } From d8e34ef7d86cf713c3d72a75704a61beef0b02c1 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 13 Jan 2018 18:07:04 +0000 Subject: [PATCH 2/5] Separate SSH tests --- test/Ssh.Tests.ps1 | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/Ssh.Tests.ps1 b/test/Ssh.Tests.ps1 index a5391cf9a..097b7deae 100644 --- a/test/Ssh.Tests.ps1 +++ b/test/Ssh.Tests.ps1 @@ -1,20 +1,23 @@ -. $PSScriptRoot\Shared.ps1 +. $PSScriptRoot\..\src\Utils.ps1 #for Invoke-NullCoalescing + +. $PSScriptRoot\..\src\SshUtils.ps1 Describe 'SSH Function Tests' { Context 'Get-SshPath Tests' { + $HomePath = Join-Path $Home '.ssh' It 'Returns the correct default path' { - Get-SshPath | Should BeExactly (MakeNativePath $Home\.ssh\id_rsa) + Get-SshPath | Should BeExactly (Join-Path $HomePath id_rsa) } It 'Returns the correct path for a given filename' { $filename = 'xyzzy-eb2ff0a9-81ee-4983-b32d-530286600a51' - Get-SshPath $filename | Should BeExactly (MakeNativePath $Home\.ssh\$filename) + Get-SshPath $filename | Should BeExactly (Join-Path $HomePath $filename) } It 'Returns the correct path, given $Env:Home is not defined ($null)' { $origEnvHome = $Env:HOME try { Remove-Item Env:\Home -ErrorAction SilentlyContinue $Env:Home | Should BeNullOrEmpty - Get-SshPath | Should BeExactly (MakeNativePath $Home\.ssh\id_rsa) + Get-SshPath | Should BeExactly (Join-Path $HomePath id_rsa) } finally { Set-Item Env:\HOME -Value $origEnvHome From 11cb06384756c22d144f12213fddf5cbefddf888 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 13 Jan 2018 18:30:56 +0000 Subject: [PATCH 3/5] Remove Utils dependency from SshUtils Remove use of Invoke-NullCoalescing --- src/SshUtils.ps1 | 3 ++- test/Ssh.Tests.ps1 | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/SshUtils.ps1 b/src/SshUtils.ps1 index 5521c46ff..d48a9504c 100644 --- a/src/SshUtils.ps1 +++ b/src/SshUtils.ps1 @@ -114,7 +114,8 @@ function Get-SshPath($File = 'id_rsa') { # Also avoid ~ as it is invalid if the user is cd'd into say cert:\ or hklm:\. # Also, apparently using the PowerShell built-in $HOME variable may not cut it for msysGit with has different # ideas about the path to the user's home dir e.g. /c/Users/Keith - $homePath = Invoke-NullCoalescing $Env:HOME $Home + # $homePath = Invoke-NullCoalescing $Env:HOME $Home + $homePath = if ($Env:HOME) {$Env:HOME} else {$Home} Join-Path $homePath (Join-Path .ssh $File) } diff --git a/test/Ssh.Tests.ps1 b/test/Ssh.Tests.ps1 index 097b7deae..4613b9c5c 100644 --- a/test/Ssh.Tests.ps1 +++ b/test/Ssh.Tests.ps1 @@ -1,5 +1,3 @@ -. $PSScriptRoot\..\src\Utils.ps1 #for Invoke-NullCoalescing - . $PSScriptRoot\..\src\SshUtils.ps1 Describe 'SSH Function Tests' { From bb9d03e72d8990712b16693c19308b10e26a4f63 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 13 Jan 2018 18:45:56 +0000 Subject: [PATCH 4/5] Move Environment Variable utils to SshUtils SSH tools are the only thing that use them, outside of posh-git.psm1 --- src/GitUtils.ps1 | 31 ------------------------------- src/SshUtils.ps1 | 31 +++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/GitUtils.ps1 b/src/GitUtils.ps1 index 042e80c68..7f2361766 100644 --- a/src/GitUtils.ps1 +++ b/src/GitUtils.ps1 @@ -355,37 +355,6 @@ function Get-AliasPattern($exe) { "($($aliases -join '|'))" } -function setenv($key, $value) { - [void][Environment]::SetEnvironmentVariable($key, $value) - Set-TempEnv $key $value -} - -function Get-TempEnv($key) { - $path = Get-TempEnvPath($key) - if (Test-Path $path) { - $value = Get-Content $path - [void][Environment]::SetEnvironmentVariable($key, $value) - } -} - -function Set-TempEnv($key, $value) { - $path = Get-TempEnvPath($key) - if ($null -eq $value) { - if (Test-Path $path) { - Remove-Item $path - } - } - else { - New-Item $path -Force -ItemType File > $null - $value | Out-File -FilePath $path -Encoding ascii -Force - } -} - -function Get-TempEnvPath($key){ - $path = Join-Path ([System.IO.Path]::GetTempPath()) ".ssh\$key.env" - return $path -} - function Update-AllBranches($Upstream = 'master', [switch]$Quiet) { $head = git rev-parse --abbrev-ref HEAD git checkout -q $Upstream diff --git a/src/SshUtils.ps1 b/src/SshUtils.ps1 index d48a9504c..3f60e2384 100644 --- a/src/SshUtils.ps1 +++ b/src/SshUtils.ps1 @@ -2,6 +2,37 @@ # SshUtils.ps1 # +function setenv($key, $value) { + [void][Environment]::SetEnvironmentVariable($key, $value) + Set-TempEnv $key $value +} + +function Get-TempEnv($key) { + $path = Get-TempEnvPath($key) + if (Test-Path $path) { + $value = Get-Content $path + [void][Environment]::SetEnvironmentVariable($key, $value) + } +} + +function Set-TempEnv($key, $value) { + $path = Get-TempEnvPath($key) + if ($null -eq $value) { + if (Test-Path $path) { + Remove-Item $path + } + } + else { + New-Item $path -Force -ItemType File > $null + $value | Out-File -FilePath $path -Encoding ascii -Force + } +} + +function Get-TempEnvPath($key){ + $path = Join-Path ([System.IO.Path]::GetTempPath()) ".ssh\$key.env" + return $path +} + # Retrieve the current SSH agent PID (or zero). Can be used to determine if there # is a running agent. function Get-SshAgent() { From dcac52f404fd44aa637245a7f6a3ca59a9c4b1c8 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 13 Jan 2018 18:52:50 +0000 Subject: [PATCH 5/5] Move Get-TempEnv calls to SshUtils.ps1 No reason for posh-git.psm1 to be doing it --- src/SshUtils.ps1 | 3 +++ src/posh-git.psm1 | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SshUtils.ps1 b/src/SshUtils.ps1 index 3f60e2384..243b05b33 100644 --- a/src/SshUtils.ps1 +++ b/src/SshUtils.ps1 @@ -224,3 +224,6 @@ function Stop-SshAgent() { setenv 'SSH_AUTH_SOCK' $null } } + +Get-TempEnv 'SSH_AGENT_PID' +Get-TempEnv 'SSH_AUTH_SOCK' diff --git a/src/posh-git.psm1 b/src/posh-git.psm1 index 1955c7bcf..339b8687a 100644 --- a/src/posh-git.psm1 +++ b/src/posh-git.psm1 @@ -16,9 +16,6 @@ param([switch]$NoVersionWarn, [switch]$ForcePoshGitPrompt) if (!$Env:HOME) { $Env:HOME = "$Env:HOMEDRIVE$Env:HOMEPATH" } if (!$Env:HOME) { $Env:HOME = "$Env:USERPROFILE" } -Get-TempEnv 'SSH_AGENT_PID' -Get-TempEnv 'SSH_AUTH_SOCK' - # Get the default prompt definition. $initialSessionState = [Runspace]::DefaultRunspace.InitialSessionState if (!$initialSessionState.Commands -or !$initialSessionState.Commands['prompt']) {