How to Check to See If a File Exists (On the Remote Server) in Capistrano

How can you check to see if a file exists (on the remote server) in Capistrano?

@knocte is correct that capture is problematic because normally everyone targets deployments to more than one host (and capture only gets the output from the first one). In order to check across all hosts, you'll need to use invoke_command instead (which is what capture uses internally). Here is an example where I check to ensure a file exists across all matched servers:

def remote_file_exists?(path)
results = []

invoke_command("if [ -e '#{path}' ]; then echo -n 'true'; fi") do |ch, stream, out|
results << (out == 'true')
end

results.all?
end

Note that invoke_command uses run by default -- check out the options you can pass for more control.

Checking directory existence in Capistrano task fails

All Ruby methods that you use in your Capistrano tasks run on your local machine.

For example:

  • File.directory?
  • File.symlink?

These are always evaluated on your local filesystem. Capistrano never runs Ruby code on the remote server. These methods always return false for you because they are trying to find e.g. "#{release_path}/public" on your local computer, which of course does not exist.

To run code on the server, the tools available to you are Capistrano's test and execute methods. These take in a command string that is executed remotely via SSH.

If you want to test if a remote path is a directory, you cannot use Ruby; you have to use something that can be run in a remote shell. Here is one way to test if a path is a directory, for example:

is_directory = test("[ -d #{release_path}/public ]")

Likewise, to test if a path is a symlink:

is_symbolic_link = test("[ -h #{release_path}/public ]")

File.read() fails in my Capistrano task (file *does* exist)

Figured it out.

File.read() is being performed on my local machine, not on the remote one. Ugh.

What I needed to do was something like this:

within release_path do
execute "cat #{pid_file}"
end

My actual task is:

desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
pid_file = fetch(:pid_file)

within release_path do
execute "((ls #{pid_file} && ps -p `cat #{pid_file}`) && kill -9 `cat #{pid_file}`) || true"
execute "(ls #{pid_file} && /bin/rm #{pid_file}) || true"

# RESTART COMMAND GOES HERE
end
end
end

Capistrano: linked file database.yml does not exist on my.server.ipadress

Just create /home/deploy/myrailsapp/shared/config/database.yml file manually and adjust it.

Capistrano doesn't create (or manage) configuration file out of the box. So, you should do it manually or automate use own Capistrano scripts, Puppet, Chef, Ansible tools.

Is there a way to tell Capistrano to deploy a Local Repository to a Remote Server?

The setting that you are looking for is:

set :deploy_via, :copy

That creates a local .tar.gz file in your /tmp/ directory and pushes that to the server during deployment.

If you look at the source code, specifically lib/capistrano/recipes/deploy/strategy/copy.rb, you'll see a large block of comments beginning with the following.

  # This class implements the strategy for deployments which work
# by preparing the source code locally, compressing it, copying the
# file to each target host, and uncompressing it to the deployment
# directory.

This article was written for an older version, but it's still quite interesting and covers deployment options and optimizations.

Keep unversioned files when deploying with Capistrano

Personally, I think the best way to deal with those kind of things is to store them in the shared folder and create a task in capistrano to create symlinks to the shared assets.

Here's an example from one of my projects:

set :shared_assets, %w{public/images/products public/images/barcodes}

namespace :assets do
namespace :symlinks do
desc "Setup application symlinks for shared assets"
task :setup, :roles => [:app, :web] do
shared_assets.each { |link| run "mkdir -p #{shared_path}/#{link}" }
end

desc "Link assets for current deploy to the shared location"
task :update, :roles => [:app, :web] do
shared_assets.each { |link| run "ln -nfs #{shared_path}/#{link} #{release_path}/#{link}" }
end
end
end

before "deploy:setup" do
assets.symlinks.setup
end

before "deploy:symlink" do
assets.symlinks.update
end


Related Topics



Leave a reply



Submit