lib/quandl/command/tasks/update.rb in quandl-0.3.7 vs lib/quandl/command/tasks/update.rb in quandl-0.4.0
- old
+ new
@@ -1,23 +1,21 @@
class Quandl::Command::Tasks::Update < Quandl::Command::Task
depends 'uri', 'net/http', 'quandl/utility/config', 'zlib', 'archive/tar/minitar'
-
- # 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 )
+ Updating from a.b.c ...
+ You are up to date! ( x.y.z )
- $ quandl update beta1
- Updating from 0.3.1 ...
- You are up to date! ( 0.3.1-beta1 )}
+ $ quandl update prerelease
+ Updating from x.y.z ...
+ You are up to date! ( x.y.z-prerelease )}
PACKAGE_URL = 'http://s3.amazonaws.com/quandl-command/'
disable_in_gem!
@@ -31,75 +29,67 @@
File.join(PACKAGE_URL, filename)
end
end
def execute
+ if args[0] == 'rollback'
+ execute_rollback
+ else
+ execute_update
+ end
+ rescue => err
+ # log error
+ error(err)
+ debug(err.backtrace.join("\n"))
+ # report failure and rollback
+ error("----\nAn error has occured! Rolling back to previous version ... ")
+ error("If the problem persists reinstall with: #{installer_url}")
+ rollback_update
+ end
+
+ def execute_update
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
+ install_executable
# success
version = %x{quandl -v}.to_s.strip.rstrip
- info "You are up to date! ( #{version} )"
+ cleanup_releases
- rescue => err
- # log error
- info(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
+ info "You are up to date! ( #{version} )"
end
+ def execute_rollback
+ info "Rolling back from #{current_timestamp} ... "
+ previous_timestamp = find_timestamp_before(current_timestamp)
+ if previous_timestamp.blank?
+ info("nothing to rollback to!")
+ return false
+ end
+ info "to: #{previous_timestamp}"
+ install_timestamp( previous_timestamp )
+ version = %x{quandl -v}.to_s.strip.rstrip
+ info "You have rolled back! ( #{version} )"
+ 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
+ private
- def tarball_path
- @tarball_path ||= File.join(update_path, "update.tar.gz")
- end
+ delegate :chmod, :cp, :ln_sf, :mv, :rm_rf, :rm, :mkdir_p, :cp_r, to: :qu
- 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 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
+ FileUtils.mkdir_p(releases_path) unless Dir.exists?(releases_path)
end
def download_tarball
debug "Downloading '#{package_url}' to '#{tarball_path}'"
# download new package
@@ -112,84 +102,148 @@
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 )
+ debug "Archive::Tar::Minitar.unpack( '#{tarball_path}', '#{releases_path}' )"
+ # extract into releases_path
+ Archive::Tar::Minitar.unpack( Zlib::GzipReader.open(tarball_path), releases_path )
+ # rename quandl-command to release name
+ mv( package_path, timestamp_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
+ def install_update
+ install_timestamp(new_timestamp)
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
+ def rollback_update
+ # remove the failed release
+ rm_rf(timestamp_path) if Dir.exists?(timestamp_path)
+ # install previous release
+ install_timestamp(current_timestamp)
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
+ def install_executable
+ new_executable = File.join(timestamp_path, 'pkg', 'quandl')
+ return unless File.exists?(new_executable)
+ rm executable_path if File.exists?(executable_path)
+ cp new_executable, executable_path
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
+ def install_timestamp(stamp)
+ # remove previous
+ rm_rf(current_path) if File.exists?(current_path)
+ # install timestamp
+ path = File.join( releases_path, stamp.to_s )
+ # copy or link
+ if Quandl::Utility::Config.windows?
+ cp_r(path, current_path)
+
+ else
+ ln_sf(path, current_path)
+
end
+ update_current_timestamp(stamp)
end
- def ensure_correct_permissions
- chmod("+x", File.join(root_path, 'bin/quandl'))
+ def update_current_timestamp(stamp)
+ File.open( timestamp_release_path, 'wb' ){|f| f.write(stamp.to_s) }
end
- def chmod(*args)
- debug("FileUtils.chmod #{args.to_a.join(' ')}")
- FileUtils.chmod(*args)
+ def delete_timestamp(stamp)
+ path = File.join(releases_path, stamp.to_s)
+ rm_rf(path) if Dir.exists?(path)
end
+
+ def cleanup_releases
+ return unless release_timestamps.count > 5
+ release_timestamps.reverse[5..-1].each{|stamp| delete_timestamp(stamp) }
+ end
+
+ def executable_path
+ @executable_path ||= File.join(root_path, 'bin', 'quandl')
+ end
+
+ def timestamp_release_path
+ @timestamp_release_path ||= File.join(root_path, '.timestamp')
+ end
+
+ def timestamp_path
+ @timestamp_path ||= File.join( releases_path, new_timestamp )
+ end
+
+ def tarball_path
+ @tarball_path ||= File.join(releases_path, "update.tar.gz")
+ end
+
+ def package_path
+ @package_path ||= File.join(releases_path, "quandl-command")
+ end
+
+ def releases_path
+ @releases_path ||= File.expand_path(File.join( root_path, 'releases' ))
+ end
+
+ def package_url
+ @package_url ||= self.class.package_url(args.first)
+ end
+
+ def current_path
+ @current_path ||= File.join(root_path, 'current')
+ end
+
+ def root_path
+ return @root_path if defined?(@root_path)
+ parent_root = File.expand_path(File.join(Quandl::Command::Tasks.root, '../'))
+ if Dir.exists?(File.join(parent_root, 'releases')) && Dir.exists?(File.join(parent_root, 'bin'))
+ @root_path = parent_root
+ else
+ @root_path = Quandl::Command::Tasks.root
+ end
+ @root_path
+ end
- def cp(old_path, new_path)
- debug("FileUtils.cp #{old_path} #{new_path}")
- FileUtils.cp( old_path, new_path )
+ def new_timestamp
+ @new_timestamp ||= (Time.now.getutc.to_f * 10000).to_i.to_s.gsub('.','')
end
- def mv(old_path, new_path)
- debug("FileUtils.mv #{old_path} #{new_path}")
- FileUtils.mv( old_path, new_path )
+ def find_timestamp_before(stamp)
+ release_timestamps.sort.each do |rstamp|
+ return rstamp if rstamp.to_i < stamp.to_i
+ end
+ nil
end
- def rm_rf(*args)
- debug( "FileUtils.rm_rf #{args.to_a.join(" ")}")
- FileUtils.rm_rf(*args)
+ def current_timestamp
+ # largest to smallest
+ @current_timestamp ||= File.read(timestamp_release_path).strip.rstrip.to_i if File.exists?(timestamp_release_path)
+ @current_timestamp ||= release_timestamps.sort.reverse.detect{|t| t < new_timestamp.to_i }
end
- def mkdir_p(*args)
- debug( "FileUtils.mkdir_p #{args.to_a.join(" ")}")
- FileUtils.mkdir_p(*args)
+ def release_timestamps
+ @release_timestamps ||= Dir["#{releases_path}/*"].select{|f| File.directory?(f) }.collect{|f| File.basename(f).to_i }
+ end
+
+ def qu
+ @qu ||= QuandlUtils.new(self)
+ end
+
+ class QuandlUtils
+ attr_accessor :task
+
+ def initialize(task=nil)
+ self.task = task
+ end
+
+ def method_missing(method_name, *args, &block)
+ fu(method_name, *args, &block)
+ end
+
+ private
+
+ def fu(method_name, *args, &block)
+ task.debug("#{method_name} #{args}") unless task.nil?
+ FileUtils.send(method_name, *args, &block)
+ end
end
end
\ No newline at end of file