Capistrano provides 5 super helpful methods you can use to transfer files and resources from your local machine to remote servers and vice versa. Those methods lives in a module called FileTransfer
, part of the Capistrano::Configuration::Actions
namespace.
The FileTransfer methods are:
These methods are not a merely a ssh-based implementation of the basic FTP commands. Because they lives in the Capistrano and Rails universe, they know everything about your environment including your deployment roles, servers and connection gateways.
And this is exactly the case when those methods turn out to be really useful. Suppose your production servers are reachable only through a gateway. Unless your FTP client supports a gateway-based connection, you would need to authenticate to the gateway server and then authenticate from the gateway to the end-point.
Because Capistrano file transfer methods know about your deployment settings, you don't need to bother with authentication or ssh connections. These methods will dutifully obey your deploy.rb
configurations and use them.
Let me show you some examples.
upload(from, to, options={}, &block)
The upload
action stores the file at the given path on all servers targeted by the current task.
If you ever used the deploy:upload
task before, then you might already know how this method works. It takes the path of the resource you want to upload and the target path on the remote servers.
desc "Uploads CHANGELOG.txt to all remote servers."
task :upload_changelog do
upload("#{RAILS_ROOT}/CHANGELOG.txt", "#{current_path}/public/CHANGELOG")
end
Beware that source and target path aren't automatically scoped to your Rails project. That said, if you forget to prepend the current_path
variable to the target path, Capistrano will try to upload your CHANGELOG.txt
file to the /public
folder on your server instead of the /public
folder under your Rails instance.
Unless otherwise specified, the upload method copies the source content to the target path on any remote server. If you want to alter the default behavior you need to pass a role
or a group of servers as option.
upload source, target, :hosts => "192.168.0.10"
upload source, target, :hosts => %w(192.168.0.10 192.168.0.11)
upload source, target, :only => { :primary => true }
put(data, path, options={})
The put
action stores the data at the given path on all servers targeted by the current task. This action is built on the top of upload
. Compared with upload
, this action requires data
to be a data string (ex. an IO) instead of a file system path.
download(from, to, options={}, &block)
As you can guess, the download
action is the opposite of upload
. It downloads the from
file from all configured servers and transfers them to your local machine.
As for upload
, you can selectively download the file from specific locations passing either a role
or a hosts
option. If you need to download the file once without selectively specify the source, check out the get
method below.
get(remote_path, path, options={}, &block)
The get
action downloads the file remote_path
from the FIRST configured server and transfers it to your local machine.
It is built on the top of the download
method. The main difference between the get
and the download
action described above is that the former downloads remote_path
from the FIRST server encountered in the server chain.
Let me show you a real world example. I have a multi-language Rails application where I installed the Translate plugin. For security reason, the translation interface is available in a staging environment only, restricted from public access. Just before a new release is shipped on production, I need to download the translations from staging, commit the changes to the repository and finally deploy the release.
Both production and staging servers are not directly accessible. They are part of a server cluster and the only way to ssh them is to ssh a gateway server, then open a ssh connection from the gateway to the target server. As you probably understand, downloading files from those servers to my local computer is a major hassle.
Fortunately, my deploy.rb
file already includes all the configuration I need to connect to the staging server from my local machine through the right gateway.
set :gateway_username, fetch(:gateway_username, default_gateway_username)
set :gateway, "#{gateway_username}@zeus"
set :host, fetch(:host, "apollo")
role :app, fetch(:host)
role :web, fetch(:host)
role :db, fetch(:host), :primary => true # set to false to not migrate
Great! All what I need is to create a task to download each language file from the remote server.
namespace :i18n do
desc <<-EOD
Downloads all translation files (except the English one) from the server
and stores them into the local config/locales folder.
EOD
task :download_translations do
Language.all.each do |language|
next if language.locale == :en
get "#{current_path}/config/locales/#{language.locale}.yml", "config/locales/#{language.locale}.yml"
end
end
end
Once more, Capistrano saved me hundreds of keystrokes!
transfer(direction, from, to, options={}, &block)
The transfer
action is the lowest-level method available. Under the hood, all methods above rely on transfer
.
transfer
requires a direction, the source path and the target path. Supported directions are :up
(for uploads) and :down
(for downloads).
The following example demonstrates how to upload a file to all remote servers using the transfer
action.
transfer :up, "#{RAILS_ROOT}/CHANGELOG.txt", "#{current_path}/public/CHANGELOG"
As you probably guess, this is equivalent to the following example used to describe the upload
action (for the sake of completeness, upload also provides the ability to chmod
the file once stored on the remote server).
upload("#{RAILS_ROOT}/CHANGELOG.txt", "#{current_path}/public/CHANGELOG")
Unless you need to implement a custom transfer mechanism, I suggest you to use one of the previous methods.