lib/xcode/install.rb in xcode-install-1.1.0 vs lib/xcode/install.rb in xcode-install-1.2.0

- old
+ new

@@ -1,7 +1,8 @@ require 'fileutils' require 'pathname' +require 'rexml/document' require 'spaceship' require 'json' require 'rubygems/version' require 'xcode/install/command' require 'xcode/install/version' @@ -10,24 +11,25 @@ CACHE_DIR = Pathname.new("#{ENV['HOME']}/Library/Caches/XcodeInstall") class Curl COOKIES_PATH = Pathname.new('/tmp/curl-cookies.txt') def fetch(url, directory = nil, cookies = nil, output = nil, progress = true) - options = cookies.nil? ? '' : "-b '#{cookies}' -c #{COOKIES_PATH}" - # options += ' -vvv' + options = cookies.nil? ? [] : ['--cookie', cookies, '--cookie-jar', COOKIES_PATH] + # options << ' -vvv' uri = URI.parse(url) output ||= File.basename(uri.path) output = (Pathname.new(directory) + Pathname.new(output)) if directory - progress = progress ? '-#' : '-s' - command = "curl #{options} -L -C - #{progress} -o #{output} #{url}" - IO.popen(command).each do |fd| - puts(fd) - end - result = $CHILD_STATUS.to_i == 0 + progress = progress ? '--progress-bar' : '--silent' + command = ['curl', *options, '--location', '--continue-at', '-', progress, '--output', output, url].map(&:to_s) + io = IO.popen(command) + io.each { |line| puts line } + io.close + result = $?.exitstatus == 0 + FileUtils.rm_f(COOKIES_PATH) result end end @@ -67,31 +69,31 @@ installed.map { |x| InstalledXcode.new(x) }.sort do |a, b| Gem::Version.new(a.version) <=> Gem::Version.new(b.version) end end - def install_dmg(dmgPath, suffix = '', switch = true, clean = true) + def install_dmg(dmg_path, suffix = '', switch = true, clean = true) xcode_path = "/Applications/Xcode#{suffix}.app" - `hdiutil mount -nobrowse -noverify #{dmgPath}` + mount_dir = mount(dmg_path) puts 'Please authenticate for Xcode installation...' - source = Dir.glob('/Volumes/Xcode/Xcode*.app').first + source = Dir.glob(File.join(mount_dir, 'Xcode*.app')).first if source.nil? - out <<-HELP -No `Xcode.app` found in DMG. Please remove #{dmgPath} if you suspect a corrupted + out = <<-HELP +No `Xcode.app` found in DMG. Please remove #{dmg_path} if you suspect a corrupted download or run `xcversion update` to see if the version you tried to install has been pulled by Apple. If none of this is true, please open a new GH issue. HELP - $stderr.puts out.gsub("\n", ' ') + $stderr.puts out.tr("\n", ' ') return end `sudo ditto "#{source}" "#{xcode_path}"` `umount "/Volumes/Xcode"` - if not verify_integrity(xcode_path) + unless verify_integrity(xcode_path) `sudo rm -f #{xcode_path}` return end enable_developer_mode @@ -105,11 +107,11 @@ `sudo xcode-select --switch #{xcode_path}` puts `xcodebuild -version` end - FileUtils.rm_f(dmgPath) if clean + FileUtils.rm_f(dmg_path) if clean end def install_version(version, switch = true, clean = true, install = true, progress = true, url = nil) dmg_path = get_dmg(version, progress, url) fail Informative, "Failed to download Xcode #{version}." if dmg_path.nil? @@ -152,11 +154,11 @@ private def spaceship @spaceship ||= begin begin - Spaceship.login(ENV["XCODE_INSTALL_USER"], ENV["XCODE_INSTALL_PASSWORD"]) + Spaceship.login(ENV['XCODE_INSTALL_USER'], ENV['XCODE_INSTALL_PASSWORD']) rescue Spaceship::Client::InvalidUserCredentialsError $stderr.puts 'The specified Apple developer account credentials are incorrect.' exit(1) rescue Spaceship::Client::NoUserCredentialsError $stderr.puts <<-HELP @@ -164,12 +166,12 @@ XCODE_INSTALL_USER and XCODE_INSTALL_PASSWORD environment variables. HELP exit(1) end - if ENV.key?("XCODE_INSTALL_TEAM_ID") - Spaceship.client.team_id = ENV["XCODE_INSTALL_TEAM_ID"] + if ENV.key?('XCODE_INSTALL_TEAM_ID') + Spaceship.client.team_id = ENV['XCODE_INSTALL_TEAM_ID'] end Spaceship.client end end @@ -194,19 +196,19 @@ download(version, progress, url) end def fetch_seedlist - @xcodes = parse_seedlist(spaceship.send(:request, :get, '/services-account/QH65B2/downloadws/listDownloads.action', { - start: "0", - limit: "1000", - sort: "dateModified", - dir: "DESC", - searchTextField: "", - searchCategories: "", - search: "false", - }).body) + @xcodes = parse_seedlist(spaceship.send(:request, :get, + '/services-account/QH65B2/downloadws/listDownloads.action', + start: '0', + limit: '1000', + sort: 'dateModified', + dir: 'DESC', + searchTextField: '', + searchCategories: '', + search: 'false').body) names = @xcodes.map(&:name) @xcodes += prereleases.reject { |pre| names.include?(pre.name) } File.open(LIST_FILE, 'w') do |f| @@ -241,19 +243,19 @@ installed = installed_versions.map(&:version) seedlist.map(&:name).reject { |x| installed.include?(x) } end def prereleases - body=spaceship.send(:request, :get, '/xcode/download/').body - links=body.scan(/<a.+?href="(.+?.dmg)".*>(.*)<\/a>/) + body = spaceship.send(:request, :get, '/xcode/download/').body + links = body.scan(%r{<a.+?href="(.+?.dmg)".*>(.*)</a>}) links = links.map do |link| - parent = link[0].scan(/path=(\/.*\/.*\/)/).first.first + parent = link[0].scan(%r{path=(/.*/.*/)}).first.first match = body.scan(/#{Regexp.quote(parent)}(.+?.pdf)/).first if match - link += [parent + match.first] + link + [parent + match.first] else - link += [nil] + link + [nil] end end links.map { |pre| Xcode.new_prerelease(pre[1].strip.gsub(/.*Xcode /, ''), pre[0], pre[2]) } end @@ -264,10 +266,26 @@ def verify_integrity(path) puts `/usr/sbin/spctl --assess --verbose=4 --type execute #{path}` $?.exitstatus == 0 end + + def hdiutil(*args) + io = IO.popen(['hdiutil', *args]) + result = io.read + io.close + fail Informative, 'Failed to invoke hdiutil.' unless $?.exitstatus == 0 + result + end + + def mount(dmg_path) + plist = hdiutil('mount', '-plist', '-nobrowse', '-noverify', dmg_path.to_s) + document = REXML::Document.new(plist) + node = REXML::XPath.first(document, "//key[.='mount-point']/following-sibling::*[1]") + fail Informative, 'Failed to mount image.' unless node + node.text + end end class Simulator attr_reader :version attr_reader :name @@ -325,11 +343,11 @@ :private def prepare_package puts 'Mounting DMG' - mount_location = `hdiutil mount -nobrowse -noverify #{dmg_path}`.scan(/\/Volumes.*\n/).first.chomp + mount_location = mount(dmg_path) puts 'Expanding pkg' expanded_pkg_path = CACHE_DIR + identifier FileUtils.rm_rf(expanded_pkg_path) `pkgutil --expand #{mount_location}/*.pkg #{expanded_pkg_path}` puts "Expanded pkg into #{expanded_pkg_path}" @@ -380,11 +398,11 @@ def initialize(path) @path = Pathname.new(path) end def version - @version ||= get_version + @version ||= fetch_version end def bundle_version @bundle_version ||= Gem::Version.new(plist_entry(':DTXcode').to_i.to_s.split(//).join('.')) end @@ -424,11 +442,11 @@ def plist_entry(keypath) `/usr/libexec/PlistBuddy -c "Print :#{keypath}" "#{path}/Contents/Info.plist"`.chomp end - def get_version + def fetch_version output = `DEVELOPER_DIR='' "#{@path}/Contents/Developer/usr/bin/xcodebuild" -version` return '0.0' if output.nil? # ¯\_(ツ)_/¯ output.split("\n").first.split(' ')[1] end end @@ -465,10 +483,9 @@ url == other.url && version == other.version end def self.new_prerelease(version, url, release_notes_path) new('name' => version, - 'dateModified' => Time.now.to_i, 'files' => [{ 'remotePath' => url.split('=').last }], 'release_notes_path' => release_notes_path) end end end