diff --git a/src/GitUtils.ps1 b/src/GitUtils.ps1 index 1e6bef8f6..7f2361766 100644 --- a/src/GitUtils.ps1 +++ b/src/GitUtils.ps1 @@ -355,228 +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 -} - -# 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..243b05b33 --- /dev/null +++ b/src/SshUtils.ps1 @@ -0,0 +1,229 @@ +# +# 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() { + 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 + $homePath = if ($Env:HOME) {$Env:HOME} else {$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 + } +} + +Get-TempEnv 'SSH_AGENT_PID' +Get-TempEnv 'SSH_AUTH_SOCK' diff --git a/src/posh-git.psm1 b/src/posh-git.psm1 index 057ea9e9c..339b8687a 100644 --- a/src/posh-git.psm1 +++ b/src/posh-git.psm1 @@ -11,13 +11,11 @@ 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" } -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']) { diff --git a/test/Ssh.Tests.ps1 b/test/Ssh.Tests.ps1 index a5391cf9a..4613b9c5c 100644 --- a/test/Ssh.Tests.ps1 +++ b/test/Ssh.Tests.ps1 @@ -1,20 +1,21 @@ -. $PSScriptRoot\Shared.ps1 +. $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