vagrant/plugins/synced_folders/rsync/helper.rb

118 lines
3.8 KiB
Ruby

require "vagrant/util/platform"
require "vagrant/util/subprocess"
module VagrantPlugins
module SyncedFolderRSync
# This is a helper that abstracts out the functionality of rsyncing
# folders so that it can be called from anywhere.
class RsyncHelper
# This converts an rsync exclude pattern to a regular expression
# we can send to Listen.
def self.exclude_to_regexp(path, exclude)
start_anchor = false
if exclude.start_with?("/")
start_anchor = true
exclude = exclude[1..-1]
end
path = "#{path}/" if !path.end_with?("/")
regexp = "^#{Regexp.escape(path)}"
regexp += ".*" if !start_anchor
# This is REALLY ghetto, but its a start. We can improve and
# keep unit tests passing in the future.
exclude = exclude.gsub("**", "|||GLOBAL|||")
exclude = exclude.gsub("*", "|||PATH|||")
exclude = exclude.gsub("|||PATH|||", "[^/]*")
exclude = exclude.gsub("|||GLOBAL|||", ".*")
regexp += exclude
Regexp.new(regexp)
end
def self.rsync_single(machine, ssh_info, opts)
# Folder info
guestpath = opts[:guestpath]
hostpath = opts[:hostpath]
hostpath = File.expand_path(hostpath, machine.env.root_path)
hostpath = Vagrant::Util::Platform.fs_real_path(hostpath).to_s
if Vagrant::Util::Platform.windows?
# rsync for Windows expects cygwin style paths, always.
hostpath = Vagrant::Util::Platform.cygwin_path(hostpath)
end
# Make sure the host path ends with a "/" to avoid creating
# a nested directory...
if !hostpath.end_with?("/")
hostpath += "/"
end
# Folder options
opts[:owner] ||= ssh_info[:username]
opts[:group] ||= ssh_info[:username]
# Connection information
username = ssh_info[:username]
host = ssh_info[:host]
rsh = [
"ssh -p #{ssh_info[:port]} -o StrictHostKeyChecking=no",
ssh_info[:private_key_path].map { |p| "-i '#{p}'" },
].flatten.join(" ")
# Exclude some files by default, and any that might be configured
# by the user.
excludes = ['.vagrant/']
excludes += Array(opts[:exclude]).map(&:to_s) if opts[:exclude]
excludes.uniq!
# Get the command-line arguments
args = nil
args = Array(opts[:args]) if opts[:args]
args ||= ["--verbose", "--archive", "--delete", "-z"]
# Build up the actual command to execute
command = [
"rsync",
args,
"-e", rsh,
excludes.map { |e| ["--exclude", e] },
hostpath,
"#{username}@#{host}:#{guestpath}",
].flatten
# The working directory should be the root path
command_opts = {}
command_opts[:workdir] = machine.env.root_path.to_s
machine.ui.info(I18n.t(
"vagrant.rsync_folder", guestpath: guestpath, hostpath: hostpath))
if excludes.length > 1
machine.ui.info(I18n.t(
"vagrant.rsync_folder_excludes", excludes: excludes.inspect))
end
# If we have tasks to do before rsyncing, do those.
if machine.guest.capability?(:rsync_pre)
machine.guest.capability(:rsync_pre, opts)
end
r = Vagrant::Util::Subprocess.execute(*(command + [command_opts]))
if r.exit_code != 0
raise Vagrant::Errors::RSyncError,
command: command.join(" "),
guestpath: guestpath,
hostpath: hostpath,
stderr: r.stderr
end
# If we have tasks to do after rsyncing, do those.
if machine.guest.capability?(:rsync_post)
machine.guest.capability(:rsync_post, opts)
end
end
end
end
end