33

I'm trying to write a task for Capistrano 3 that involves executing 'composer install' within the directory of the current release. It looks something like this:

namespace :composer do
  desc 'Install dependencies with Composer'
  task :install do
    on roles(:web) do
      within release_path do
        execute "#{fetch(:composer_command)} install"
      end
    end
  end
end

composer_command is set in the staging and production files - in my particular case to php /home/user/composer.phar

For some reason this command does not actually run in the current release directory, but instead runs in the parent directory (containing current, shared, releases, etc)

I delved into this a bit further and found that when I ran a single word command, like:

within release_path do
    execute "pwd"
end

It works just fine, and runs the command in the current release directory. But... when I run a command with spaces, like:

within release_path do
    execute "pwd && ls"
end

It runs in the parent directory, and not the directory set by the within block.

Can someone shed some light on this? Thanks!

Tombart
  • 30,520
  • 16
  • 123
  • 136
Rahul Sekhar
  • 2,761
  • 5
  • 23
  • 27

5 Answers5

34

Smells like a Cap 3 bug.

I suggest just guaranteeing you are where you want to be from the shell perspective:

execute "cd '#{release_path}'; #{fetch(:composer_command)} install"
Electrawn
  • 2,254
  • 18
  • 24
  • 4
    I raised an issue - https://github.com/capistrano/capistrano/issues/719 Turns out that it's a constraint with SSHKit. So I'll go with your solution - the old capistrano 2 way, as it were. – Rahul Sekhar Oct 19 '13 at 07:18
  • 1
    It's explained [in the docs](http://capistranorb.com/documentation/getting-started/tasks/). See [this answer](http://stackoverflow.com/a/32499307/52499). – x-yuri Sep 06 '16 at 18:19
11

You can retain all the niceties of within(), with(), default_env, etc, while still keeping the natural string syntax:

within release_path do
  execute *%w[ pip install -r requirements.txt ]
end
bricker
  • 8,911
  • 2
  • 44
  • 54
7

A couple of tips:

1) Capistrano uses SSHKit for a lot of things, among which command execution. In order to simplify using Composer you could configure the command map (in deploy.rb or production.rb, etc), here are 2 examples:

SSHKit.config.command_map[:composer] = "#{shared_path.join('composer.phar')}"
SSHKit.config.command_map[:composer] = '/usr/bin/env composer.phar'

Next you can execute it like so:

execute :composer, :install

2) From a security perspective it's wise to disable the php setting allow_url_fopen, but unfortunately Composer needs it enabled to function. You can use this trick to leave it disabled globally:

SSHKit.config.command_map[:composer] = "/usr/bin/env php -d allow_url_fopen=On #{shared_path.join('composer.phar')}"

Check out iniscan for more security advise on php settings.

3) Composer has an option -d, --working-dir, which you can point to the directory containing the composer.json file in order to run Composer from any other directory. This should solve your problem:

execute :composer, '-d', release_path, :install

4) You may want to take a look at the capistrano-composer project :)

Jasper N. Brouwer
  • 21,517
  • 4
  • 52
  • 76
  • Don't know if it's due to updates to Capistrano, or my specific build case, but what I had to do was slightly modify your second example to: `SSHKit.config.command_map[:composer] = "/[direct full path to]/composer.phar"` inside my deploy.rb. After that, adding `execute :composer, "install" within my .rake file worked fine. – karolus Sep 10 '17 at 14:20
5

Actually, your use of the within function is almost correct. You have supplied it an entire string as a command, but the doc points out that this results in unreliable behaviour (which I have experienced myself).

Let the first argument to execute be a symbol instead of a string (which contains whitespace):

within release_path do
    execute fetch(:composer_command).to_sym, "install"
    execute :pwd
    execute :ls
end
JellicleCat
  • 28,480
  • 24
  • 109
  • 162
4

just for reference here is the Capistrano Doc explaining why within {} does not work with arguments with whitespace. I hope this helps.

cdog
  • 513
  • 2
  • 8
  • 15