Don Don's Garage

Adding to the slowness of the internet a little bit each day.

  • Cars
  • 57 Wrecker
  • Bluebird
  • Programming
  • Bundler version 1.0.0.rc.5 Full Service Capistrano Task

    • 13 Aug 2010
    • 0 Responses
    •  views
    • bundler capistrano programming
    • Edit
    • Delete
    • Tags
    • Autopost

    I've recently been working on updating a set of Ruby applications and converting all of the gem dependencies to use Bundler (gembundler.com)

    I've added to my optimized Capistrano Bundler tasks to include handling the prequisite rubygems version and providing a number of customization parameters.

    Bundler 1.0 now includes a basic "bundle:install" Capistrano script http://github.com/carlhuda/bundler/blob/master/lib/bundler/capistrano.rb, but since mine covers a bit more ground than the default one I'm continuing to build my scripts around the following Bundler tasks.

    
    
    Capistrano::Configuration.instance(:must_exist).load do
    
    
      desc "Add deploy hooks to invoke bundler:install"
      task :acts_as_bundled do
        after "deploy:rollback:revision", "bundler:install"
        after "deploy:update_code", "bundler:install"
        after "deploy:setup", "bundler:setup"
      end
      
      namespace :bundler do
        
        set :bundler_ver, '1.0.0.rc.5'
        set :bundler_opts, %w(--deployment --no-color --quiet)
        set(:bundler_exec) { ruby_enterprise_path + "/bin/bundle" }
        set(:bundler_dir) { "#{shared_path}/bundle" }
        set :bundler_rubygems_ver, '1.3.7'
        set(:bundler_user) { apache_run_user }
        set :bundler_file, "Gemfile"
        
        desc "Update Rubygems to be compatible with bundler"
        task :update_rubygems, :except => { :no_release => true } do
          gem_ver = capture("gem --version").chomp
          if gem_ver < bundler_rubygems_ver
            logger.important "RubyGems needs to be udpated, has gem --version #{gem_ver}"
            gem2.update_system 
          end
        end
        
        desc "Setup system to use bundler"
        task :setup, :except => { :no_release => true } do
          bundler.update_rubygems
          gem2.install_only "bundler", bundler_ver
        end
      
        desc "bundle the release"
        task :install, :except => { :no_release => true } do
          bundler.setup
          
          #Don't bother if there's no gemfile.
          #optionally do it as a specific user to avoid permissions problems
          #do as much as possible in a single 'run' for speed.
          
          args = bundler_opts
          args << "--path #{bundler_dir}" unless bundler_dir.to_s.empty? || bundler_opts.include?("--system")
          args << "--gemfile=#{bundler_file}" unless bundler_file == "Gemfile"
          
          cmd = "cd #{latest_release}; if [ -f #{bundler_file} ]; then #{bundler_exec} install #{args.join(' ')}; fi"
          cmd = "sudo -u #{bundler_user} sh -c '#{cmd}'" if bundler_user and not bundler_user.empty?
          run cmd
    
          on_rollback do
            if previous_release
              cmd = "cd #{previous_release}; if [ -f #{bundler_file} ]; then #{bundler_exec} install #{args.join(' ')}; fi"
              cmd = "sudo -u #{bundler_user} sh -c '#{cmd}'" if bundler_user and not bundler_user.empty?
              run cmd
            else
              logger.important "no previous release to rollback to, rollback of bundler:install skipped"
            end
          end
            
        end
    
      end
      
    end
    
    
    

    This code uses some other dependencies that I use throughout my Capistrano scripts, namely the customized Gem2 plugin from vmbuilder_plugins. This following code necessary to monkey-patch the Gem2 plugin, which is bundled with Mike Bailey's deprec project http://github.com/mbailey/deprec/blob/master/lib/vmbuilder_plugins/gem.rb

    
    
    module Gem
    
    
      GEM_UNINSTALL= "gem uninstall --ignore-dependencies --executables"
    
    
      def install_only(package, version=nil)
        tries = 3
        begin
          cmd = "if ! gem list | grep --silent -e '#{package}.*#{version}'; then gem uninstall --ignore-dependencies --executables --all #{package}; #{GEM_INSTALL} #{if version then '-v '+version.to_s end} #{package}; fi"
          send(run_method,cmd)
        rescue Capistrano::Error
          tries -= 1
          retry if tries > 0
        end
      end
    
    
      # uninstalls the gems detailed in +package+, selecting version +version+ if
      # specified, otherwise all.
      #
      #  
      def uninstall(package, version=nil)
        cmd = "#{GEM_UNINSTALL} #{if version then '-v '+version.to_s  else '--all' end} #{package}"
        wrapped_cmd = "if gem list | grep --silent -e '#{package}.*#{version}'; then #{cmd}; fi"
        send(run_method,wrapped_cmd)
      end
    
    
    end
    
    
    

    Unlike many examples of Capistrano deploy scripts which are kept inside the app root of the application they deploy, my deploy scripts are kept in their own repository, and they were built to contain all of the "institutional knowledge" of how to interact with all of our server farms and applications.  There is a lot of shared code that would have to be duplicated if I were to try to break the deploy scripts apart, and store them with each application.  There would also be a lot of tasks that don't make sense to live in only one application, some wouldn't have a home in any application.  I've found a couple of patterns that make it easier to hook and unhook code into the main deploy processes, which I may touch on in other posts. 

    One of the techniques which is used in the bundler tasks is to separate out the callback hooks into their own top level task.  This allows me to not have to remember all of bundlers individual hooks, I can easily add them to any application that needs them, and omit them from applications that don't.  A second technique that I use is to control the task chains.  I learned early on that you want to be able to call individual tasks for maintenance and not inadvertently trigger a long series of chained tasks.

    This is an example of what you might see in one of my application deploy scripts:

    
    
      on :start, :only => ["deploy","deploy:setup","deploy:migrations","deploy:cold"] do
        acts_as_bundled
        before "deploy:update_code", "deploy:clear_release_path","deploy:setup_dirs"
        after "deploy:update_code", "deploy:authentication", "git:track", "scalr:on_boot_finish", "git:config", "scalr:on_hostup"
        after "deploy:symlink", "deploy:cleanup"
      end
    
    
    

    Notice the acts_as_bundled acts as a nice declarative way to invoke bundlers callback assignments, and that they are only invoked if one of the top level tasks matches the :only clause.

    So for example I can do a command like

    
    
    cap deploy:symlink
    
    
    

    Without causing "deploy:cleanup" to be executed.  But when I do:

    
    
    cap deploy
    
    
    

    It will.

    • Tweet
  • Optimized Capistrano Bundler 0.9 installation task

    • 11 Feb 2010
    • 0 Responses
    •  views
    • bundler capistrano programming
    • Edit
    • Delete
    • Tags
    • Autopost

    The recent upgrade for bundler to 0.9.3 requires removing any previous bundler gems, and if you use Capistrano or another deployment system this will bite you if you haven't already upgraded.

    This task was built for Ubuntu, but should work fine for any bash environment where Ruby and Rubygems are setup properly.

    namespace :bundler do
      set :bundler_ver, '0.9.3'
      desc "install bundler"
      task :install, :roles => :app do 
        run "if ! gem list | grep --silent -e 'bundler.*#{bundler_ver}'; then gem uninstall --ignore-dependencies --executables --all bundler; gem install -y --no-rdoc --no-ri -v #{bundler_ver} bundler; fi"
      end
    end
    
    
    


    Capistrano is a systems deployment tool. www.capify.org

    Bundler is the new Ruby dependency management gem that is integrated into Rails 3. http://github.com/carlhuda/bundler

    • Tweet
  • About

    Software Developer, Systems Operations, and car-guy.

    78854 Views
  • Archive

    • 2012 (2)
      • April (1)
      • February (1)
    • 2011 (13)
      • November (3)
      • October (1)
      • September (1)
      • July (2)
      • June (1)
      • March (4)
      • January (1)
    • 2010 (27)
      • October (5)
      • August (1)
      • June (1)
      • May (4)
      • April (2)
      • March (4)
      • February (6)
      • January (4)
    • 2009 (7)
      • December (7)

    Get Updates

    Subscribe via RSS
    TwitterFacebook
  • Sites I Like

    • Antique Wreckers on Flikr
    • Hyperbole and a Half
    • The Oatmeal
    • The Big Stump (engines and cars)

    My Other Sites

    • Donnoman’s Garage | Motortopia
    • The Donnoman Daily
    • About.me
    • Blekko
    • Flavors.me
    • Github
    • Sourceforge
    • Flickr
    • Quora
    • Card Cloud
    • Twylah
    • FormSpring
    • DonDon's HubGarage

    Cool Services

    • YouSendIt
    • Pingdom
    • TwitterFeed