Public/Set-Ssh.ps1

function Set-Ssh {
    <#
    .SYNOPSIS
    Configure or convert the published location to SSH-based access.
 
    .DESCRIPTION
    Set-Ssh sets the published location for the active project folder to an SSH URL. If you do not provide one, Set-Ssh reads the existing HTTPS URL and converts it to its SSH form (for example, https://github.com/example/repo.git becomes git@github.com:example/repo.git). Use it when SSH is preferred over HTTPS, or when corporate environments require key-based authentication.
 
    .PARAMETER RemoteName
    The name of the published location to configure. Defaults to origin.
 
    .PARAMETER RemoteUrl
    Optional SSH URL. If omitted, Set-Ssh converts the existing HTTPS URL.
 
    .PARAMETER LogPath
    Override the directory where the diagnostic log for this run is written.
 
    .EXAMPLE
    Set-Ssh
 
    .EXAMPLE
    Set-Ssh -RemoteUrl 'git@github.com:example/repo.git'
 
    .NOTES
    Safety:
    - Do not commit private keys.
    - After Set-Ssh, run Test-Login to confirm key-based authentication works.
    - If SSH unexpectedly prompts for credentials, run Show-Remote and verify the URL.
 
    .LINK
    Test-Login
 
    .LINK
    Show-Remote
 
    .LINK
    Reset-Login
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([PSCustomObject])]
    param(
        [string]$RemoteName = 'origin',
        [string]$RemoteUrl,
        [string]$LogPath = ''
    )

    $repoRoot = $null
    try { $repoRoot = Get-GERepoRoot } catch { $repoRoot = $null }

    $session = Start-GELogSession -Command 'Set-Ssh' -Repository ([string]$repoRoot) -LogPath $LogPath

    $userMessageOnFailure = 'Could not configure the SSH published location.'

    try {
        if (-not $repoRoot) {
            $repoRoot = Get-GERepoRoot
        }

        if ([string]::IsNullOrWhiteSpace($RemoteUrl)) {
            $currentUrl = Get-GERemoteUrl -RemoteName $RemoteName -Path $repoRoot

            if ([string]::IsNullOrWhiteSpace($currentUrl)) {
                throw "Remote '$RemoteName' is not configured. Provide -RemoteUrl."
            }

            # F-04 defence in depth: validate the input read from .git/config
            # before passing to Convert-GERemoteToSsh. An embedded user:token@
            # in the read path would otherwise be silently carried through to
            # `git remote set-url` and the diagnostic log header. Convert
            # itself also refuses, but failing here gives a clear input-side
            # error rather than relying on the conversion helper's guard.
            Test-GERemoteUrlSafe -RemoteUrl $currentUrl | Out-Null

            $RemoteUrl = Convert-GERemoteToSsh -RemoteUrl $currentUrl
        }

        Test-GERemoteUrlSafe -RemoteUrl $RemoteUrl | Out-Null

        if ($RemoteUrl -notmatch '^git@[^:]+:.+$') {
            throw 'Set-Ssh requires an SSH remote URL or an existing HTTPS remote that can be converted.'
        }

        if (-not $PSCmdlet.ShouldProcess($repoRoot, "Set $RemoteName to SSH remote URL")) {
            Complete-GELogSession -Path $session.Path -Outcome 'SUCCESS' -UserMessage 'Skipped (WhatIf).'
            return
        }

        $existing = Get-GERemoteUrl -RemoteName $RemoteName -Path $repoRoot

        if ([string]::IsNullOrWhiteSpace($existing)) {
            Invoke-GEGit -ArgumentList @('remote', 'add', $RemoteName, $RemoteUrl) -WorkingDirectory $repoRoot -LogPath $session.Path | Out-Null
        }
        else {
            Invoke-GEGit -ArgumentList @('remote', 'set-url', $RemoteName, $RemoteUrl) -WorkingDirectory $repoRoot -LogPath $session.Path | Out-Null
        }

        $result = [PSCustomObject]@{
            Repository = $repoRoot
            Remote     = $RemoteName
            Url        = $RemoteUrl
            Message    = 'SSH remote configured. Run Test-Login to validate access.'
        }

        Complete-GELogSession -Path $session.Path -Outcome 'SUCCESS'
        return $result
    }
    catch {
        $err = $_
        $msg = if ($err.Exception.Message -like 'git *') { $userMessageOnFailure } else { $err.Exception.Message }
        Complete-GELogSession -Path $session.Path -Outcome 'FAILURE' -UserMessage $msg -ErrorMessage $err.Exception.Message
        throw "$msg Details: $($session.Path)"
    }
}