lib/xcode/install.rb in xcode-install-0.3.0 vs lib/xcode/install.rb in xcode-install-0.3.1
- old
+ new
@@ -1,283 +1,298 @@
-require "fastlane_core"
-require "fastlane_core/developer_center/developer_center"
-require "nokogiri"
-require "rubygems/version"
-require "xcode/install/command"
-require "xcode/install/version"
+require 'fastlane_core'
+require 'fastlane_core/developer_center/developer_center'
+require 'nokogiri'
+require 'rubygems/version'
+require 'xcode/install/command'
+require 'xcode/install/version'
+module CredentialsManager
+ class PasswordManager
+ alias_method :old_ask_for_login, :ask_for_login
+
+ def ask_for_login
+ puts "\nXcodeInstall needs your developer AppleID credentials to access the DevCenter."
+
+ old_ask_for_login
+ end
+ end
+end
+
module FastlaneCore
- class DeveloperCenter
- def cookies
- cookie_string = ""
+ class DeveloperCenter
+ def cookies
+ cookie_string = ''
- page.driver.cookies.each do |key, cookie|
- cookie_string << "#{cookie.name}=#{cookie.value};"
- end
+ page.driver.cookies.each do |_key, cookie|
+ cookie_string << "#{cookie.name}=#{cookie.value};"
+ end
- cookie_string
- end
+ cookie_string
+ end
- def download_seedlist
- JSON.parse(page.evaluate_script("$.ajax({data: { start: \"0\", limit: \"1000\", " +
- "sort: \"dateModified\", dir: \"DESC\", searchTextField: \"\", " +
- "searchCategories: \"\", search: \"false\" } , type: 'GET', " +
- "url: '/services-account/QH65B2/downloadws/listDownloads.action', " +
- "async: false})")['responseText'])
- end
- end
+ def download_seedlist
+ JSON.parse(page.evaluate_script("$.ajax({data: { start: \"0\", limit: \"1000\", " \
+ "sort: \"dateModified\", dir: \"DESC\", searchTextField: \"\", " \
+ "searchCategories: \"\", search: \"false\" } , type: 'GET', " \
+ "url: '/services-account/QH65B2/downloadws/listDownloads.action', " \
+ 'async: false})')['responseText'])
+ end
+ end
- module Helper
- def self.is_test?
- true
- end
- end
+ module Helper
+ def self.is_test?
+ true
+ end
+ end
end
module XcodeInstall
- class Curl
- COOKIES_PATH = Pathname.new('/tmp/curl-cookies.txt')
+ class Curl
+ COOKIES_PATH = Pathname.new('/tmp/curl-cookies.txt')
- def fetch(url, directory = nil, cookies = nil, output = nil)
- options = cookies.nil? ? '' : "-b '#{cookies}' -c #{COOKIES_PATH}"
- #options += ' -vvv'
+ def fetch(url, directory = nil, cookies = nil, output = nil)
+ options = cookies.nil? ? '' : "-b '#{cookies}' -c #{COOKIES_PATH}"
+ # options += ' -vvv'
- uri = URI.parse(url)
- output ||= File.basename(uri.path)
- output = (Pathname.new(directory) + Pathname.new(output)) if directory
+ uri = URI.parse(url)
+ output ||= File.basename(uri.path)
+ output = (Pathname.new(directory) + Pathname.new(output)) if directory
- command = "curl #{options} -L -C - -# -o #{output} #{url}"
- IO.popen(command).each do |fd|
- puts(fd)
- end
- result = $?.to_i == 0
+ command = "curl #{options} -L -C - -# -o #{output} #{url}"
+ IO.popen(command).each do |fd|
+ puts(fd)
+ end
+ result = $CHILD_STATUS.to_i == 0
- FileUtils.rm_f(COOKIES_PATH)
- result
- end
- end
+ FileUtils.rm_f(COOKIES_PATH)
+ result
+ end
+ end
- class Installer
- attr_reader :xcodes
+ class Installer
+ attr_reader :xcodes
- def initialize
- FileUtils.mkdir_p(CACHE_DIR)
- end
+ def initialize
+ FileUtils.mkdir_p(CACHE_DIR)
+ end
- def cache_dir
- CACHE_DIR
- end
+ def cache_dir
+ CACHE_DIR
+ end
- def current_symlink
- File.symlink?(SYMLINK_PATH) ? SYMLINK_PATH : nil
- end
+ def current_symlink
+ File.symlink?(SYMLINK_PATH) ? SYMLINK_PATH : nil
+ end
- def download(version)
- return unless exist?(version)
- xcode = seedlist.select { |x| x.name == version }.first
- dmg_file = Pathname.new(File.basename(xcode.path))
+ def download(version)
+ return unless exist?(version)
+ xcode = seedlist.find { |x| x.name == version }
+ dmg_file = Pathname.new(File.basename(xcode.path))
- result = Curl.new.fetch(xcode.url, CACHE_DIR, devcenter.cookies, dmg_file)
- result ? CACHE_DIR + dmg_file : nil
- end
+ result = Curl.new.fetch(xcode.url, CACHE_DIR, devcenter.cookies, dmg_file)
+ result ? CACHE_DIR + dmg_file : nil
+ end
- def exist?(version)
- list_versions.include?(version)
- end
+ def exist?(version)
+ list_versions.include?(version)
+ end
- def installed?(version)
- installed_versions.map { |x| x.version }.include?(version)
- end
+ def installed?(version)
+ installed_versions.map(&:version).include?(version)
+ end
- def installed_versions
- @installed ||= installed.map { |x| InstalledXcode.new(x) }.sort {
- |a,b| Gem::Version.new(a.version) <=> Gem::Version.new(b.version)
- }
- end
+ def installed_versions
+ @installed ||= 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)
- xcode_path = "/Applications/Xcode#{suffix}.app"
+ def install_dmg(dmgPath, suffix = '', switch = true, clean = true)
+ xcode_path = "/Applications/Xcode#{suffix}.app"
- `hdiutil mount -nobrowse -noverify #{dmgPath}`
- puts 'Please authenticate for Xcode installation...'
- source = Dir.glob('/Volumes/Xcode/Xcode*.app').first
+ `hdiutil mount -nobrowse -noverify #{dmgPath}`
+ puts 'Please authenticate for Xcode installation...'
+ source = Dir.glob('/Volumes/Xcode/Xcode*.app').first
- if source.nil?
- puts 'No `Xcode.app` found in DMG.'
- return
- end
+ if source.nil?
+ puts 'No `Xcode.app` found in DMG.'
+ return
+ end
- `sudo ditto "#{source}" "#{xcode_path}"`
- `umount "/Volumes/Xcode"`
+ `sudo ditto "#{source}" "#{xcode_path}"`
+ `umount "/Volumes/Xcode"`
- enable_developer_mode
- `sudo xcodebuild -license` unless xcode_license_approved?
+ enable_developer_mode
+ `sudo xcodebuild -license` unless xcode_license_approved?
- if switch
- `sudo rm -f #{SYMLINK_PATH}` unless current_symlink.nil?
- `sudo ln -sf #{xcode_path} #{SYMLINK_PATH}` unless SYMLINK_PATH.exist?
-
- `sudo xcode-select --switch #{xcode_path}`
- puts `xcodebuild -version`
- end
+ if switch
+ `sudo rm -f #{SYMLINK_PATH}` unless current_symlink.nil?
+ `sudo ln -sf #{xcode_path} #{SYMLINK_PATH}` unless SYMLINK_PATH.exist?
- FileUtils.rm_f(dmgPath) if clean
- end
+ `sudo xcode-select --switch #{xcode_path}`
+ puts `xcodebuild -version`
+ end
- def install_version(version, switch = true, clean = true)
- return if version.nil?
- dmg_path = get_dmg(version)
- raise Informative, "Failed to download Xcode #{version}." if dmg_path.nil?
+ FileUtils.rm_f(dmgPath) if clean
+ end
- install_dmg(dmg_path, "-#{version.split(' ')[0]}", switch, clean)
- end
+ def install_version(version, switch = true, clean = true)
+ return if version.nil?
+ dmg_path = get_dmg(version)
+ fail Informative, "Failed to download Xcode #{version}." if dmg_path.nil?
- def list_current
- majors = list_versions.map { |v| v.split('.')[0] }.select { |v| v.length == 1 }.uniq
- list_versions.select { |v| v.start_with?(majors.last) }.join("\n")
- end
+ install_dmg(dmg_path, "-#{version.split(' ')[0]}", switch, clean)
+ end
- def list
- list_versions.join("\n")
- end
+ def list_current
+ majors = list_versions.map { |v| v.split('.')[0] }.map { |v| v.split(' ')[0] }
+ majors = majors.select { |v| v.length == 1 }.uniq
+ list_versions.select { |v| v.start_with?(majors.last) }.join("\n")
+ end
- def rm_list_cache
- FileUtils.rm_f(LIST_FILE)
- end
+ def list
+ list_versions.join("\n")
+ end
- def symlink(version)
- xcode = installed_versions.select { |x| x.version == version }.first
- `sudo rm -f #{SYMLINK_PATH}` unless current_symlink.nil?
- `sudo ln -sf #{xcode.path} #{SYMLINK_PATH}` unless xcode.nil? || SYMLINK_PATH.exist?
- end
+ def rm_list_cache
+ FileUtils.rm_f(LIST_FILE)
+ end
- def symlinks_to
- File.absolute_path(File.readlink(current_symlink), SYMLINK_PATH.dirname) if current_symlink
- end
+ def symlink(version)
+ xcode = installed_versions.find { |x| x.version == version }
+ `sudo rm -f #{SYMLINK_PATH}` unless current_symlink.nil?
+ `sudo ln -sf #{xcode.path} #{SYMLINK_PATH}` unless xcode.nil? || SYMLINK_PATH.exist?
+ end
- private
+ def symlinks_to
+ File.absolute_path(File.readlink(current_symlink), SYMLINK_PATH.dirname) if current_symlink
+ end
- CACHE_DIR = Pathname.new("#{ENV['HOME']}/Library/Caches/XcodeInstall")
- LIST_FILE = CACHE_DIR + Pathname.new('xcodes.bin')
- MINIMUM_VERSION = Gem::Version.new('4.3')
- SYMLINK_PATH = Pathname.new('/Applications/Xcode.app')
+ private
- def devcenter
- @devcenter ||= FastlaneCore::DeveloperCenter.new
- end
+ CACHE_DIR = Pathname.new("#{ENV['HOME']}/Library/Caches/XcodeInstall")
+ LIST_FILE = CACHE_DIR + Pathname.new('xcodes.bin')
+ MINIMUM_VERSION = Gem::Version.new('4.3')
+ SYMLINK_PATH = Pathname.new('/Applications/Xcode.app')
- def enable_developer_mode
- `sudo /usr/sbin/DevToolsSecurity -enable`
- `sudo /usr/sbin/dseditgroup -o edit -t group -a staff _developer`
- end
+ def devcenter
+ @devcenter ||= FastlaneCore::DeveloperCenter.new
+ end
- def get_dmg(version)
- if ENV.key?("XCODE_INSTALL_CACHE_DIR")
- cache_path = Pathname.new(ENV["XCODE_INSTALL_CACHE_DIR"]) + Pathname.new("xcode-#{version}.dmg")
- if cache_path.exist?
- return cache_path
- end
- end
-
- return download(version)
- end
+ def enable_developer_mode
+ `sudo /usr/sbin/DevToolsSecurity -enable`
+ `sudo /usr/sbin/dseditgroup -o edit -t group -a staff _developer`
+ end
- def get_seedlist
- @xcodes = parse_seedlist(devcenter.download_seedlist)
-
- names = @xcodes.map(&:name)
- @xcodes += prereleases.reject { |pre| names.include?(pre.name) }
+ def get_dmg(version)
+ if ENV.key?('XCODE_INSTALL_CACHE_DIR')
+ cache_path = Pathname.new(ENV['XCODE_INSTALL_CACHE_DIR']) + Pathname.new("xcode-#{version}.dmg")
+ return cache_path if cache_path.exist?
+ end
- File.open(LIST_FILE,'w') do |f|
- f << Marshal.dump(xcodes)
- end
+ download(version)
+ end
- xcodes
- end
+ def fetch_seedlist
+ @xcodes = parse_seedlist(devcenter.download_seedlist)
- def installed
- `mdfind "kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode'" 2>/dev/null`.split("\n")
- end
+ names = @xcodes.map(&:name)
+ @xcodes += prereleases.reject { |pre| names.include?(pre.name) }
- def parse_seedlist(seedlist)
- seedlist['downloads'].select {
- |t| /^Xcode [0-9]/.match(t['name'])
- }.map { |x| Xcode.new(x) }.reject { |x| x.version < MINIMUM_VERSION }.sort {
- |a,b| a.dateModified <=> b.dateModified
- }.select { |x| x.url.end_with?('.dmg') }
- end
+ File.open(LIST_FILE, 'w') do |f|
+ f << Marshal.dump(xcodes)
+ end
- def list_versions
- installed = installed_versions.map { |x| x.version }
- seedlist.map { |x| x.name }.reject { |x| installed.include?(x) }
- end
+ xcodes
+ end
- def prereleases
- page = Nokogiri::HTML.parse(devcenter.download_file('/xcode/downloads/'))
- links = page.xpath('//a').select { |link| link['href'].end_with?('.dmg') }
+ def installed
+ `mdfind "kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode'" 2>/dev/null`.split("\n")
+ end
- links.map { |pre| Xcode.new_prelease(pre.text.strip.gsub(/.*Xcode /, ''), pre['href']) }
- end
+ def parse_seedlist(seedlist)
+ seeds = seedlist['downloads'].select do |t|
+ /^Xcode [0-9]/.match(t['name'])
+ end
- def seedlist
- @xcodes = Marshal.load(File.read(LIST_FILE)) if LIST_FILE.exist? && xcodes.nil?
- xcodes || get_seedlist
- end
+ xcodes = seeds.map { |x| Xcode.new(x) }.reject { |x| x.version < MINIMUM_VERSION }.sort do |a, b|
+ a.date_modified <=> b.date_modified
+ end
- def xcode_license_approved?
- !(`/usr/bin/xcrun clang 2>&1` =~ /license/ && !$?.success?)
- end
- end
+ xcodes.select { |x| x.url.end_with?('.dmg') }
+ end
- class InstalledXcode
- attr_reader :path
- attr_reader :version
+ def list_versions
+ installed = installed_versions.map(&:version)
+ seedlist.map(&:name).reject { |x| installed.include?(x) }
+ end
- def initialize(path)
- @path = Pathname.new(path)
- @version = get_version(path)
- end
+ def prereleases
+ page = Nokogiri::HTML.parse(devcenter.download_file('/xcode/downloads/'))
+ links = page.xpath('//a').select { |link| link['href'].end_with?('.dmg') }
- :private
+ links.map { |pre| Xcode.new_prelease(pre.text.strip.gsub(/.*Xcode /, ''), pre['href']) }
+ end
- def get_version(xcode_path)
- output = `DEVELOPER_DIR='' "#{xcode_path}/Contents/Developer/usr/bin/xcodebuild" -version`
- output.split("\n").first.split(' ')[1]
- end
- end
+ def seedlist
+ @xcodes = Marshal.load(File.read(LIST_FILE)) if LIST_FILE.exist? && xcodes.nil?
+ xcodes || fetch_seedlist
+ end
- class Xcode
- attr_reader :dateModified
- attr_reader :name
- attr_reader :path
- attr_reader :url
- attr_reader :version
+ def xcode_license_approved?
+ !(`/usr/bin/xcrun clang 2>&1` =~ /license/ && !$CHILD_STATUS.success?)
+ end
+ end
- def initialize(json)
- @dateModified = json['dateModified'].to_i
- @name = json['name'].gsub(/^Xcode /, '')
- @path = json['files'].first['remotePath']
- @url = "https://developer.apple.com/devcenter/download.action?path=#{@path}"
+ class InstalledXcode
+ attr_reader :path
+ attr_reader :version
- begin
- @version = Gem::Version.new(@name.split(' ')[0])
- rescue
- @version = Installer::MINIMUM_VERSION
- end
- end
+ def initialize(path)
+ @path = Pathname.new(path)
+ @version = get_version(path)
+ end
- def to_s
- "Xcode #{version} -- #{url}"
- end
+ :private
- def ==(o)
- dateModified == o.dateModified && name == o.name && path == o.path && \
- url == o.url && version == o.version
- end
+ def get_version(xcode_path)
+ output = `DEVELOPER_DIR='' "#{xcode_path}/Contents/Developer/usr/bin/xcodebuild" -version`
+ output.split("\n").first.split(' ')[1]
+ end
+ end
- def self.new_prelease(version, url)
- self.new({'name' => version,
- 'dateModified' => Time.now.to_i,
- 'files' => [{'remotePath' => url.split('=').last}]})
- end
- end
+ class Xcode
+ attr_reader :date_modified
+ attr_reader :name
+ attr_reader :path
+ attr_reader :url
+ attr_reader :version
+
+ def initialize(json)
+ @date_modified = json['dateModified'].to_i
+ @name = json['name'].gsub(/^Xcode /, '')
+ @path = json['files'].first['remotePath']
+ @url = "https://developer.apple.com/devcenter/download.action?path=#{@path}"
+
+ begin
+ @version = Gem::Version.new(@name.split(' ')[0])
+ rescue
+ @version = Installer::MINIMUM_VERSION
+ end
+ end
+
+ def to_s
+ "Xcode #{version} -- #{url}"
+ end
+
+ def ==(other)
+ date_modified == other.date_modified && name == other.name && path == other.path && \
+ url == other.url && version == other.version
+ end
+
+ def self.new_prelease(version, url)
+ new('name' => version,
+ 'dateModified' => Time.now.to_i,
+ 'files' => [{ 'remotePath' => url.split('=').last }])
+ end
+ end
end