0

I'm doing a Capistrano deployment of a Rails app. It's been a lot of fun for the most part.

After the deployment is complete (in deploy:restart), I would like to start the Rails server, watch the output for a while, and then hit Ctrl-C to send an interrupt, thereby stopping the output, and proceeding the deploy:cleanup task. At one point it seemed like this was working, except that it appeared to be considering the interrupt to be an exception and so was saying it "Cannot Start the Rails Server" even though it was actually started and running. I wanted to rescue the interrupt, and so wrote the following, based in part on another thread here:

namespace :deploy do
    task :restart, :roles => :app, :except => { :no_release => true } do 
      begin
        logger.info 'Attempting to Start the Rails Server'
        run "cd #{release_path} && script/rails s"
      rescue SystemExit, Interrupt
        logger.info %q[Au revoir! And don't worry. The server will continue running just fine without you hanging around looking over it's shoulder.]
      rescue Exception => error
        logger.important 'Cannot Start the Rails Server. This may be a problem.'
        logger.info "#{error}"
      end
    end
  end

However, this doesn't work. Before I hit Ctrl-C, while the server is still running, as I would expect, I'm getting this sort of thing:

 ** [out :: server.example.com] Started GET "/assets/bootstrap.js?body=1" for 178.120.25.53 at 2012-07-09 19:10:53 +0000
 ** [out :: server.example.com] Served asset /bootstrap.js - 200 OK (11ms)

And then after I send the interrupt, I'm getting this:

 ** Au revoir! And don't worry. The server will continue running just fine without you hanging around looking over it's shoulder.
    triggering after callbacks for `deploy:restart'
  * executing `deploy:cleanup'
  * executing "ls -xt /srv/www/my_project/releases"
    servers: ["server.example.com"]
    [server.example.com] executing command
    command finished in 774ms
 ** keeping 1 of 2 deployed releases
  * executing "rm -rf /srv/www/my_project/releases/20120709190209"
    servers: ["server.example.com"]
    [server.example.com] executing command
    command finished in 811ms

Which looks right...but as it turns out, Rails is not, in fact, still running, as a grep of the processes before and after reveals.

Before Ctrl-C, I see both the Capistrano command (19358), and the Rails server it started (19507):

user@server.example.com:~$ ps ax | grep rails | grep -v grep
19358 pts/1    Ss+  0:01 bash -c cd /srv/www/my_project/releases/20120709190521 && script/rails s
19507 pts/1    Sl+  0:41 ruby script/rails s

After Ctrl-C, the Rails server is still there, or it appears to be:

user@server.example.com:~$ ps ax | grep rails | grep -v grep
19507 ?        Sl   0:41 ruby script/rails s

But after I attempt to hit the site in a web browser, it disappears! Weird eh?

user@server.example.com:~$ ps ax | grep rails | grep -v grep
user@server.example.com:~$ [no output; returned to prompt]

So, my question is: How do I do this thing? How do sever the communication between the running Rails process and Capistrano, allow Capistrano to move on to it's remaining tasks, and then give me back my terminal prompt, without stopping the Rails server? Any help would be appreciated.

Community
  • 1
  • 1
Day Davis Waterbury
  • 2,052
  • 18
  • 31

2 Answers2

0

I've now realized that this was a PEBCAC error. The Begin-Rescue-End block in my Capistrano script was not catching (rescuing) my inbound Ctrl-C. It was merely passing it along to the running Rails server process, which was obediently exiting with a SystemExit, which was passed back up the line to the Capistrano script, which then caught the outbound exception. At that point it was a done deal. No amount of catching and handling the outbound exceptions from within the context of the Capistrano script was ever going to prevent the Rails server from stopping. So, I understand now why it wasn't working. But I am still curious if there's a way to do what I was trying to do. It would mean catching my inbound interrupt in Capistrano somewhere and handling it before it could be passed on to the server.

Day Davis Waterbury
  • 2,052
  • 18
  • 31
0

You can use Signal.trap in Ruby to catch Ctrl-C. But I'm not sure how you can do what you need to with Capistrano – you want to spawn a grandchild process that won't be terminated when the Capistrano process is.

  • And this will catch the *inbound* interrupt signal *before* it reaches the process in question? If so, this may be just what I was looking for. I'll check it out. Thanks! – Day Davis Waterbury Jul 16 '12 at 18:07