Class: VagrantPlugins::RsyncOnlyChanged::RsyncHelper

Inherits:
Object
  • Object
show all
Defined in:
lib/vagrant-rsync-only-changed/helper.rb

Overview

This is a helper that abstracts out the functionality of rsyncing folders so that it can be called from anywhere.

Class Method Summary collapse

Class Method Details

.adapt_filepath_for_rsync(root_path, filepath) ⇒ Object



46
47
48
49
50
51
# File 'lib/vagrant-rsync-only-changed/helper.rb', line 46

def self.adapt_filepath_for_rsync(root_path, filepath)
  path = File.dirname(filepath)
  filename = File.basename(filepath)
  
  return adapt_path_for_rsync(root_path, path) + "/" +  filename
end

.adapt_path_for_rsync(root_path, path) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
# File 'lib/vagrant-rsync-only-changed/helper.rb', line 34

def self.adapt_path_for_rsync(root_path, path)
  path = File.expand_path(path, root_path)
  path = Vagrant::Util::Platform.fs_real_path(path).to_s
  
  if Vagrant::Util::Platform.windows?
    # rsync for Windows expects cygwin style paths, always.

    path = Vagrant::Util::Platform.cygwin_path(path)
  end
  
  return path
end

.exclude_to_regexp(path, exclude) ⇒ Object

This converts an rsync exclude pattern to a regular expression we can send to Listen.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/vagrant-rsync-only-changed/helper.rb', line 11

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

.rsync_single(machine, ssh_info, opts, changed_folders = []) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/vagrant-rsync-only-changed/helper.rb', line 53

def self.rsync_single(machine, ssh_info, opts, changed_folders=[])
  # Folder info

  guestpath = opts[:guestpath]
  hostpath  = opts[:hostpath]
  original_path = opts[:hostpath]

  root_path = machine.env.root_path
  hostpath  = adapt_path_for_rsync(root_path, hostpath)

  # 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]
  proxy_command = ""
  if ssh_info[:proxy_command]
    proxy_command = "-o ProxyCommand='#{ssh_info[:proxy_command]}' "
  end

  rsh = [
    "ssh -p #{ssh_info[:port]} " +
    proxy_command +
    "-o StrictHostKeyChecking=no " +
    "-o IdentitiesOnly=true " +
    "-o UserKnownHostsFile=/dev/null",
    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]).dup if opts[:args]
  args ||= ["--verbose", "--archive", "--delete", "-z", "--copy-links"]

  # On Windows, we have to set a default chmod flag to avoid permission issues

  if Vagrant::Util::Platform.windows? && !args.any? { |arg| arg.start_with?("--chmod=") }
    # Ensures that all non-masked bits get enabled

    args << "--chmod=ugo=rwX"

    # Remove the -p option if --archive is enabled (--archive equals -rlptgoD)

    # otherwise new files will not have the destination-default permissions

    args << "--no-perms" if args.include?("--archive") || args.include?("-a")
  end

  # Disable rsync's owner/group preservation (implied by --archive) unless

  # specifically requested, since we adjust owner/group to match shared

  # folder setting ourselves.

  args << "--no-owner" unless args.include?("--owner") || args.include?("-o")
  args << "--no-group" unless args.include?("--group") || args.include?("-g")

  # Tell local rsync how to invoke remote rsync with sudo

  rsync_path = opts[:rsync_path]
  if !rsync_path && machine.guest.capability?(:rsync_command)
    rsync_path = machine.guest.capability(:rsync_command)
  end
  if rsync_path
    args << "--rsync-path"<< rsync_path
  end
  
  # Make sure the original host path ends with a "/" to create

  # only relative entries in the changed files list...

  if !original_path.end_with?("/")
    original_path += "/"
  end
  
  # Create a file with the list of changed files/directories

  if changed_folders.any?
    changed_list_file = File.join(Dir.tmpdir, "rsync.#{rand(1000)}")
    File.open(changed_list_file, "w+") do |f|
      changed_folders.each do |folder| 
        rsync_folder = folder.sub(original_path, '')
        
        #The changed file needs to be relative to the rsync root

        f.puts(rsync_folder) 
      end
    end
    
    args << "--delete-missing-args"
    args << "--files-from=" + adapt_filepath_for_rsync(root_path, changed_list_file)
  end

  # 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 opts.include?(:verbose)
    machine.ui.info(I18n.t("vagrant.rsync_showing_output"));
  end

  # If we have tasks to do before rsyncing, do those.

  if machine.guest.capability?(:rsync_pre)
    machine.guest.capability(:rsync_pre, opts)
  end

  if opts.include?(:verbose)
    command_opts[:notify] = [:stdout, :stderr]
    r = Vagrant::Util::Subprocess.execute(*(command + [command_opts])) {
      |io_name,data| data.each_line { |line|
        machine.ui.info("rsync[#{io_name}] -> #{line}") }
    }
  else
    r = Vagrant::Util::Subprocess.execute(*(command + [command_opts]))
  end

  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