206 lines
5.6 KiB
Bash
206 lines
5.6 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
HYPERTWIST_REMOTE_WINDOWS_PASSWORD=... \
|
|
scripts/run-hypertwist-remote-windows-file-sync.sh \
|
|
--file UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp
|
|
|
|
Options:
|
|
--file <path>
|
|
Relative repo path to copy into the remote Windows worktree. May be repeated.
|
|
|
|
--remote-root <path>
|
|
Optional Windows worktree root. Defaults to C:\HyperTwist_worktrees\phase10validate.
|
|
|
|
--dry-run
|
|
Print the copy plan without transferring files.
|
|
|
|
Environment:
|
|
HYPERTWIST_REMOTE_WINDOWS_PASSWORD Required password for the reverse-SSH Windows user.
|
|
HYPERTWIST_REMOTE_TUNNEL_PORT Optional, defaults to 22022.
|
|
HYPERTWIST_REMOTE_WINDOWS_USER Optional, defaults to "anthracite ace".
|
|
HYPERTWIST_REMOTE_TUNNEL_HOST Optional, defaults to "localhost".
|
|
EOF
|
|
}
|
|
|
|
if ! command -v sshpass >/dev/null 2>&1; then
|
|
echo "sshpass is required but was not found on PATH." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v sha256sum >/dev/null 2>&1; then
|
|
echo "sha256sum is required but was not found on PATH." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v scp >/dev/null 2>&1; then
|
|
echo "scp is required but was not found on PATH." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v python3 >/dev/null 2>&1; then
|
|
echo "python3 is required but was not found on PATH." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "${HYPERTWIST_REMOTE_WINDOWS_PASSWORD:-}" ]]; then
|
|
echo "Set HYPERTWIST_REMOTE_WINDOWS_PASSWORD before using this wrapper." >&2
|
|
exit 1
|
|
fi
|
|
|
|
remote_root='C:\HyperTwist_worktrees\phase10validate'
|
|
dry_run="false"
|
|
files=()
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--file)
|
|
files+=("${2:-}")
|
|
shift 2
|
|
;;
|
|
--remote-root)
|
|
remote_root="${2:-}"
|
|
shift 2
|
|
;;
|
|
--dry-run)
|
|
dry_run="true"
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
usage >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ ${#files[@]} -eq 0 ]]; then
|
|
echo "Provide at least one --file." >&2
|
|
exit 1
|
|
fi
|
|
|
|
remote_port="${HYPERTWIST_REMOTE_TUNNEL_PORT:-22022}"
|
|
remote_user="${HYPERTWIST_REMOTE_WINDOWS_USER:-anthracite ace}"
|
|
remote_host="${HYPERTWIST_REMOTE_TUNNEL_HOST:-localhost}"
|
|
|
|
encode_powershell_command() {
|
|
local script_text="$1"
|
|
|
|
python3 - <<'PY' "$script_text"
|
|
import base64
|
|
import sys
|
|
|
|
script = sys.argv[1]
|
|
sys.stdout.write(base64.b64encode(script.encode("utf-16le")).decode("ascii"))
|
|
PY
|
|
}
|
|
|
|
ps_single_quote() {
|
|
printf "'%s'" "${1//\'/\'\'}"
|
|
}
|
|
|
|
ssh_base=(
|
|
sshpass -p "${HYPERTWIST_REMOTE_WINDOWS_PASSWORD}"
|
|
ssh
|
|
-o StrictHostKeyChecking=no
|
|
-o PreferredAuthentications=password
|
|
-o PubkeyAuthentication=no
|
|
-p "${remote_port}"
|
|
-l "${remote_user}"
|
|
"${remote_host}"
|
|
)
|
|
|
|
scp_base=(
|
|
sshpass -p "${HYPERTWIST_REMOTE_WINDOWS_PASSWORD}"
|
|
scp
|
|
-q
|
|
-o StrictHostKeyChecking=no
|
|
-o PreferredAuthentications=password
|
|
-o PubkeyAuthentication=no
|
|
-P "${remote_port}"
|
|
)
|
|
|
|
for relative_path in "${files[@]}"; do
|
|
if [[ "$relative_path" = /* ]]; then
|
|
echo "Use repo-relative paths only: $relative_path" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$relative_path" ]]; then
|
|
echo "File not found: $relative_path" >&2
|
|
exit 1
|
|
fi
|
|
|
|
windows_relative_path="${relative_path//\//\\}"
|
|
windows_dest_path="${remote_root}\\${windows_relative_path}"
|
|
local_hash="$(sha256sum "$relative_path" | awk '{print tolower($1)}')"
|
|
windows_incoming_path="${windows_dest_path}.hypertwist-incoming-${local_hash:0:12}"
|
|
|
|
if [[ "$dry_run" == "true" ]]; then
|
|
printf '%s -> %s (%s)\n' "$relative_path" "$windows_dest_path" "$local_hash"
|
|
continue
|
|
fi
|
|
|
|
remote_prepare_script="$(
|
|
cat <<EOF
|
|
\$ProgressPreference = 'SilentlyContinue'
|
|
\$ErrorActionPreference = 'Stop'
|
|
\$Path = $(ps_single_quote "$windows_dest_path")
|
|
\$IncomingPath = $(ps_single_quote "$windows_incoming_path")
|
|
\$dir = [System.IO.Path]::GetDirectoryName(\$Path)
|
|
if (\$dir) {
|
|
[System.IO.Directory]::CreateDirectory(\$dir) | Out-Null
|
|
}
|
|
if (Test-Path -LiteralPath \$IncomingPath) {
|
|
Remove-Item -LiteralPath \$IncomingPath -Force
|
|
}
|
|
EOF
|
|
)"
|
|
encoded_remote_prepare_script="$(encode_powershell_command "$remote_prepare_script")"
|
|
"${ssh_base[@]}" "powershell -NoProfile -EncodedCommand ${encoded_remote_prepare_script}" >/dev/null
|
|
|
|
incoming_scp_path="${windows_incoming_path//\\//}"
|
|
"${scp_base[@]}" \
|
|
"$relative_path" \
|
|
"${remote_user}@${remote_host}:${incoming_scp_path}"
|
|
|
|
remote_finalize_script="$(
|
|
cat <<EOF
|
|
\$ErrorActionPreference = 'Stop'
|
|
\$Path = $(ps_single_quote "$windows_dest_path")
|
|
\$IncomingPath = $(ps_single_quote "$windows_incoming_path")
|
|
\$ExpectedHash = '${local_hash}'
|
|
if (-not (Test-Path -LiteralPath \$IncomingPath -PathType Leaf)) {
|
|
throw "Incoming upload was not found at '\$IncomingPath'."
|
|
}
|
|
\$IncomingHash = (Get-FileHash -LiteralPath \$IncomingPath -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
if (\$IncomingHash -ne \$ExpectedHash) {
|
|
Remove-Item -LiteralPath \$IncomingPath -Force
|
|
throw "Incoming hash mismatch: expected \$ExpectedHash but received \$IncomingHash."
|
|
}
|
|
Move-Item -LiteralPath \$IncomingPath -Destination \$Path -Force
|
|
Write-Output ('REMOTE_HASH=' + (Get-FileHash -LiteralPath \$Path -Algorithm SHA256).Hash.ToLowerInvariant())
|
|
EOF
|
|
)"
|
|
encoded_remote_finalize_script="$(encode_powershell_command "$remote_finalize_script")"
|
|
remote_hash="$(
|
|
"${ssh_base[@]}" "powershell -NoProfile -EncodedCommand ${encoded_remote_finalize_script}" \
|
|
| tr -d '\r' \
|
|
| sed -n 's/^REMOTE_HASH=//p'
|
|
)"
|
|
|
|
if [[ "$remote_hash" != "$local_hash" ]]; then
|
|
echo "Hash mismatch for $relative_path" >&2
|
|
echo " local : $local_hash" >&2
|
|
echo " remote: $remote_hash" >&2
|
|
exit 1
|
|
fi
|
|
|
|
printf 'Synced %s -> %s (%s)\n' "$relative_path" "$windows_dest_path" "$local_hash"
|
|
done
|