xcode-install 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +29 -0
- data/lib/xcode/install/command.rb +1 -0
- data/lib/xcode/install/simulators.rb +58 -0
- data/lib/xcode/install/version.rb +1 -1
- data/lib/xcode/install.rb +133 -2
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6400b83c001ff5429ba066cb709c7c48a3ee52d3
|
4
|
+
data.tar.gz: d03b8a2ca220319f47e47883cff2b240bcdd6697
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f670c8560447440ae2b048c481a91addf64752bbc083c0f849a92bc46a403eb09f7d58c05eaaa2714a9d7b005287821d01340bf78a4cc91f89be8e0875687b1
|
7
|
+
data.tar.gz: 1c116c302f03f011a924f6bd764967703fcc00af5578554318be11bc96045e28ec210a6e310e8059d04c20473d26af21e763e3b1e57a28986077a92f76f14549
|
data/README.md
CHANGED
@@ -69,6 +69,35 @@ they have to be installed using the full name, e.g. `xcversion install '7 GM see
|
|
69
69
|
|
70
70
|
XcodeInstall can also install Xcode's Command Line Tools by calling `xcversion install-cli-tools`.
|
71
71
|
|
72
|
+
### Simulators
|
73
|
+
|
74
|
+
XcodeInstall can also manage your local simulators using the `simulators` command.
|
75
|
+
|
76
|
+
```bash
|
77
|
+
$ xcode-install simulators
|
78
|
+
Xcode 6.4.0 (/Applications/Xcode.app)
|
79
|
+
iOS 7.1 Simulator (installed)
|
80
|
+
iOS 8.1 Simulator (not installed)
|
81
|
+
iOS 8.2 Simulator (not installed)
|
82
|
+
iOS 8.3 Simulator (installed)
|
83
|
+
Xcode 7.0.0 (/Applications/Xcode-beta.app)
|
84
|
+
iOS 8.1 Simulator (not installed)
|
85
|
+
iOS 8.2 Simulator (not installed)
|
86
|
+
iOS 8.3 Simulator (installed)
|
87
|
+
iOS 8.4 Simulator (installed)
|
88
|
+
```
|
89
|
+
|
90
|
+
To install a simulator, simply:
|
91
|
+
|
92
|
+
```bash
|
93
|
+
$ xcode-install simulators --install=8.4
|
94
|
+
########################################################### 82.1%
|
95
|
+
######################################################################## 100.0%
|
96
|
+
Please authenticate to install iOS 8.4 Simulator...
|
97
|
+
|
98
|
+
Successfully installed iOS 8.4 Simulator
|
99
|
+
```
|
100
|
+
|
72
101
|
## Limitations
|
73
102
|
|
74
103
|
Unfortunately, the installation size of Xcodes downloaded will be bigger than when downloading via the Mac App Store, see [#10](/../../issues/10) and feel free to dupe the radar. 📡
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'claide'
|
2
|
+
|
3
|
+
module XcodeInstall
|
4
|
+
class Command
|
5
|
+
class Simulators < Command
|
6
|
+
self.command = 'simulators'
|
7
|
+
self.summary = 'List or install iOS simulators.'
|
8
|
+
|
9
|
+
def self.options
|
10
|
+
[['--install=version', 'Install simulator with the given version.']].concat(super)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(argv)
|
14
|
+
@installed_xcodes = Installer.new.installed_versions
|
15
|
+
@install = argv.option('install')
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
@install ? install : list
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
:private
|
25
|
+
|
26
|
+
def install
|
27
|
+
filtered_simulators = @installed_xcodes.map { |x| x.available_simulators }.flatten.select do |sim|
|
28
|
+
sim.version.to_s.start_with?(@install)
|
29
|
+
end
|
30
|
+
if filtered_simulators.count == 0
|
31
|
+
puts "[!] No simulator matching #{@install} was found. Please specify a version from the following available simulators:".ansi.red
|
32
|
+
list
|
33
|
+
exit 1
|
34
|
+
elsif filtered_simulators.count == 1
|
35
|
+
simulator = filtered_simulators.first
|
36
|
+
fail Informative, "#{simulator.name} is already installed." if simulator.installed?
|
37
|
+
puts "Installing #{simulator.name} for Xcode #{simulator.xcode.bundle_version}..."
|
38
|
+
simulator.install
|
39
|
+
else
|
40
|
+
puts "[!] More than one simulator matching #{@install} was found. Please specify the full version.".ansi.red
|
41
|
+
filtered_simulators.each do |simulator|
|
42
|
+
puts "Xcode #{simulator.xcode.bundle_version} (#{simulator.xcode.path})".ansi.green
|
43
|
+
puts "xcode-install simulator --install=#{simulator.version}"
|
44
|
+
end
|
45
|
+
exit 1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def list
|
50
|
+
@installed_xcodes.each do |xcode|
|
51
|
+
puts "Xcode #{xcode.version} (#{xcode.path})".ansi.green
|
52
|
+
xcode.available_simulators.each do |simulator|
|
53
|
+
puts simulator.to_s
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/xcode/install.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'pathname'
|
3
3
|
require 'spaceship'
|
4
|
+
require 'json'
|
4
5
|
require 'rubygems/version'
|
5
6
|
require 'xcode/install/command'
|
6
7
|
require 'xcode/install/version'
|
7
8
|
|
8
9
|
module XcodeInstall
|
10
|
+
CACHE_DIR = Pathname.new("#{ENV['HOME']}/Library/Caches/XcodeInstall")
|
9
11
|
class Curl
|
10
12
|
COOKIES_PATH = Pathname.new('/tmp/curl-cookies.txt')
|
11
13
|
|
@@ -171,7 +173,6 @@ HELP
|
|
171
173
|
end
|
172
174
|
end
|
173
175
|
|
174
|
-
CACHE_DIR = Pathname.new("#{ENV['HOME']}/Library/Caches/XcodeInstall")
|
175
176
|
LIST_FILE = CACHE_DIR + Pathname.new('xcodes.bin')
|
176
177
|
MINIMUM_VERSION = Gem::Version.new('4.3')
|
177
178
|
SYMLINK_PATH = Pathname.new('/Applications/Xcode.app')
|
@@ -267,13 +268,133 @@ HELP
|
|
267
268
|
end
|
268
269
|
end
|
269
270
|
|
271
|
+
class Simulator
|
272
|
+
attr_reader :version
|
273
|
+
attr_reader :name
|
274
|
+
attr_reader :identifier
|
275
|
+
attr_reader :source
|
276
|
+
attr_reader :xcode
|
277
|
+
|
278
|
+
def initialize(downloadable)
|
279
|
+
@version = Gem::Version.new(downloadable['version'])
|
280
|
+
@install_prefix = apply_variables(downloadable['userInfo']['InstallPrefix'])
|
281
|
+
@name = apply_variables(downloadable['name'])
|
282
|
+
@identifier = apply_variables(downloadable['identifier'])
|
283
|
+
@source = apply_variables(downloadable['source'])
|
284
|
+
end
|
285
|
+
|
286
|
+
def installed?
|
287
|
+
# FIXME: use downloadables' `InstalledIfAllReceiptsArePresentOrNewer` key
|
288
|
+
File.directory?(@install_prefix)
|
289
|
+
end
|
290
|
+
|
291
|
+
def installed_string
|
292
|
+
installed? ? 'installed' : 'not installed'
|
293
|
+
end
|
294
|
+
|
295
|
+
def to_s
|
296
|
+
"#{name} (#{installed_string})"
|
297
|
+
end
|
298
|
+
|
299
|
+
def xcode
|
300
|
+
Installer.new.installed_versions.find do |x|
|
301
|
+
x.available_simulators.find do |s|
|
302
|
+
s.version == version
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def download
|
308
|
+
result = Curl.new.fetch(source, CACHE_DIR)
|
309
|
+
result ? dmg_path : nil
|
310
|
+
end
|
311
|
+
|
312
|
+
def install
|
313
|
+
download unless dmg_path.exist?
|
314
|
+
prepare_package unless pkg_path.exist?
|
315
|
+
puts "Please authenticate to install #{name}..."
|
316
|
+
`sudo installer -pkg #{pkg_path} -target /`
|
317
|
+
fail Informative, "Could not install #{name}, please try again" unless installed?
|
318
|
+
source_receipts_dir = '/private/var/db/receipts'
|
319
|
+
target_receipts_dir = "#{@install_prefix}/System/Library/Receipts"
|
320
|
+
FileUtils.mkdir_p(target_receipts_dir)
|
321
|
+
FileUtils.cp("#{source_receipts_dir}/#{@identifier}.bom", target_receipts_dir)
|
322
|
+
FileUtils.cp("#{source_receipts_dir}/#{@identifier}.plist", target_receipts_dir)
|
323
|
+
puts "Successfully installed #{name}"
|
324
|
+
end
|
325
|
+
|
326
|
+
:private
|
327
|
+
|
328
|
+
def prepare_package
|
329
|
+
puts 'Mounting DMG'
|
330
|
+
mount_location = `hdiutil mount -nobrowse -noverify #{dmg_path}`.scan(/\/Volumes.*\n/).first.chomp
|
331
|
+
puts 'Expanding pkg'
|
332
|
+
expanded_pkg_path = CACHE_DIR + identifier
|
333
|
+
FileUtils.rm_rf(expanded_pkg_path)
|
334
|
+
`pkgutil --expand #{mount_location}/*.pkg #{expanded_pkg_path}`
|
335
|
+
puts "Expanded pkg into #{expanded_pkg_path}"
|
336
|
+
puts 'Unmounting DMG'
|
337
|
+
`umount #{mount_location}`
|
338
|
+
puts 'Setting package installation location'
|
339
|
+
package_info_path = expanded_pkg_path + 'PackageInfo'
|
340
|
+
package_info_contents = File.read(package_info_path)
|
341
|
+
File.open(package_info_path, 'w') do |f|
|
342
|
+
f << package_info_contents.sub('pkg-info', %(pkg-info install-location="#{@install_prefix}"))
|
343
|
+
end
|
344
|
+
puts 'Rebuilding package'
|
345
|
+
`pkgutil --flatten #{expanded_pkg_path} #{pkg_path}`
|
346
|
+
FileUtils.rm_rf(expanded_pkg_path)
|
347
|
+
end
|
348
|
+
|
349
|
+
def dmg_path
|
350
|
+
CACHE_DIR + Pathname.new(source).basename
|
351
|
+
end
|
352
|
+
|
353
|
+
def pkg_path
|
354
|
+
CACHE_DIR + "#{identifier}.pkg"
|
355
|
+
end
|
356
|
+
|
357
|
+
def apply_variables(template)
|
358
|
+
variable_map = {
|
359
|
+
'$(DOWNLOADABLE_VERSION_MAJOR)' => version.to_s.split('.')[0],
|
360
|
+
'$(DOWNLOADABLE_VERSION_MINOR)' => version.to_s.split('.')[1],
|
361
|
+
'$(DOWNLOADABLE_IDENTIFIER)' => identifier,
|
362
|
+
'$(DOWNLOADABLE_VERSION)' => version.to_s
|
363
|
+
}.freeze
|
364
|
+
variable_map.each do |key, value|
|
365
|
+
next unless template.include?(key)
|
366
|
+
template.sub!(key, value)
|
367
|
+
end
|
368
|
+
template
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
270
372
|
class InstalledXcode
|
271
373
|
attr_reader :path
|
272
374
|
attr_reader :version
|
375
|
+
attr_reader :bundle_version
|
376
|
+
attr_reader :uuid
|
377
|
+
attr_reader :downloadable_index_url
|
378
|
+
attr_reader :available_simulators
|
273
379
|
|
274
380
|
def initialize(path)
|
275
381
|
@path = Pathname.new(path)
|
276
|
-
|
382
|
+
end
|
383
|
+
|
384
|
+
def version
|
385
|
+
@version ||= get_version
|
386
|
+
end
|
387
|
+
|
388
|
+
def bundle_version
|
389
|
+
@bundle_version ||= Gem::Version.new(plist_entry(':DTXcode').to_i.to_s.split(//).join('.'))
|
390
|
+
end
|
391
|
+
|
392
|
+
def uuid
|
393
|
+
@uuid ||= plist_entry(':DVTPlugInCompatibilityUUID')
|
394
|
+
end
|
395
|
+
|
396
|
+
def downloadable_index_url
|
397
|
+
@downloadable_index_url ||= "https://devimages.apple.com.edgekey.net/downloads/xcode/simulators/index-#{bundle_version}-#{uuid}.dvtdownloadableindex"
|
277
398
|
end
|
278
399
|
|
279
400
|
def approve_license
|
@@ -285,6 +406,12 @@ HELP
|
|
285
406
|
`sudo /usr/libexec/PlistBuddy -c "add :IDEXcodeVersionForAgreedToGMLicense string #{@version}" #{license_plist_path}`
|
286
407
|
end
|
287
408
|
|
409
|
+
def available_simulators
|
410
|
+
@available_simulators ||= JSON.parse(`curl -Ls #{downloadable_index_url} | plutil -convert json -o - -`)['downloadables'].map do |downloadable|
|
411
|
+
Simulator.new(downloadable)
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
288
415
|
def install_components
|
289
416
|
`sudo installer -pkg #{@path}/Contents/Resources/Packages/MobileDevice.pkg -target /`
|
290
417
|
osx_build_version = `sw_vers -buildVersion`.chomp
|
@@ -295,6 +422,10 @@ HELP
|
|
295
422
|
|
296
423
|
:private
|
297
424
|
|
425
|
+
def plist_entry(keypath)
|
426
|
+
`/usr/libexec/PlistBuddy -c "Print :#{keypath}" "#{path}/Contents/Info.plist"`.chomp
|
427
|
+
end
|
428
|
+
|
298
429
|
def get_version
|
299
430
|
output = `DEVELOPER_DIR='' "#{@path}/Contents/Developer/usr/bin/xcodebuild" -version`
|
300
431
|
return '0.0' if output.nil? # ¯\_(ツ)_/¯
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xcode-install
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boris Bügling
|
@@ -102,6 +102,7 @@ files:
|
|
102
102
|
- lib/xcode/install/list.rb
|
103
103
|
- lib/xcode/install/select.rb
|
104
104
|
- lib/xcode/install/selected.rb
|
105
|
+
- lib/xcode/install/simulators.rb
|
105
106
|
- lib/xcode/install/uninstall.rb
|
106
107
|
- lib/xcode/install/update.rb
|
107
108
|
- lib/xcode/install/version.rb
|