xcode-install 2.3.1 → 2.4.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/.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
|
-
[](http://badge.fury.io/rb/xcode-install)
|
3
|
+
[](http://badge.fury.io/rb/xcode-install) [](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:
|