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.
 
    Steps:
    1. Probe for the project folder root and start a diagnostic log session.
    2. If no URL was provided, read the existing HTTPS URL and convert it to SSH form.
    3. Validate that the final URL does not embed credentials and matches the expected SSH format.
    4. Handle WhatIf and return early if applicable.
    5. Set or add the published location with the SSH URL.
    6. Return a structured result with the remote name and configured 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)"
    }
}