xcode-install 2.3.1 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +49 -7
- data/bin//360/237/216/211 +1 -1
- data/circle.yml +7 -0
- data/lib/xcode/install.rb +194 -44
- data/lib/xcode/install/select.rb +6 -0
- data/lib/xcode/install/version.rb +1 -1
- data/spec/curl_spec.rb +1 -1
- data/spec/install_spec.rb +4 -4
- data/spec/installer_spec.rb +100 -0
- data/spec/prerelease_spec.rb +1 -1
- data/xcode-install.gemspec +3 -0
- metadata +5 -4
- data/.travis.yml +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd49f95272394358487c0eed4f39884120ea0717
|
4
|
+
data.tar.gz: f6dc4b73867986caaa06811a2c91e90f6741ec21
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97e5d324c43f25f40f1b1682eac49544f6d695f44f2e27a942eb9782b635fb770bc271ef5ec81c93f4ace20523fa4cb2ad6dfb7dee03a446923d04feccae663e
|
7
|
+
data.tar.gz: 3ee8b79219737c56cb9d9a8050be6bc88aa15d74b94329907ec742e11bf95341268517ce8aff4218a14192638b465441896bfbd77412fc28bae3615800d2dd9b
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Xcode::Install
|
2
2
|
|
3
|
-
[![Gem Version](http://img.shields.io/gem/v/xcode-install.svg?style=flat)](http://badge.fury.io/rb/xcode-install)
|
3
|
+
[![Gem Version](http://img.shields.io/gem/v/xcode-install.svg?style=flat)](http://badge.fury.io/rb/xcode-install) [![CircleCI](https://circleci.com/gh/KrauseFx/xcode-install.svg?style=svg)](https://circleci.com/gh/KrauseFx/xcode-install)
|
4
4
|
|
5
5
|
Install and update your Xcodes automatically.
|
6
6
|
|
@@ -9,6 +9,8 @@ $ gem install xcode-install
|
|
9
9
|
$ xcversion install 6.3
|
10
10
|
```
|
11
11
|
|
12
|
+
This tool uses the [Downloads for Apple Developer](https://developer.apple.com/download/more/) page.
|
13
|
+
|
12
14
|
## Installation
|
13
15
|
|
14
16
|
```
|
@@ -34,6 +36,8 @@ XCODE_INSTALL_USER
|
|
34
36
|
XCODE_INSTALL_PASSWORD
|
35
37
|
```
|
36
38
|
|
39
|
+
### List
|
40
|
+
|
37
41
|
To list available versions:
|
38
42
|
|
39
43
|
```
|
@@ -41,17 +45,21 @@ $ xcversion list
|
|
41
45
|
6.0.1
|
42
46
|
6.1
|
43
47
|
6.1.1
|
44
|
-
6.2
|
48
|
+
6.2 (installed)
|
45
49
|
6.3
|
46
50
|
```
|
47
51
|
|
48
|
-
|
52
|
+
Already installed versions are marked with `(installed)`.
|
53
|
+
(Use `$ xcversion installed` to only list installed Xcodes with their path).
|
54
|
+
|
55
|
+
To update the list of available versions, run:
|
49
56
|
|
50
|
-
To list all available versions run
|
51
57
|
```
|
52
|
-
$ xcversion
|
58
|
+
$ xcversion update
|
53
59
|
```
|
54
60
|
|
61
|
+
### Install
|
62
|
+
|
55
63
|
To install a certain version, simply:
|
56
64
|
|
57
65
|
```
|
@@ -64,7 +72,10 @@ Xcode 8
|
|
64
72
|
Build version 6D570
|
65
73
|
```
|
66
74
|
|
67
|
-
This will download and install that version of Xcode.
|
75
|
+
This will download and install that version of Xcode. Then you can start it from `/Applications` as usual.
|
76
|
+
The new version will also be automatically selected for CLI commands (see below).
|
77
|
+
|
78
|
+
#### GMs and beta versions
|
68
79
|
|
69
80
|
Note: GMs and beta versions usually have special names, e.g.
|
70
81
|
|
@@ -74,7 +85,24 @@ $ xcversion list
|
|
74
85
|
7.1 beta
|
75
86
|
```
|
76
87
|
|
77
|
-
|
88
|
+
They have to be installed using the full name, e.g. `xcversion install '7 GM seed'`.
|
89
|
+
|
90
|
+
### Select
|
91
|
+
|
92
|
+
To see the currently selected version, run
|
93
|
+
```
|
94
|
+
$ xcversion selected
|
95
|
+
```
|
96
|
+
|
97
|
+
To select a version as active, run
|
98
|
+
```
|
99
|
+
$ xcversion select 8
|
100
|
+
```
|
101
|
+
|
102
|
+
To select a version as active and change the symlink at `/Applications/Xcode`, run
|
103
|
+
```
|
104
|
+
$ xcversion select 8 --symlink
|
105
|
+
```
|
78
106
|
|
79
107
|
### Command Line Tools
|
80
108
|
|
@@ -127,6 +155,8 @@ versions on unindexed volumes.
|
|
127
155
|
|
128
156
|
## Thanks
|
129
157
|
|
158
|
+
Thanks to [@neonichu](https://github.com/neonichu), the original (and best) author.
|
159
|
+
|
130
160
|
[This][3] downloading script which has been used for some inspiration, also [this][4]
|
131
161
|
for doing the installation. Additionally, many thanks to everyone who has contributed to this
|
132
162
|
project, especially [@henrikhodne][6] and [@lacostej][7] for making XcodeInstall C extension free.
|
@@ -139,6 +169,18 @@ project, especially [@henrikhodne][6] and [@lacostej][7] for making XcodeInstall
|
|
139
169
|
4. Push to the branch (`git push origin my-new-feature`)
|
140
170
|
5. Create a new Pull Request
|
141
171
|
|
172
|
+
### Running tests
|
173
|
+
|
174
|
+
```
|
175
|
+
bundle exec rake spec
|
176
|
+
```
|
177
|
+
|
178
|
+
### Running code style linter
|
179
|
+
|
180
|
+
```
|
181
|
+
bundle exec rubocop -a
|
182
|
+
```
|
183
|
+
|
142
184
|
## License
|
143
185
|
|
144
186
|
This project is licensed under the terms of the MIT license. See the [LICENSE](LICENSE) file.
|
data/bin//360/237/216/211
CHANGED
data/circle.yml
ADDED
data/lib/xcode/install.rb
CHANGED
@@ -6,42 +6,116 @@ require 'json'
|
|
6
6
|
require 'rubygems/version'
|
7
7
|
require 'xcode/install/command'
|
8
8
|
require 'xcode/install/version'
|
9
|
+
require 'shellwords'
|
10
|
+
require 'open3'
|
11
|
+
require 'fileutils'
|
9
12
|
|
10
13
|
module XcodeInstall
|
11
14
|
CACHE_DIR = Pathname.new("#{ENV['HOME']}/Library/Caches/XcodeInstall")
|
12
15
|
class Curl
|
13
16
|
COOKIES_PATH = Pathname.new('/tmp/curl-cookies.txt')
|
14
17
|
|
15
|
-
|
18
|
+
# @param url: The URL to download
|
19
|
+
# @param directory: The directory to download this file into
|
20
|
+
# @param cookies: Any cookies we should use for the download (used for auth with Apple)
|
21
|
+
# @param output: A PathName for where we want to store the file
|
22
|
+
# @param progress: parse and show the progress?
|
23
|
+
# @param progress_block: A block that's called whenever we have an updated progress %
|
24
|
+
# the parameter is a single number that's literally percent (e.g. 1, 50, 80 or 100)
|
25
|
+
# rubocop:disable Metrics/AbcSize
|
26
|
+
def fetch(url: nil,
|
27
|
+
directory: nil,
|
28
|
+
cookies: nil,
|
29
|
+
output: nil,
|
30
|
+
progress: nil,
|
31
|
+
progress_block: nil)
|
16
32
|
options = cookies.nil? ? [] : ['--cookie', cookies, '--cookie-jar', COOKIES_PATH]
|
17
|
-
# options << ' -vvv'
|
18
33
|
|
19
34
|
uri = URI.parse(url)
|
20
35
|
output ||= File.basename(uri.path)
|
21
36
|
output = (Pathname.new(directory) + Pathname.new(output)) if directory
|
22
37
|
|
38
|
+
# Piping over all of stderr over to a temporary file
|
39
|
+
# the file content looks like this:
|
40
|
+
# 0 4766M 0 6835k 0 0 573k 0 2:21:58 0:00:11 2:21:47 902k
|
41
|
+
# This way we can parse the current %
|
42
|
+
# The header is
|
43
|
+
# % Total % Received % Xferd Average Speed Time Time Time Current
|
44
|
+
#
|
45
|
+
# Discussion for this on GH: https://github.com/KrauseFx/xcode-install/issues/276
|
46
|
+
# It was not easily possible to reimplement the same system using built-in methods
|
47
|
+
# especially when it comes to resuming downloads
|
48
|
+
# Piping over stderror to Ruby directly didn't work, due to the lack of flushing
|
49
|
+
# from curl. The only reasonable way to trigger this, is to pipe things directly into a
|
50
|
+
# local file, and parse that, and just poll that. We could get real time updates using
|
51
|
+
# the `tail` command or similar, however the download task is not time sensitive enough
|
52
|
+
# to make this worth the extra complexity, that's why we just poll and
|
53
|
+
# wait for the process to be finished
|
54
|
+
progress_log_file = File.join(CACHE_DIR, "progress.#{Time.now.to_i}.progress")
|
55
|
+
FileUtils.rm_f(progress_log_file)
|
56
|
+
|
23
57
|
retry_options = ['--retry', '3']
|
24
|
-
|
25
|
-
|
58
|
+
command = [
|
59
|
+
'curl',
|
60
|
+
*options,
|
61
|
+
*retry_options,
|
62
|
+
'--location',
|
63
|
+
'--continue-at',
|
64
|
+
'-',
|
65
|
+
'--output',
|
66
|
+
output,
|
67
|
+
url
|
68
|
+
].map(&:to_s)
|
69
|
+
|
70
|
+
command_string = command.collect(&:shellescape).join(' ')
|
71
|
+
command_string += " 2> #{progress_log_file}" # to not run shellescape on the `2>`
|
26
72
|
|
27
73
|
# Run the curl command in a loop, retry when curl exit status is 18
|
28
74
|
# "Partial file. Only a part of the file was transferred."
|
29
75
|
# https://curl.haxx.se/mail/archive-2008-07/0098.html
|
30
76
|
# https://github.com/KrauseFx/xcode-install/issues/210
|
31
77
|
3.times do
|
32
|
-
|
33
|
-
|
34
|
-
|
78
|
+
# Non-blocking call of Open3
|
79
|
+
# We're not using the block based syntax, as the bacon testing
|
80
|
+
# library doesn't seem to support writing tests for it
|
81
|
+
stdin, stdout, stderr, wait_thr = Open3.popen3(command_string)
|
82
|
+
|
83
|
+
# Poll the file and see if we're done yet
|
84
|
+
while wait_thr.alive?
|
85
|
+
sleep(0.5) # it's not critical for this to be real-time
|
86
|
+
next unless File.exist?(progress_log_file) # it might take longer for it to be created
|
87
|
+
|
88
|
+
progress_content = File.read(progress_log_file).split("\r").last
|
89
|
+
|
90
|
+
# Print out the progress for the CLI
|
91
|
+
if progress
|
92
|
+
print "\r#{progress_content}%"
|
93
|
+
$stdout.flush
|
94
|
+
end
|
95
|
+
|
96
|
+
# Call back the block for other processes that might be interested
|
97
|
+
matched = progress_content.match(/^\s*(\d+)/)
|
98
|
+
next unless matched.length == 2
|
99
|
+
percent = matched[1].to_i
|
100
|
+
progress_block.call(percent) if progress_block
|
101
|
+
end
|
35
102
|
|
36
|
-
|
37
|
-
|
103
|
+
# as we're not making use of the block-based syntax
|
104
|
+
# we need to manually close those
|
105
|
+
stdin.close
|
106
|
+
stdout.close
|
107
|
+
stderr.close
|
108
|
+
|
109
|
+
return wait_thr.value.success? if wait_thr.value.success?
|
38
110
|
end
|
39
111
|
false
|
40
112
|
ensure
|
41
113
|
FileUtils.rm_f(COOKIES_PATH)
|
114
|
+
FileUtils.rm_f(progress_log_file)
|
42
115
|
end
|
43
116
|
end
|
44
117
|
|
118
|
+
# rubocop:disable Metrics/ClassLength
|
45
119
|
class Installer
|
46
120
|
attr_reader :xcodes
|
47
121
|
|
@@ -57,17 +131,47 @@ module XcodeInstall
|
|
57
131
|
File.symlink?(SYMLINK_PATH) ? SYMLINK_PATH : nil
|
58
132
|
end
|
59
133
|
|
60
|
-
def download(version, progress, url = nil)
|
61
|
-
|
62
|
-
|
134
|
+
def download(version, progress, url = nil, progress_block = nil)
|
135
|
+
xcode = find_xcode_version(version) if url.nil?
|
136
|
+
return if url.nil? && xcode.nil?
|
137
|
+
|
63
138
|
dmg_file = Pathname.new(File.basename(url || xcode.path))
|
64
139
|
|
65
|
-
result = Curl.new.fetch(
|
140
|
+
result = Curl.new.fetch(
|
141
|
+
url: url || xcode.url,
|
142
|
+
directory: CACHE_DIR,
|
143
|
+
cookies: url ? nil : spaceship.cookie,
|
144
|
+
output: dmg_file,
|
145
|
+
progress: progress,
|
146
|
+
progress_block: progress_block
|
147
|
+
)
|
66
148
|
result ? CACHE_DIR + dmg_file : nil
|
67
149
|
end
|
68
150
|
|
151
|
+
def find_xcode_version(version)
|
152
|
+
# By checking for the name and the version we have the best success rate
|
153
|
+
# Sometimes the user might pass
|
154
|
+
# "4.3 for Lion"
|
155
|
+
# or they might pass an actual Gem::Version
|
156
|
+
# Gem::Version.new("8.0.0")
|
157
|
+
# which should automatically match with "Xcode 8"
|
158
|
+
|
159
|
+
begin
|
160
|
+
parsed_version = Gem::Version.new(version)
|
161
|
+
rescue ArgumentError
|
162
|
+
nil
|
163
|
+
end
|
164
|
+
|
165
|
+
seedlist.each do |current_seed|
|
166
|
+
return current_seed if current_seed.name == version
|
167
|
+
return current_seed if parsed_version && current_seed.version == parsed_version
|
168
|
+
end
|
169
|
+
nil
|
170
|
+
end
|
171
|
+
|
69
172
|
def exist?(version)
|
70
|
-
|
173
|
+
return true if find_xcode_version(version)
|
174
|
+
false
|
71
175
|
end
|
72
176
|
|
73
177
|
def installed?(version)
|
@@ -80,6 +184,31 @@ module XcodeInstall
|
|
80
184
|
end
|
81
185
|
end
|
82
186
|
|
187
|
+
# Returns an array of `XcodeInstall::Xcode`
|
188
|
+
# <XcodeInstall::Xcode:0x007fa1d451c390
|
189
|
+
# @date_modified=2015,
|
190
|
+
# @name="6.4",
|
191
|
+
# @path="/Developer_Tools/Xcode_6.4/Xcode_6.4.dmg",
|
192
|
+
# @url=
|
193
|
+
# "https://developer.apple.com/devcenter/download.action?path=/Developer_Tools/Xcode_6.4/Xcode_6.4.dmg",
|
194
|
+
# @version=Gem::Version.new("6.4")>,
|
195
|
+
#
|
196
|
+
# the resulting list is sorted with the most recent release as first element
|
197
|
+
def seedlist
|
198
|
+
@xcodes = Marshal.load(File.read(LIST_FILE)) if LIST_FILE.exist? && xcodes.nil?
|
199
|
+
all_xcodes = (xcodes || fetch_seedlist)
|
200
|
+
|
201
|
+
# We have to set the `installed` value here, as we might still use
|
202
|
+
# the cached list of available Xcode versions, but have a new Xcode
|
203
|
+
# installed in the mean-time
|
204
|
+
cached_installed_versions = installed_versions.map(&:bundle_version)
|
205
|
+
all_xcodes.each do |current_xcode|
|
206
|
+
current_xcode.installed = cached_installed_versions.include?(current_xcode.version)
|
207
|
+
end
|
208
|
+
|
209
|
+
all_xcodes.sort_by(&:version)
|
210
|
+
end
|
211
|
+
|
83
212
|
def install_dmg(dmg_path, suffix = '', switch = true, clean = true)
|
84
213
|
archive_util = '/System/Library/CoreServices/Applications/Archive Utility.app/Contents/MacOS/Archive Utility'
|
85
214
|
prompt = "Please authenticate for Xcode installation.\nPassword: "
|
@@ -142,12 +271,13 @@ HELP
|
|
142
271
|
FileUtils.rm_f(dmg_path) if clean
|
143
272
|
end
|
144
273
|
|
145
|
-
|
146
|
-
|
274
|
+
# rubocop:disable Metrics/ParameterLists
|
275
|
+
def install_version(version, switch = true, clean = true, install = true, progress = true, url = nil, show_release_notes = true, progress_block = nil)
|
276
|
+
dmg_path = get_dmg(version, progress, url, progress_block)
|
147
277
|
fail Informative, "Failed to download Xcode #{version}." if dmg_path.nil?
|
148
278
|
|
149
279
|
if install
|
150
|
-
install_dmg(dmg_path, "-#{version.split(' ')[0]}", switch, clean)
|
280
|
+
install_dmg(dmg_path, "-#{version.to_s.split(' ')[0]}", switch, clean)
|
151
281
|
else
|
152
282
|
puts "Downloaded Xcode #{version} to '#{dmg_path}'"
|
153
283
|
end
|
@@ -199,14 +329,12 @@ HELP
|
|
199
329
|
begin
|
200
330
|
Spaceship.login(ENV['XCODE_INSTALL_USER'], ENV['XCODE_INSTALL_PASSWORD'])
|
201
331
|
rescue Spaceship::Client::InvalidUserCredentialsError
|
202
|
-
|
203
|
-
exit(1)
|
332
|
+
raise 'The specified Apple developer account credentials are incorrect.'
|
204
333
|
rescue Spaceship::Client::NoUserCredentialsError
|
205
|
-
|
334
|
+
raise <<-HELP
|
206
335
|
Please provide your Apple developer account credentials via the
|
207
336
|
XCODE_INSTALL_USER and XCODE_INSTALL_PASSWORD environment variables.
|
208
337
|
HELP
|
209
|
-
exit(1)
|
210
338
|
end
|
211
339
|
|
212
340
|
if ENV.key?('XCODE_INSTALL_TEAM_ID')
|
@@ -225,7 +353,7 @@ HELP
|
|
225
353
|
`sudo /usr/sbin/dseditgroup -o edit -t group -a staff _developer`
|
226
354
|
end
|
227
355
|
|
228
|
-
def get_dmg(version, progress = true, url = nil)
|
356
|
+
def get_dmg(version, progress = true, url = nil, progress_block = nil)
|
229
357
|
if url
|
230
358
|
path = Pathname.new(url)
|
231
359
|
return path if path.exist?
|
@@ -235,7 +363,7 @@ HELP
|
|
235
363
|
return cache_path if cache_path.exist?
|
236
364
|
end
|
237
365
|
|
238
|
-
download(version, progress, url)
|
366
|
+
download(version, progress, url, progress_block)
|
239
367
|
end
|
240
368
|
|
241
369
|
def fetch_seedlist
|
@@ -254,8 +382,7 @@ HELP
|
|
254
382
|
|
255
383
|
def installed
|
256
384
|
unless (`mdutil -s /` =~ /disabled/).nil?
|
257
|
-
|
258
|
-
exit(1)
|
385
|
+
raise 'Please enable Spotlight indexing for /Applications.'
|
259
386
|
end
|
260
387
|
|
261
388
|
`mdfind "kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode'" 2>/dev/null`.split("\n")
|
@@ -314,11 +441,6 @@ HELP
|
|
314
441
|
links
|
315
442
|
end
|
316
443
|
|
317
|
-
def seedlist
|
318
|
-
@xcodes = Marshal.load(File.read(LIST_FILE)) if LIST_FILE.exist? && xcodes.nil?
|
319
|
-
xcodes || fetch_seedlist
|
320
|
-
end
|
321
|
-
|
322
444
|
def verify_integrity(path)
|
323
445
|
puts `/usr/sbin/spctl --assess --verbose=4 --type execute #{path}`
|
324
446
|
$?.exitstatus.zero?
|
@@ -375,8 +497,13 @@ HELP
|
|
375
497
|
end
|
376
498
|
end
|
377
499
|
|
378
|
-
def download(progress)
|
379
|
-
result = Curl.new.fetch(
|
500
|
+
def download(progress, progress_block = nil)
|
501
|
+
result = Curl.new.fetch(
|
502
|
+
url: source,
|
503
|
+
directory: CACHE_DIR,
|
504
|
+
progress: progress,
|
505
|
+
progress_block: progress_block
|
506
|
+
)
|
380
507
|
result ? dmg_path : nil
|
381
508
|
end
|
382
509
|
|
@@ -478,12 +605,16 @@ HELP
|
|
478
605
|
end
|
479
606
|
|
480
607
|
def approve_license
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
608
|
+
if Gem::Version.new(version) < Gem::Version.new('7.3')
|
609
|
+
license_path = "#{@path}/Contents/Resources/English.lproj/License.rtf"
|
610
|
+
license_id = IO.read(license_path).match(/\bEA\d{4}\b/)
|
611
|
+
license_plist_path = '/Library/Preferences/com.apple.dt.Xcode.plist'
|
612
|
+
`sudo rm -rf #{license_plist_path}`
|
613
|
+
`sudo /usr/libexec/PlistBuddy -c "add :IDELastGMLicenseAgreedTo string #{license_id}" #{license_plist_path}`
|
614
|
+
`sudo /usr/libexec/PlistBuddy -c "add :IDEXcodeVersionForAgreedToGMLicense string #{@version}" #{license_plist_path}`
|
615
|
+
else
|
616
|
+
`sudo #{@path}/Contents/Developer/usr/bin/xcodebuild -license accept`
|
617
|
+
end
|
487
618
|
end
|
488
619
|
|
489
620
|
def available_simulators
|
@@ -510,27 +641,46 @@ HELP
|
|
510
641
|
`touch #{cache_dir}com.apple.dt.Xcode.InstallCheckCache_#{osx_build_version}_#{tools_version}`
|
511
642
|
end
|
512
643
|
|
513
|
-
|
514
|
-
|
515
|
-
def plist_entry(keypath)
|
516
|
-
`/usr/libexec/PlistBuddy -c "Print :#{keypath}" "#{path}/Contents/Info.plist"`.chomp
|
517
|
-
end
|
518
|
-
|
644
|
+
# This method might take a few ms, this could be improved by implementing https://github.com/KrauseFx/xcode-install/issues/273
|
519
645
|
def fetch_version
|
520
646
|
output = `DEVELOPER_DIR='' "#{@path}/Contents/Developer/usr/bin/xcodebuild" -version`
|
521
647
|
return '0.0' if output.nil? || output.empty? # ¯\_(ツ)_/¯
|
522
648
|
output.split("\n").first.split(' ')[1]
|
523
649
|
end
|
650
|
+
|
651
|
+
:private
|
652
|
+
|
653
|
+
def plist_entry(keypath)
|
654
|
+
`/usr/libexec/PlistBuddy -c "Print :#{keypath}" "#{path}/Contents/Info.plist"`.chomp
|
655
|
+
end
|
524
656
|
end
|
525
657
|
|
658
|
+
# A version of Xcode we fetched from the Apple Developer Portal
|
659
|
+
# we can download & install.
|
660
|
+
#
|
661
|
+
# Sample object:
|
662
|
+
# <XcodeInstall::Xcode:0x007fa1d451c390
|
663
|
+
# @date_modified=2015,
|
664
|
+
# @name="6.4",
|
665
|
+
# @path="/Developer_Tools/Xcode_6.4/Xcode_6.4.dmg",
|
666
|
+
# @url=
|
667
|
+
# "https://developer.apple.com/devcenter/download.action?path=/Developer_Tools/Xcode_6.4/Xcode_6.4.dmg",
|
668
|
+
# @version=Gem::Version.new("6.4")>,
|
526
669
|
class Xcode
|
527
670
|
attr_reader :date_modified
|
671
|
+
|
672
|
+
# The name might include extra information like "for Lion" or "beta 2"
|
528
673
|
attr_reader :name
|
529
674
|
attr_reader :path
|
530
675
|
attr_reader :url
|
531
676
|
attr_reader :version
|
532
677
|
attr_reader :release_notes_url
|
533
678
|
|
679
|
+
# Accessor since it's set by the `Installer`
|
680
|
+
attr_accessor :installed
|
681
|
+
|
682
|
+
alias installed? installed
|
683
|
+
|
534
684
|
def initialize(json, url = nil, release_notes_url = nil)
|
535
685
|
if url.nil?
|
536
686
|
@date_modified = json['dateModified'].to_i
|
data/lib/xcode/install/select.rb
CHANGED
@@ -8,9 +8,14 @@ module XcodeInstall
|
|
8
8
|
CLAide::Argument.new('VERSION', :true)
|
9
9
|
]
|
10
10
|
|
11
|
+
def self.options
|
12
|
+
[['--symlink', 'Update symlink in /Applications with selected Xcode']].concat(super)
|
13
|
+
end
|
14
|
+
|
11
15
|
def initialize(argv)
|
12
16
|
@installer = Installer.new
|
13
17
|
@version = argv.shift_argument
|
18
|
+
@should_symlink = argv.flag?('symlink', false)
|
14
19
|
super
|
15
20
|
end
|
16
21
|
|
@@ -24,6 +29,7 @@ module XcodeInstall
|
|
24
29
|
def run
|
25
30
|
xcode = @installer.installed_versions.detect { |v| v.version == @version }
|
26
31
|
`sudo xcode-select --switch #{xcode.path}`
|
32
|
+
@installer.symlink xcode.version if @should_symlink
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|
data/spec/curl_spec.rb
CHANGED
data/spec/install_spec.rb
CHANGED
@@ -12,26 +12,26 @@ module XcodeInstall
|
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'downloads and installs' do
|
15
|
-
Installer.any_instance.expects(:download).with('6.3', true, nil).returns('/some/path')
|
15
|
+
Installer.any_instance.expects(:download).with('6.3', true, nil, nil).returns('/some/path')
|
16
16
|
Installer.any_instance.expects(:install_dmg).with('/some/path', '-6.3', true, true)
|
17
17
|
Command::Install.run(['6.3'])
|
18
18
|
end
|
19
19
|
|
20
20
|
it 'downloads and installs with custom HTTP URL' do
|
21
21
|
url = 'http://yolo.com/xcode.dmg'
|
22
|
-
Installer.any_instance.expects(:download).with('6.3', true, url).returns('/some/path')
|
22
|
+
Installer.any_instance.expects(:download).with('6.3', true, url, nil).returns('/some/path')
|
23
23
|
Installer.any_instance.expects(:install_dmg).with('/some/path', '-6.3', true, true)
|
24
24
|
Command::Install.run(['6.3', "--url=#{url}"])
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'downloads and installs and does not switch if --no-switch given' do
|
28
|
-
Installer.any_instance.expects(:download).with('6.3', true, nil).returns('/some/path')
|
28
|
+
Installer.any_instance.expects(:download).with('6.3', true, nil, nil).returns('/some/path')
|
29
29
|
Installer.any_instance.expects(:install_dmg).with('/some/path', '-6.3', false, true)
|
30
30
|
Command::Install.run(['6.3', '--no-switch'])
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'downloads without progress if switch --no-progress is given' do
|
34
|
-
Installer.any_instance.expects(:download).with('6.3', false, nil).returns('/some/path')
|
34
|
+
Installer.any_instance.expects(:download).with('6.3', false, nil, nil).returns('/some/path')
|
35
35
|
Installer.any_instance.expects(:install_dmg).with('/some/path', '-6.3', true, true)
|
36
36
|
Command::Install.run(['6.3', '--no-progress'])
|
37
37
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
module XcodeInstall
|
4
|
+
describe '#fetch' do
|
5
|
+
before do
|
6
|
+
client = mock
|
7
|
+
client.stubs(:cookie).returns('customCookie')
|
8
|
+
Spaceship::PortalClient.stubs(:login).returns(client)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'downloads the file and calls the `progress_block` with the percentage' do
|
12
|
+
installer = Installer.new
|
13
|
+
|
14
|
+
xcode = XcodeInstall::Xcode.new('name' => 'Xcode 9.3',
|
15
|
+
'files' => [{
|
16
|
+
'remotePath' => '/Developer_Tools/Xcode_9.3/Xcode_9.3.xip'
|
17
|
+
}])
|
18
|
+
|
19
|
+
installer.stubs(:fetch_seedlist).returns([xcode])
|
20
|
+
|
21
|
+
stdin = 'stdin'
|
22
|
+
stdout = 'stdout'
|
23
|
+
stderr = 'stderr'
|
24
|
+
wait_thr = 'wait_thr'
|
25
|
+
|
26
|
+
stdin.stubs(:close)
|
27
|
+
stdout.stubs(:close)
|
28
|
+
stderr.stubs(:close)
|
29
|
+
|
30
|
+
current_time = '123123'
|
31
|
+
Time.stubs(:now).returns(current_time)
|
32
|
+
|
33
|
+
xip_path = File.join(File.expand_path('~'), '/Library/Caches/XcodeInstall/Xcode_9.3.xip')
|
34
|
+
progress_log_file = File.join(File.expand_path('~'), "/Library/Caches/XcodeInstall/progress.#{current_time}.progress")
|
35
|
+
|
36
|
+
command = [
|
37
|
+
'curl',
|
38
|
+
'--cookie customCookie',
|
39
|
+
'--cookie-jar /tmp/curl-cookies.txt',
|
40
|
+
'--retry 3',
|
41
|
+
'--location',
|
42
|
+
'--continue-at -',
|
43
|
+
"--output #{xip_path}",
|
44
|
+
'https://developer.apple.com/devcenter/download.action\\?path\\=/Developer_Tools/Xcode_9.3/Xcode_9.3.xip',
|
45
|
+
"2> #{progress_log_file}"
|
46
|
+
]
|
47
|
+
Open3.stubs(:popen3).with(command.join(' ')).returns([stdin, stdout, stderr, wait_thr])
|
48
|
+
|
49
|
+
wait_thr.stubs(:alive?).returns(true)
|
50
|
+
|
51
|
+
thr_value = 'thr_value'
|
52
|
+
wait_thr.stubs(:value).returns(thr_value)
|
53
|
+
thr_value.stubs(:success?).returns(true)
|
54
|
+
|
55
|
+
installer.stubs(:install_dmg).with(Pathname.new(xip_path), '-9.3', false, false)
|
56
|
+
|
57
|
+
Thread.new do
|
58
|
+
sleep(1)
|
59
|
+
File.write(progress_log_file, ' 0 4766M 0 6835k 0 0 573k 0 2:21:58 0:00:11 2:21:47 902k')
|
60
|
+
sleep(1)
|
61
|
+
File.write(progress_log_file, ' 5 4766M 0 6835k 0 0 573k 0 2:21:58 0:00:11 2:21:47 902k')
|
62
|
+
sleep(1)
|
63
|
+
File.write(progress_log_file, '50 4766M 0 6835k 0 0 573k 0 2:21:58 0:00:11 2:21:47 902k')
|
64
|
+
sleep(1)
|
65
|
+
File.write(progress_log_file, '100 4766M 0 6835k 0 0 573k 0 2:21:58 0:00:11 2:21:47 902k')
|
66
|
+
sleep(0.5)
|
67
|
+
wait_thr.stubs(:alive?).returns(false)
|
68
|
+
end
|
69
|
+
|
70
|
+
percentages = []
|
71
|
+
installer.install_version(
|
72
|
+
# version: the version to install
|
73
|
+
'9.3',
|
74
|
+
# `should_switch
|
75
|
+
false,
|
76
|
+
# `should_clean`
|
77
|
+
false, # false for now for faster debugging
|
78
|
+
# `should_install`
|
79
|
+
true,
|
80
|
+
# `progress`
|
81
|
+
false,
|
82
|
+
# `url` is nil, as we don't have a custom source
|
83
|
+
nil,
|
84
|
+
# `show_release_notes` is `false`, as this is a non-interactive machine
|
85
|
+
false,
|
86
|
+
# `progress_block` be updated on the download progress
|
87
|
+
proc do |percent|
|
88
|
+
percentages << percent
|
89
|
+
end
|
90
|
+
)
|
91
|
+
|
92
|
+
percentages.each do |current_percent|
|
93
|
+
# Verify all reported percentages are between 0 and 100
|
94
|
+
current_percent.should.be.close(50, 50)
|
95
|
+
end
|
96
|
+
# Verify we got a good amount of percentages reported
|
97
|
+
percentages.count.should.be.close(8, 4)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/spec/prerelease_spec.rb
CHANGED
@@ -29,7 +29,7 @@ module XcodeInstall
|
|
29
29
|
it 'can parse prereleases from 20150414' do
|
30
30
|
prereleases = parse_prereleases('20150414')
|
31
31
|
|
32
|
-
prereleases.should == [Xcode.new_prerelease('6.4', '/Developer_Tools/Xcode_6.4_Beta/Xcode_6.4_beta.dmg', '/Developer_Tools/Xcode_6.4_Beta/Xcode_6.4_beta_Release_Notes.pdf')]
|
32
|
+
prereleases.should == [Xcode.new_prerelease('6.4 beta', '/Developer_Tools/Xcode_6.4_Beta/Xcode_6.4_beta.dmg', '/Developer_Tools/Xcode_6.4_Beta/Xcode_6.4_beta_Release_Notes.pdf')]
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'can parse prereleases from 20150427' do
|
data/xcode-install.gemspec
CHANGED
@@ -21,7 +21,10 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
22
|
spec.require_paths = ['lib']
|
23
23
|
|
24
|
+
# CLI parsing
|
24
25
|
spec.add_dependency 'claide', '>= 0.9.1', '< 1.1.0'
|
26
|
+
|
27
|
+
# contains spaceship, which is used for auth and dev portal interactions
|
25
28
|
spec.add_dependency 'fastlane', '>= 2.1.0', '< 3.0.0'
|
26
29
|
|
27
30
|
spec.add_development_dependency 'bundler', '~> 1.7'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xcode-install
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boris Bügling
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: claide
|
@@ -91,13 +91,13 @@ files:
|
|
91
91
|
- ".gitignore"
|
92
92
|
- ".rubocop.yml"
|
93
93
|
- ".rubocop_todo.yml"
|
94
|
-
- ".travis.yml"
|
95
94
|
- Gemfile
|
96
95
|
- LICENSE
|
97
96
|
- README.md
|
98
97
|
- Rakefile
|
99
98
|
- bin/xcversion
|
100
99
|
- "bin/\U0001F389"
|
100
|
+
- circle.yml
|
101
101
|
- lib/xcode/install.rb
|
102
102
|
- lib/xcode/install/cleanup.rb
|
103
103
|
- lib/xcode/install/cli.rb
|
@@ -133,6 +133,7 @@ files:
|
|
133
133
|
- spec/fixtures/yolo.json
|
134
134
|
- spec/install_spec.rb
|
135
135
|
- spec/installed_spec.rb
|
136
|
+
- spec/installer_spec.rb
|
136
137
|
- spec/json_spec.rb
|
137
138
|
- spec/list_spec.rb
|
138
139
|
- spec/prerelease_spec.rb
|
@@ -186,9 +187,9 @@ test_files:
|
|
186
187
|
- spec/fixtures/yolo.json
|
187
188
|
- spec/install_spec.rb
|
188
189
|
- spec/installed_spec.rb
|
190
|
+
- spec/installer_spec.rb
|
189
191
|
- spec/json_spec.rb
|
190
192
|
- spec/list_spec.rb
|
191
193
|
- spec/prerelease_spec.rb
|
192
194
|
- spec/spec_helper.rb
|
193
195
|
- spec/uninstall_spec.rb
|
194
|
-
has_rdoc:
|