Add one-click deployment scripts
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet("deploy", "preflight", "status", "rollback")]
|
||||
[string]$Action = "deploy",
|
||||
|
||||
[string]$ProjectPath,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidatePattern("^[A-Za-z0-9][A-Za-z0-9_-]*$")]
|
||||
[string]$AppName,
|
||||
|
||||
[ValidateSet("auto", "compose", "dockerfile")]
|
||||
[string]$Mode = "auto",
|
||||
|
||||
[string]$ServerHost = "192.168.31.51",
|
||||
[string]$ServerUser = "jsxq",
|
||||
[int]$SshPort = 22,
|
||||
[string]$DeployRoot = "/home/jsxq/apps",
|
||||
[string]$HealthUrl = "",
|
||||
[int]$PublishPort = 0,
|
||||
[int]$ContainerPort = 0,
|
||||
[switch]$SkipHealthCheck,
|
||||
[switch]$KeepArchive
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$script:ArchivePath = $null
|
||||
$script:KnownHostsFile = Join-Path ([IO.Path]::GetTempPath()) "codex-company-linux-known-hosts"
|
||||
$AppName = $AppName.ToLowerInvariant()
|
||||
|
||||
function Require-Command([string]$Name) {
|
||||
if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
|
||||
throw "Required command not found: $Name"
|
||||
}
|
||||
}
|
||||
|
||||
function Quote-Sh([string]$Value) {
|
||||
return "'" + $Value.Replace("'", "'`"'`"'") + "'"
|
||||
}
|
||||
|
||||
function Invoke-Ssh([string]$RemoteCommand, [switch]$BatchMode) {
|
||||
$arguments = @("-T", "-p", "$SshPort", "-o", "StrictHostKeyChecking=accept-new", "-o", "UserKnownHostsFile=$script:KnownHostsFile", "-o", "ConnectTimeout=10")
|
||||
if ($BatchMode) {
|
||||
$arguments += @("-o", "BatchMode=yes")
|
||||
} else {
|
||||
$arguments += @("-o", "NumberOfPasswordPrompts=1")
|
||||
}
|
||||
$arguments += @("$ServerUser@$ServerHost", $RemoteCommand)
|
||||
& ssh.exe @arguments
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "SSH command failed with exit code $LASTEXITCODE"
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-Scp([string]$LocalPath, [string]$RemotePath) {
|
||||
& scp.exe -P $SshPort -o StrictHostKeyChecking=accept-new -o "UserKnownHostsFile=$script:KnownHostsFile" -o ConnectTimeout=10 -o NumberOfPasswordPrompts=1 $LocalPath "$ServerUser@$ServerHost`:$RemotePath"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "SCP upload failed with exit code $LASTEXITCODE"
|
||||
}
|
||||
}
|
||||
|
||||
function Enable-AskPass {
|
||||
if ([string]::IsNullOrWhiteSpace($env:COMPANY_LINUX_PASSWORD)) {
|
||||
return $false
|
||||
}
|
||||
$env:SSH_ASKPASS = Join-Path $PSScriptRoot "ssh-askpass.cmd"
|
||||
$env:SSH_ASKPASS_REQUIRE = "force"
|
||||
$env:DISPLAY = "codex"
|
||||
return $true
|
||||
}
|
||||
|
||||
Require-Command "ssh.exe"
|
||||
|
||||
$oldAskPass = $env:SSH_ASKPASS
|
||||
$oldAskPassRequire = $env:SSH_ASKPASS_REQUIRE
|
||||
$oldDisplay = $env:DISPLAY
|
||||
$usingPassword = Enable-AskPass
|
||||
|
||||
try {
|
||||
if (-not $usingPassword) {
|
||||
& ssh.exe -T -p $SshPort -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o "UserKnownHostsFile=$script:KnownHostsFile" -o ConnectTimeout=8 "$ServerUser@$ServerHost" "true"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "SSH key authentication failed. Run setup-ssh-key.ps1 or set COMPANY_LINUX_PASSWORD only for this process."
|
||||
}
|
||||
}
|
||||
|
||||
if ($Action -eq "preflight") {
|
||||
$preflight = "set -eu; echo '=== identity ==='; id; echo '=== runtime ==='; docker --version; docker compose version; echo '=== disk ==='; df -h /; echo '=== used ports ==='; docker ps --format '{{.Names}}|{{.Ports}}'; echo '=== target ==='; if [ -e " + (Quote-Sh "$DeployRoot/$AppName") + " ]; then ls -ld " + (Quote-Sh "$DeployRoot/$AppName") + "; else echo 'new application'; fi"
|
||||
Invoke-Ssh $preflight -BatchMode:(-not $usingPassword)
|
||||
return
|
||||
}
|
||||
|
||||
if ($Action -eq "status") {
|
||||
$projectName = "codex-$($AppName.ToLowerInvariant())"
|
||||
$status = "set -eu; app_root=" + (Quote-Sh "$DeployRoot/$AppName") + "; echo '=== release ==='; if [ -L `"`$app_root/current`" ]; then readlink -f `"`$app_root/current`"; else echo 'not deployed'; fi; echo '=== containers ==='; docker ps -a --filter " + (Quote-Sh "label=com.docker.compose.project=$projectName") + " --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'; docker ps -a --filter " + (Quote-Sh "name=^/${projectName}-app$") + " --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'"
|
||||
Invoke-Ssh $status -BatchMode:(-not $usingPassword)
|
||||
return
|
||||
}
|
||||
|
||||
Require-Command "scp.exe"
|
||||
$remoteScript = "/tmp/codex-remote-deploy-$AppName-$([Guid]::NewGuid().ToString('N')).sh"
|
||||
Invoke-Scp (Join-Path $PSScriptRoot "remote-deploy.sh") $remoteScript
|
||||
|
||||
if ($Action -eq "rollback") {
|
||||
$command = "bash " + (Quote-Sh $remoteScript) + " rollback " + (Quote-Sh $AppName) + " '' auto " + (Quote-Sh $DeployRoot) + " '' 1 0 0; rc=`$?; rm -f " + (Quote-Sh $remoteScript) + "; exit `$rc"
|
||||
Invoke-Ssh $command -BatchMode:(-not $usingPassword)
|
||||
return
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($ProjectPath)) {
|
||||
throw "ProjectPath is required for deploy."
|
||||
}
|
||||
|
||||
Require-Command "tar.exe"
|
||||
$resolvedProject = (Resolve-Path -LiteralPath $ProjectPath).Path
|
||||
if (-not (Test-Path -LiteralPath $resolvedProject -PathType Container)) {
|
||||
throw "Project path is not a directory: $resolvedProject"
|
||||
}
|
||||
|
||||
$composeNames = @("compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml")
|
||||
$composeFile = $composeNames | Where-Object { Test-Path -LiteralPath (Join-Path $resolvedProject $_) } | Select-Object -First 1
|
||||
$effectiveMode = $Mode
|
||||
if ($Mode -eq "auto") {
|
||||
if ($composeFile) {
|
||||
$effectiveMode = "compose"
|
||||
} elseif (Test-Path -LiteralPath (Join-Path $resolvedProject "Dockerfile")) {
|
||||
$effectiveMode = "dockerfile"
|
||||
} else {
|
||||
throw "No Compose file or Dockerfile found in $resolvedProject"
|
||||
}
|
||||
}
|
||||
if ($effectiveMode -eq "compose" -and -not $composeFile) {
|
||||
throw "Compose mode selected, but no Compose file was found."
|
||||
}
|
||||
if ($effectiveMode -eq "dockerfile" -and -not (Test-Path -LiteralPath (Join-Path $resolvedProject "Dockerfile"))) {
|
||||
throw "Dockerfile mode selected, but Dockerfile was not found."
|
||||
}
|
||||
if ($effectiveMode -eq "dockerfile" -and (($PublishPort -eq 0) -xor ($ContainerPort -eq 0))) {
|
||||
throw "Provide both PublishPort and ContainerPort, or neither."
|
||||
}
|
||||
|
||||
$tempRoot = Join-Path ([IO.Path]::GetTempPath()) "codex-company-deploy"
|
||||
New-Item -ItemType Directory -Path $tempRoot -Force | Out-Null
|
||||
$script:ArchivePath = Join-Path $tempRoot "$AppName-$([DateTime]::UtcNow.ToString('yyyyMMddTHHmmssZ'))-$([Guid]::NewGuid().ToString('N')).tgz"
|
||||
& tar.exe -czf $script:ArchivePath --exclude=.git --exclude=node_modules --exclude=.venv --exclude=__pycache__ --exclude=.idea --exclude=.vscode --exclude=.codex -C $resolvedProject .
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Unable to create deployment archive."
|
||||
}
|
||||
|
||||
$remoteArchive = "/tmp/$([IO.Path]::GetFileName($script:ArchivePath))"
|
||||
Invoke-Scp $script:ArchivePath $remoteArchive
|
||||
|
||||
$skipFlag = if ($SkipHealthCheck) { "1" } else { "0" }
|
||||
$commandParts = @(
|
||||
"bash", (Quote-Sh $remoteScript), "deploy", (Quote-Sh $AppName),
|
||||
(Quote-Sh $remoteArchive), (Quote-Sh $effectiveMode), (Quote-Sh $DeployRoot),
|
||||
(Quote-Sh $HealthUrl), $skipFlag, "$PublishPort", "$ContainerPort"
|
||||
)
|
||||
$command = ($commandParts -join " ") + "; rc=`$?; rm -f " + (Quote-Sh $remoteScript) + " " + (Quote-Sh $remoteArchive) + "; exit `$rc"
|
||||
Invoke-Ssh $command -BatchMode:(-not $usingPassword)
|
||||
} finally {
|
||||
$env:SSH_ASKPASS = $oldAskPass
|
||||
$env:SSH_ASKPASS_REQUIRE = $oldAskPassRequire
|
||||
$env:DISPLAY = $oldDisplay
|
||||
if ($script:ArchivePath -and (Test-Path -LiteralPath $script:ArchivePath) -and -not $KeepArchive) {
|
||||
Remove-Item -LiteralPath $script:ArchivePath -Force
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
action="${1:-}"
|
||||
app_name="${2:-}"
|
||||
archive="${3:-}"
|
||||
mode="${4:-auto}"
|
||||
deploy_root="${5:-/home/jsxq/apps}"
|
||||
health_url="${6:-}"
|
||||
skip_health="${7:-0}"
|
||||
publish_port="${8:-0}"
|
||||
container_port="${9:-0}"
|
||||
|
||||
if [[ ! "$app_name" =~ ^[A-Za-z0-9][A-Za-z0-9_-]*$ ]]; then
|
||||
echo "Invalid application name" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
expected_prefix="/home/${USER}/apps"
|
||||
if [[ "$deploy_root" != "$expected_prefix" ]]; then
|
||||
echo "Refusing deployment outside $expected_prefix" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
app_name="${app_name,,}"
|
||||
project_name="codex-${app_name}"
|
||||
app_root="${deploy_root}/${app_name}"
|
||||
releases_dir="${app_root}/releases"
|
||||
current_link="${app_root}/current"
|
||||
previous_link="${app_root}/previous"
|
||||
|
||||
find_compose_file() {
|
||||
local directory="$1"
|
||||
local candidate
|
||||
for candidate in compose.yaml compose.yml docker-compose.yaml docker-compose.yml; do
|
||||
if [[ -f "${directory}/${candidate}" ]]; then
|
||||
printf '%s\n' "${directory}/${candidate}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
compose_up() {
|
||||
local directory="$1"
|
||||
local compose_file
|
||||
compose_file="$(find_compose_file "$directory")"
|
||||
(cd "$directory" && docker compose -p "$project_name" -f "$compose_file" up -d --build --remove-orphans)
|
||||
}
|
||||
|
||||
compose_down() {
|
||||
local directory="$1"
|
||||
local compose_file
|
||||
compose_file="$(find_compose_file "$directory" || true)"
|
||||
if [[ -z "$compose_file" ]]; then
|
||||
return 0
|
||||
fi
|
||||
(cd "$directory" && docker compose -p "$project_name" -f "$compose_file" down --remove-orphans) || true
|
||||
}
|
||||
|
||||
swap_release_links() {
|
||||
local from="$1"
|
||||
local to="$2"
|
||||
ln -sfn "$to" "$current_link"
|
||||
ln -sfn "$from" "$previous_link"
|
||||
}
|
||||
|
||||
rollback_release() {
|
||||
if [[ ! -L "$previous_link" ]]; then
|
||||
echo "No previous release is available" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local current_release previous_release current_mode previous_mode
|
||||
current_release="$(readlink -f "$current_link" 2>/dev/null || true)"
|
||||
previous_release="$(readlink -f "$previous_link")"
|
||||
current_mode=""
|
||||
if [[ -n "$current_release" && -f "${current_release}/.codex-deploy-mode" ]]; then
|
||||
current_mode="$(cat "${current_release}/.codex-deploy-mode")"
|
||||
fi
|
||||
previous_mode="$(cat "${previous_release}/.codex-deploy-mode")"
|
||||
|
||||
if [[ "$current_mode" == "compose" && -n "$current_release" ]]; then
|
||||
compose_down "$current_release"
|
||||
elif [[ "$current_mode" == "dockerfile" ]]; then
|
||||
docker rm -f "${project_name}-app" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
if [[ "$previous_mode" == "compose" ]]; then
|
||||
compose_up "$previous_release"
|
||||
elif [[ "$previous_mode" == "dockerfile" ]]; then
|
||||
if docker container inspect "${project_name}-previous" >/dev/null 2>&1; then
|
||||
docker rename "${project_name}-previous" "${project_name}-app"
|
||||
docker start "${project_name}-app" >/dev/null
|
||||
else
|
||||
echo "Previous Dockerfile container is unavailable" >&2
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo "Unknown previous deployment mode: $previous_mode" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
swap_release_links "$current_release" "$previous_release"
|
||||
echo "ROLLBACK_STATUS=success"
|
||||
echo "CURRENT_RELEASE=$previous_release"
|
||||
}
|
||||
|
||||
if [[ "$action" == "rollback" ]]; then
|
||||
rollback_release
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$action" != "deploy" ]]; then
|
||||
echo "Expected deploy or rollback action" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -f "$archive" ]]; then
|
||||
echo "Deployment archive not found: $archive" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
command -v docker >/dev/null
|
||||
docker compose version >/dev/null
|
||||
|
||||
mkdir -p "$releases_dir"
|
||||
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
release_dir="${releases_dir}/${timestamp}"
|
||||
if [[ -e "$release_dir" ]]; then
|
||||
release_dir="${release_dir}-$RANDOM"
|
||||
fi
|
||||
mkdir -p "$release_dir"
|
||||
tar -xzf "$archive" -C "$release_dir"
|
||||
printf '%s\n' "$mode" > "${release_dir}/.codex-deploy-mode"
|
||||
|
||||
old_release="$(readlink -f "$current_link" 2>/dev/null || true)"
|
||||
old_mode=""
|
||||
if [[ -n "$old_release" ]]; then
|
||||
if [[ -f "${old_release}/.codex-deploy-mode" ]]; then
|
||||
old_mode="$(cat "${old_release}/.codex-deploy-mode")"
|
||||
fi
|
||||
ln -sfn "$old_release" "$previous_link"
|
||||
fi
|
||||
ln -sfn "$release_dir" "$current_link"
|
||||
|
||||
deploy_failed=0
|
||||
if [[ "$mode" == "compose" ]]; then
|
||||
if [[ "$old_mode" == "dockerfile" ]] && docker container inspect "${project_name}-app" >/dev/null 2>&1; then
|
||||
docker rm -f "${project_name}-previous" >/dev/null 2>&1 || true
|
||||
docker stop "${project_name}-app" >/dev/null || true
|
||||
docker rename "${project_name}-app" "${project_name}-previous"
|
||||
fi
|
||||
if ! find_compose_file "$release_dir" >/dev/null; then
|
||||
echo "Compose file not found in release" >&2
|
||||
deploy_failed=1
|
||||
elif ! compose_up "$release_dir"; then
|
||||
deploy_failed=1
|
||||
fi
|
||||
elif [[ "$mode" == "dockerfile" ]]; then
|
||||
if [[ "$old_mode" == "compose" && -n "$old_release" ]]; then
|
||||
compose_down "$old_release"
|
||||
fi
|
||||
if [[ ! -f "${release_dir}/Dockerfile" ]]; then
|
||||
echo "Dockerfile not found in release" >&2
|
||||
deploy_failed=1
|
||||
else
|
||||
image_name="${project_name}:${timestamp}"
|
||||
if ! (cd "$release_dir" && docker build -t "$image_name" .); then
|
||||
deploy_failed=1
|
||||
else
|
||||
docker rm -f "${project_name}-previous" >/dev/null 2>&1 || true
|
||||
if docker container inspect "${project_name}-app" >/dev/null 2>&1; then
|
||||
docker stop "${project_name}-app" >/dev/null || true
|
||||
docker rename "${project_name}-app" "${project_name}-previous"
|
||||
fi
|
||||
run_args=(--detach --name "${project_name}-app" --restart unless-stopped)
|
||||
if [[ "$publish_port" != "0" && "$container_port" != "0" ]]; then
|
||||
run_args+=(--publish "${publish_port}:${container_port}")
|
||||
fi
|
||||
if [[ -f "${release_dir}/.env" ]]; then
|
||||
run_args+=(--env-file "${release_dir}/.env")
|
||||
fi
|
||||
if ! docker run "${run_args[@]}" "$image_name"; then
|
||||
deploy_failed=1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Unsupported deployment mode: $mode" >&2
|
||||
deploy_failed=1
|
||||
fi
|
||||
|
||||
if [[ "$deploy_failed" == "0" && "$skip_health" != "1" ]]; then
|
||||
sleep 3
|
||||
if [[ -n "$health_url" ]]; then
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl --fail --silent --show-error --max-time 15 "$health_url" >/dev/null || deploy_failed=1
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget --quiet --timeout=15 --spider "$health_url" || deploy_failed=1
|
||||
else
|
||||
echo "No curl or wget is available for health checks" >&2
|
||||
deploy_failed=1
|
||||
fi
|
||||
elif [[ "$mode" == "compose" ]]; then
|
||||
compose_file="$(find_compose_file "$release_dir")"
|
||||
(cd "$release_dir" && docker compose -p "$project_name" -f "$compose_file" ps --status running -q | grep -q .) || deploy_failed=1
|
||||
else
|
||||
[[ "$(docker inspect -f '{{.State.Running}}' "${project_name}-app" 2>/dev/null || true)" == "true" ]] || deploy_failed=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$deploy_failed" != "0" ]]; then
|
||||
echo "Deployment or health check failed" >&2
|
||||
if [[ -n "$old_release" && -L "$previous_link" ]]; then
|
||||
rollback_release || true
|
||||
echo "AUTO_ROLLBACK=attempted" >&2
|
||||
else
|
||||
if [[ "$mode" == "compose" ]]; then
|
||||
compose_down "$release_dir"
|
||||
else
|
||||
docker rm -f "${project_name}-app" >/dev/null 2>&1 || true
|
||||
fi
|
||||
rm -f "$current_link"
|
||||
echo "AUTO_ROLLBACK=unavailable" >&2
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "DEPLOY_STATUS=success"
|
||||
echo "APP_NAME=$app_name"
|
||||
echo "MODE=$mode"
|
||||
echo "CURRENT_RELEASE=$release_dir"
|
||||
if [[ -n "$health_url" ]]; then
|
||||
echo "HEALTH_URL=$health_url"
|
||||
fi
|
||||
@@ -0,0 +1,68 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$ServerHost = "192.168.31.51",
|
||||
[string]$ServerUser = "jsxq",
|
||||
[int]$SshPort = 22
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$knownHostsFile = Join-Path ([IO.Path]::GetTempPath()) "codex-company-linux-known-hosts"
|
||||
|
||||
function Require-Command([string]$Name) {
|
||||
if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
|
||||
throw "Required command not found: $Name"
|
||||
}
|
||||
}
|
||||
|
||||
Require-Command "ssh.exe"
|
||||
Require-Command "ssh-keygen.exe"
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($env:COMPANY_LINUX_PASSWORD)) {
|
||||
throw "Set COMPANY_LINUX_PASSWORD in the current process before running first-time key setup."
|
||||
}
|
||||
|
||||
$sshDirectory = Join-Path $env:USERPROFILE ".ssh"
|
||||
$privateKey = Join-Path $sshDirectory "id_ed25519"
|
||||
$publicKey = "$privateKey.pub"
|
||||
|
||||
if (-not (Test-Path -LiteralPath $publicKey)) {
|
||||
New-Item -ItemType Directory -Path $sshDirectory -Force | Out-Null
|
||||
& ssh-keygen.exe -q -t ed25519 -f $privateKey -N "" -C "$env:USERNAME@codex-company-linux"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "ssh-keygen failed with exit code $LASTEXITCODE"
|
||||
}
|
||||
}
|
||||
|
||||
$keyText = (Get-Content -LiteralPath $publicKey -Raw).Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($keyText)) {
|
||||
throw "Public key is empty: $publicKey"
|
||||
}
|
||||
|
||||
$keyBase64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($keyText))
|
||||
$askPass = Join-Path $PSScriptRoot "ssh-askpass.cmd"
|
||||
$oldAskPass = $env:SSH_ASKPASS
|
||||
$oldAskPassRequire = $env:SSH_ASKPASS_REQUIRE
|
||||
$oldDisplay = $env:DISPLAY
|
||||
|
||||
try {
|
||||
$env:SSH_ASKPASS = $askPass
|
||||
$env:SSH_ASKPASS_REQUIRE = "force"
|
||||
$env:DISPLAY = "codex"
|
||||
|
||||
$remote = "set -eu; umask 077; mkdir -p ~/.ssh; touch ~/.ssh/authorized_keys; key=`$(printf '%s' '$keyBase64' | base64 -d); grep -qxF -- `"`$key`" ~/.ssh/authorized_keys || printf '%s\n' `"`$key`" >> ~/.ssh/authorized_keys; chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys"
|
||||
& ssh.exe -T -p $SshPort -o StrictHostKeyChecking=accept-new -o "UserKnownHostsFile=$knownHostsFile" -o ConnectTimeout=10 -o NumberOfPasswordPrompts=1 "$ServerUser@$ServerHost" $remote
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Unable to install the SSH public key."
|
||||
}
|
||||
} finally {
|
||||
$env:SSH_ASKPASS = $oldAskPass
|
||||
$env:SSH_ASKPASS_REQUIRE = $oldAskPassRequire
|
||||
$env:DISPLAY = $oldDisplay
|
||||
}
|
||||
|
||||
& ssh.exe -T -p $SshPort -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o "UserKnownHostsFile=$knownHostsFile" -o ConnectTimeout=10 "$ServerUser@$ServerHost" "echo SSH_KEY_READY"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "The key was installed, but key authentication verification failed."
|
||||
}
|
||||
|
||||
Write-Output "SSH key authentication is ready for $ServerUser@$ServerHost."
|
||||
Reference in New Issue
Block a user