fix: rsync double SSH spec in RunRemote, restore dropped flags in Run

- Fix Bug A: RunRemote was re-prefixing destination with user@host: when
  engine.go:135 already pre-prefixed it for remote-to-remote, causing
  "admin@host:admin@host:/path" to be passed to rsync
- Fix Bug B: Run used args[1:] when inserting -e ssh, silently dropping
  the first rsync flag (e.g. -aP became flag-less)
- Fix Bug C: RunRemote extracted source/dest in wrong order for pull
  direction (buildArgs reverses them but RunRemote assumed push order)
- Add rsync_runner_test.go covering buildArgs, RunRemote prefixing,
  and flag preservation
This commit is contained in:
2026-07-09 21:51:05 -04:00
parent 969ccacfc8
commit 147c6d8ead
4 changed files with 295 additions and 11 deletions
+34 -9
View File
@@ -40,16 +40,19 @@ func NewRsyncRunner(sshDir, privKey string) *RsyncRunner {
}
func (r *RsyncRunner) Run(ctx context.Context, pair *SyncPairConfig, onLine func(stream string, line string)) (*RsyncResult, error) {
args := r.buildArgs(pair)
cmd := r.buildRsyncCmd(pair)
return r.runCmd(ctx, cmd, onLine)
}
cmd := exec.CommandContext(ctx, "rsync", args...)
func (r *RsyncRunner) buildRsyncCmd(pair *SyncPairConfig) *exec.Cmd {
args := r.buildArgs(pair)
cmd := exec.CommandContext(context.Background(), "rsync", args...)
if r.privKey != "" {
sshCmd := fmt.Sprintf("ssh -i %s -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=%s",
r.privKey, strings.TrimRight(r.sshDir, "/")+"/known_hosts")
cmd.Args = append([]string{"rsync", "-e", sshCmd}, args[1:]...)
cmd.Args = append([]string{"rsync", "-e", sshCmd}, args...)
}
return r.runCmd(ctx, cmd, onLine)
return cmd
}
func (r *RsyncRunner) buildArgs(pair *SyncPairConfig) []string {
@@ -93,17 +96,39 @@ func (r *RsyncRunner) RunRemote(ctx context.Context, pair *SyncPairConfig, src *
destKey = filepath.Join(r.sshDir, "id_ed25519")
}
destUserHost := fmt.Sprintf("%s@%s", dst.SSHUser, dst.Host)
srcUserHost := fmt.Sprintf("%s@%s", src.SSHUser, src.Host)
args := r.buildArgs(pair)
innerSSH := fmt.Sprintf("ssh -i %s -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=%s",
destKey, filepath.Join(r.sshDir, "known_hosts"))
rsyncFlags := strings.Join(args[:len(args)-2], " ")
sourcePath := args[len(args)-2]
destPath := args[len(args)-1]
var sourcePath, destPath string
if pair.Direction == "pull" {
sourcePath = args[len(args)-1]
destPath = args[len(args)-2]
} else {
sourcePath = args[len(args)-2]
destPath = args[len(args)-1]
}
var remoteSrc, remoteDst string
if pair.Direction == "pull" {
remoteSrc = sourcePath
remoteDst = destPath
if !strings.Contains(destPath, "@") {
remoteDst = fmt.Sprintf("%s@%s:%s", dst.SSHUser, dst.Host, destPath)
}
} else {
remoteDst = destPath
if !strings.Contains(destPath, "@") {
remoteDst = fmt.Sprintf("%s@%s:%s", dst.SSHUser, dst.Host, destPath)
}
remoteSrc = fmt.Sprintf("%s:%s", srcUserHost, sourcePath)
}
remoteCmd := fmt.Sprintf("rsync %s -e %q %s %s",
rsyncFlags, innerSSH, sourcePath, destUserHost+":"+destPath)
rsyncFlags, innerSSH, remoteSrc, remoteDst)
sshArgs := []string{
"-i", src.PrivKey,