fix(ssh): handle WinError 1314 symlink failure with shutil.copy2 fallback

On Windows, os.symlink() raises OSError (WinError 1314) unless the
process has Administrator rights or Developer Mode is enabled. The SSH
bulk-upload staging logic used symlinks to mirror the remote layout
before piping through tar; this caused all ssh_bulk_upload tests to
fail on Windows.

- ssh.py: wrap os.symlink() in try/except OSError and fall back to
  shutil.copy2() so staging works on every platform. shutil was already
  imported, no new dependency introduced.
- file_sync.py: replace str(Path(remote).parent) with
  posixpath.dirname(remote) in unique_parent_dirs(). pathlib.Path uses
  the host separator (\ on Windows), but these paths are sent to a
  remote Linux host over SSH and must always use forward slashes.
- test_ssh_bulk_upload.py: make test_staging_symlinks_mirror_remote_layout
  platform-agnostic — assert file existence and content instead of
  os.path.islink() + os.readlink(), since the staged entry may be a
  copy on Windows.
This commit is contained in:
kewe63
2026-06-04 18:06:21 -07:00
committed by Teknium
parent ea44011d15
commit 46abf04012
3 changed files with 20 additions and 6 deletions
+6 -1
View File
@@ -179,6 +179,8 @@ class SSHEnvironment(BaseEnvironment):
raise RuntimeError(f"remote mkdir failed: {result.stderr.strip()}")
# Symlink staging avoids fragile GNU tar --transform rules.
# On Windows, symlink creation requires admin rights or Developer Mode,
# so fall back to copying the file when os.symlink raises OSError.
with tempfile.TemporaryDirectory(prefix="hermes-ssh-bulk-") as staging:
for host_path, remote_path in files:
try:
@@ -195,7 +197,10 @@ class SSHEnvironment(BaseEnvironment):
staged = os.path.join(staging, rel_remote)
os.makedirs(os.path.dirname(staged), exist_ok=True)
os.symlink(os.path.abspath(host_path), staged)
try:
os.symlink(os.path.abspath(host_path), staged)
except OSError:
shutil.copy2(host_path, staged)
tar_cmd = ["tar", "-chf", "-", "-C", staging, "."]
ssh_cmd = self._build_ssh_command()