class Quandl::Command::Tasks::Update < Quandl::Command::Task # curl -s https://s3.amazonaws.com/quandl-command/install.sh | bash description "Update Quandl Toolbelt to the latest version." syntax %Q{quandl update [revision] EXAMPLES: $ quandl update Updating from 0.2.18 ... You are up to date! ( 0.3.1 ) $ quandl update beta1 Updating from 0.3.1 ... You are up to date! ( 0.3.1-beta1 )} PACKAGE_URL = 'http://s3.amazonaws.com/quandl-command/' disable_in_gem! class << self def package_url(revision=nil) # are we on windows? platform = Quandl::Utility::Config.windows? ? 'windows' : nil # build filename filename = ['quandl-command', platform, revision, 'tar.gz'].compact.join(".") # append s3 url File.join(PACKAGE_URL, filename) end end def execute # load libraries needed by this action require_dependencies info "Updating from #{Quandl::Command::VERSION} ... " # wipe update/ and backup/ folders prepare_for_update download_tarball # ensure package was downloaded return error("'#{package_url}' not found") unless File.exists?(tarball_path) # install extract_tarball copy_windows_specific_files if Quandl::Utility::Config.windows? install_update configure_update ensure_correct_permissions # success version = %x{quandl -v}.to_s.strip.rstrip info "You are up to date! ( #{version} )" rescue => err # log error error(err) debug(err.backtrace.join("\n")) # report failure and rollback info("----\nAn error has occured! Rolling back to previous version ... ") info("If the problem persists reinstall with: #{installer_url}") rollback_update end def installer_url return File.join(PACKAGE_URL, "Quandl Setup.exe") if Quandl::Utility::Config.windows? return File.join(PACKAGE_URL, "quandl-toolbelt.pkg") if Quandl::Utility::Config.macosx? end def package_path @package_path ||= File.join(update_path, "quandl-command") end def tarball_path @tarball_path ||= File.join(update_path, "update.tar.gz") end def package_url @package_url ||= self.class.package_url(args.first) end def update_path @update_path ||= File.join( root_path, 'update' ) end def backup_path @backup_path ||= File.join( root_path, 'backup' ) end def root_path Quandl::Command::Tasks.root end private def require_dependencies require 'uri' require 'net/http' require 'quandl/utility/config' require 'zlib' require 'archive/tar/minitar' end def prepare_for_update [backup_path, update_path].each do |path| # remove previous directory if present rm_rf(path) if Dir.exists?(path) # create new directory mkdir_p(path) unless Dir.exists?(path) end end def download_tarball debug "Downloading '#{package_url}' to '#{tarball_path}'" # download new package uri = URI( package_url ) # open connection to storage host Net::HTTP.start( uri.host, uri.port ) do |http| # download file resp = http.get( uri.path ) # write tar file if it was found open( tarball_path, "wb"){|f| f.write(resp.body) } unless resp.code == '404' end end def extract_tarball debug "Archive::Tar::Minitar.unpack( '#{tarball_path}', '#{update_path}' )" # extract into update_path Archive::Tar::Minitar.unpack( Zlib::GzipReader.open(tarball_path), update_path ) end def copy_windows_specific_files files = ['bin/quandl.bat'] files.each do |file| source_path = File.join(root_path, file) cp source_path, File.join(package_path, file) if File.exists?(source_path) end end def install_update debug "Installing '#{update_path}' to '#{root_path}'" # install each folder into the live directory Dir.glob(File.join(package_path, "/*")) do |update_dir| # current folder folder_name = File.basename(update_dir) # get live dir live_dir = File.join(root_path, folder_name ) # move live to backup (skip this step if it doesn't exist since it might be a new folder) mv live_dir, File.join(backup_path, folder_name) if File.exists?(live_dir) # move update to live mv update_dir, live_dir end end def configure_update if Dir.exists?("#{root_path}/ruby") bin_file = File.read("#{root_path}/bin/quandl") bin_file.gsub!("#!/usr/bin/env ruby", "#!/#{root_path}/ruby/bin/ruby") File.write("#{root_path}/bin/quandl", bin_file) end end def rollback_update Dir.glob(File.join(backup_path, "/*")) do |backup_dir| # current folder folder_name = File.basename(backup_dir) # get live dir live_dir = File.join(root_path, folder_name ) # move live to update mv live_dir, File.join(package_path, folder_name) # move backup to live mv backup_dir, live_dir end end def ensure_correct_permissions chmod("+x", File.join(root_path, 'bin/quandl')) end def chmod(*args) debug("FileUtils.chmod #{args.to_a.join(' ')}") FileUtils.chmod(*args) end def cp(old_path, new_path) debug("FileUtils.cp #{old_path} #{new_path}") FileUtils.cp( old_path, new_path ) end def mv(old_path, new_path) debug("FileUtils.mv #{old_path} #{new_path}") FileUtils.mv( old_path, new_path ) end def rm_rf(*args) debug( "FileUtils.rm_rf #{args.to_a.join(" ")}") FileUtils.rm_rf(*args) end def mkdir_p(*args) debug( "FileUtils.mkdir_p #{args.to_a.join(" ")}") FileUtils.mkdir_p(*args) end end