Module: Twin::Remote
- Defined in:
- lib/twin/remote.rb
Overview
Remote (ssh) targets, written exactly as rsync understands them: "user@host:/path" or "host:/path". A target counts as remote when a colon appears before the first slash. Sources stay local — twin pushes.
Constant Summary collapse
- SSH_OPTS =
["-o", "BatchMode=yes", "-o", "ConnectTimeout=5"].freeze
- STAT_SCRIPT =
Stat many paths in one ssh round-trip. Paths go over stdin (one per line), the remote loop answers "path
epoch" or "path -" for missing ones. Tries BSD stat first, then GNU — covers macOS and Linux. Returns => Time or nil-if-missing, or nil when ssh itself failed. <<~SH.freeze while IFS= read -r p; do if [ -e "$p" ]; then printf '%s\t%s\n' "$p" "$(stat -f %m -- "$p" 2>/dev/null || stat -c %Y -- "$p")" else printf '%s\t-\n' "$p" fi done SH
Class Method Summary collapse
-
.mkdir_p(host, dir) ⇒ Object
Create a directory on the remote side (mkdir -p equivalent).
-
.reachable?(host) ⇒ Boolean
Non-interactive reachability probe (BatchMode: never asks for a password).
- .remote?(target) ⇒ Boolean
-
.shellesc(s) ⇒ Object
Escape one argument for the remote shell (ssh joins args with spaces and hands the string to a shell — local exec-style arrays don't protect it).
-
.split(target) ⇒ Object
"user@host:/path" → ["user@host", "/path"].
- .stat_paths(host, paths) ⇒ Object
Class Method Details
.mkdir_p(host, dir) ⇒ Object
Create a directory on the remote side (mkdir -p equivalent).
61 62 63 64 |
# File 'lib/twin/remote.rb', line 61 def mkdir_p(host, dir) _out, _err, status = Open3.capture3("ssh", *SSH_OPTS, host, "mkdir", "-p", shellesc(dir)) status.success? end |
.reachable?(host) ⇒ Boolean
Non-interactive reachability probe (BatchMode: never asks for a password).
23 24 25 |
# File 'lib/twin/remote.rb', line 23 def reachable?(host) system("ssh", *SSH_OPTS, host, "true", out: File::NULL, err: File::NULL) end |
.remote?(target) ⇒ Boolean
12 13 14 |
# File 'lib/twin/remote.rb', line 12 def remote?(target) %r{\A[^/]+:}.match?(target.to_s) end |
.shellesc(s) ⇒ Object
Escape one argument for the remote shell (ssh joins args with spaces and hands the string to a shell — local exec-style arrays don't protect it).
68 69 70 |
# File 'lib/twin/remote.rb', line 68 def shellesc(s) "'" + s.gsub("'", "'\\\\''") + "'" end |
.split(target) ⇒ Object
"user@host:/path" → ["user@host", "/path"]
17 18 19 20 |
# File 'lib/twin/remote.rb', line 17 def split(target) host, path = target.to_s.split(":", 2) [host, path.to_s] end |
.stat_paths(host, paths) ⇒ Object
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/twin/remote.rb', line 41 def stat_paths(host, paths) return {} if paths.empty? out, _err, status = Open3.capture3( "ssh", *SSH_OPTS, host, STAT_SCRIPT, stdin_data: paths.join("\n") + "\n" ) return nil unless status.success? result = {} out.each_line do |line| path, mtime = line.chomp.split("\t", 2) next unless path && mtime result[path] = mtime == "-" ? nil : Time.at(mtime.to_i) end result rescue Errno::ENOENT nil # ssh not installed end |