diff --git a/lib/xcode/install.rb b/lib/xcode/install.rb index feb103f8..78f25bbf 100644 --- a/lib/xcode/install.rb +++ b/lib/xcode/install.rb @@ -25,13 +25,15 @@ class Curl # @param progress: parse and show the progress? # @param progress_block: A block that's called whenever we have an updated progress % # the parameter is a single number that's literally percent (e.g. 1, 50, 80 or 100) + # @param retry_download_count: A count to retry the downloading Xcode dmg/xip # rubocop:disable Metrics/AbcSize def fetch(url: nil, directory: nil, cookies: nil, output: nil, progress: nil, - progress_block: nil) + progress_block: nil, + retry_download_count: 3) options = cookies.nil? ? [] : ['--cookie', cookies, '--cookie-jar', COOKIES_PATH] uri = URI.parse(url) @@ -78,7 +80,7 @@ def fetch(url: nil, # "Partial file. Only a part of the file was transferred." # https://curl.haxx.se/mail/archive-2008-07/0098.html # https://github.com/KrauseFx/xcode-install/issues/210 - 3.times do + retry_download_count.times do # Non-blocking call of Open3 # We're not using the block based syntax, as the bacon testing # library doesn't seem to support writing tests for it @@ -135,7 +137,7 @@ def current_symlink File.symlink?(SYMLINK_PATH) ? SYMLINK_PATH : nil end - def download(version, progress, url = nil, progress_block = nil) + def download(version, progress, url = nil, progress_block = nil, retry_download_count = 3) xcode = find_xcode_version(version) if url.nil? return if url.nil? && xcode.nil? @@ -147,7 +149,8 @@ def download(version, progress, url = nil, progress_block = nil) cookies: url ? nil : spaceship.cookie, output: dmg_file, progress: progress, - progress_block: progress_block + progress_block: progress_block, + retry_download_count: retry_download_count ) result ? CACHE_DIR + dmg_file : nil end @@ -280,8 +283,8 @@ def install_dmg(dmg_path, suffix = '', switch = true, clean = true) end # rubocop:disable Metrics/ParameterLists - def install_version(version, switch = true, clean = true, install = true, progress = true, url = nil, show_release_notes = true, progress_block = nil) - dmg_path = get_dmg(version, progress, url, progress_block) + def install_version(version, switch = true, clean = true, install = true, progress = true, url = nil, show_release_notes = true, progress_block = nil, retry_download_count = 3) + dmg_path = get_dmg(version, progress, url, progress_block, retry_download_count) fail Informative, "Failed to download Xcode #{version}." if dmg_path.nil? if install @@ -370,7 +373,7 @@ def enable_developer_mode `sudo /usr/sbin/dseditgroup -o edit -t group -a staff _developer` end - def get_dmg(version, progress = true, url = nil, progress_block = nil) + def get_dmg(version, progress = true, url = nil, progress_block = nil, retry_download_count = 3) if url path = Pathname.new(url) return path if path.exist? @@ -381,7 +384,7 @@ def get_dmg(version, progress = true, url = nil, progress_block = nil) end end - download(version, progress, url, progress_block) + download(version, progress, url, progress_block, retry_download_count) end def fetch_seedlist @@ -512,12 +515,13 @@ def xcode end end - def download(progress, progress_block = nil) + def download(progress, progress_block = nil, retry_download_count = 3) result = Curl.new.fetch( url: source, directory: CACHE_DIR, progress: progress, - progress_block: progress_block + progress_block: progress_block, + retry_download_count: retry_download_count ) result ? dmg_path : nil end diff --git a/lib/xcode/install/install.rb b/lib/xcode/install/install.rb index 00b66fb1..c2926e4f 100644 --- a/lib/xcode/install/install.rb +++ b/lib/xcode/install/install.rb @@ -17,7 +17,8 @@ def self.options ['--no-install', 'Only download DMG, but do not install it.'], ['--no-progress', 'Don’t show download progress.'], ['--no-clean', 'Don’t delete DMG after installation.'], - ['--no-show-release-notes', 'Don’t open release notes in browser after installation.']].concat(super) + ['--no-show-release-notes', 'Don’t open release notes in browser after installation.'], + ['--retry-download-count', 'Count of retrying download when curl is failed.']].concat(super) end def initialize(argv) @@ -31,6 +32,7 @@ def initialize(argv) @should_switch = argv.flag?('switch', true) @progress = argv.flag?('progress', true) @show_release_notes = argv.flag?('show-release-notes', true) + @retry_download_count = argv.option('retry-download-count', '3') super end @@ -44,11 +46,12 @@ def validate! end fail Informative, "Version #{@version} doesn't exist." unless @url || @installer.exist?(@version) fail Informative, "Invalid URL: `#{@url}`" unless !@url || @url =~ /\A#{URI.regexp}\z/ + fail Informative, "Invalid Retry: `#{@retry_download_count} is not positive number.`" if (@retry_download_count =~ /\A[0-9]*\z/).nil? end def run @installer.install_version(@version, @should_switch, @should_clean, @should_install, - @progress, @url, @show_release_notes) + @progress, @url, @show_release_notes, nil, @retry_download_count.to_i) end end end diff --git a/spec/install_spec.rb b/spec/install_spec.rb index 17732357..4cdb9588 100644 --- a/spec/install_spec.rb +++ b/spec/install_spec.rb @@ -12,32 +12,32 @@ module XcodeInstall end it 'downloads and installs' do - Installer.any_instance.expects(:download).with('6.3', true, nil, nil).returns('/some/path') + Installer.any_instance.expects(:download).with('6.3', true, nil, nil, 3).returns('/some/path') Installer.any_instance.expects(:install_dmg).with('/some/path', '-6.3', true, true) Command::Install.run(['6.3']) end it 'downloads and installs with custom HTTP URL' do url = 'http://yolo.com/xcode.dmg' - Installer.any_instance.expects(:download).with('6.3', true, url, nil).returns('/some/path') + Installer.any_instance.expects(:download).with('6.3', true, url, nil, 3).returns('/some/path') Installer.any_instance.expects(:install_dmg).with('/some/path', '-6.3', true, true) Command::Install.run(['6.3', "--url=#{url}"]) end it 'downloads and installs and does not switch if --no-switch given' do - Installer.any_instance.expects(:download).with('6.3', true, nil, nil).returns('/some/path') + Installer.any_instance.expects(:download).with('6.3', true, nil, nil, 3).returns('/some/path') Installer.any_instance.expects(:install_dmg).with('/some/path', '-6.3', false, true) Command::Install.run(['6.3', '--no-switch']) end it 'downloads without progress if switch --no-progress is given' do - Installer.any_instance.expects(:download).with('6.3', false, nil, nil).returns('/some/path') + Installer.any_instance.expects(:download).with('6.3', false, nil, nil, 3).returns('/some/path') Installer.any_instance.expects(:install_dmg).with('/some/path', '-6.3', true, true) Command::Install.run(['6.3', '--no-progress']) end it 'reads .xcode-version' do - Installer.any_instance.expects(:download).with('6.3', true, nil, nil).returns('/some/path') + Installer.any_instance.expects(:download).with('6.3', true, nil, nil, 3).returns('/some/path') Installer.any_instance.expects(:install_dmg).with('/some/path', '-6.3', true, true) File.expects(:exist?).with('.xcode-version').returns(true) File.expects(:read).returns('6.3')