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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 94d2ba944e260009634b80f9962d20322fd1f902
4
- data.tar.gz: eeefd86022b7f7b2649c2d336b74edcb86fae74e
3
+ metadata.gz: fd49f95272394358487c0eed4f39884120ea0717
4
+ data.tar.gz: f6dc4b73867986caaa06811a2c91e90f6741ec21
5
5
  SHA512:
6
- metadata.gz: 0c35741166c044efbf422f5957bcb8527b6935ed0c8177d02f890d6ef5ee2b96bf46bfe379931317f5aca5491a387b199b08fe4e099299b406d2944530e802e1
7
- data.tar.gz: 58e3d669156fe437fb0519be9f2c08e2a987bda6c7c859cfd2c4019d6bd97a01fac06fc18ed5bd73bfe7712ebbde62e70794f8b33d428574d010b0523230ed74
6
+ metadata.gz: 97e5d324c43f25f40f1b1682eac49544f6d695f44f2e27a942eb9782b635fb770bc271ef5ec81c93f4ace20523fa4cb2ad6dfb7dee03a446923d04feccae663e
7
+ data.tar.gz: 3ee8b79219737c56cb9d9a8050be6bc88aa15d74b94329907ec742e11bf95341268517ce8aff4218a14192638b465441896bfbd77412fc28bae3615800d2dd9b
data/.gitignore CHANGED
@@ -13,3 +13,4 @@
13
13
  *.a
14
14
  mkmf.log
15
15
  .DS_Store
16
+ test
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
- Installed versions will be omitted and by default, only the latest major version is listed.
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 list --all
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. It will also be automatically selected.
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
- they have to be installed using the full name, e.g. `xcversion install '7 GM seed'`.
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.
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- load File.expand_path("../xcversion", __FILE__)
3
+ load File.expand_path('../xcversion', __FILE__)
@@ -0,0 +1,7 @@
1
+ machine:
2
+ xcode:
3
+ version: 9.0
4
+ test:
5
+ override:
6
+ - bundle exec rake spec
7
+ - bundle exec rake rubocop
@@ -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
- def fetch(url, directory = nil, cookies = nil, output = nil, progress = true)
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
- progress = progress ? '--progress-bar' : '--silent'
25
- command = ['curl', *options, *retry_options, '--location', '--continue-at', '-', progress, '--output', output, url].map(&:to_s)
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
- io = IO.popen(command)
33
- io.each { |line| puts line }
34
- io.close
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
- exit_code = $?.exitstatus
37
- return exit_code.zero? unless exit_code == 18
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
- return unless url || exist?(version)
62
- xcode = seedlist.find { |x| x.name == version } unless url
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(url || xcode.url, CACHE_DIR, url ? nil : spaceship.cookie, dmg_file, progress)
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
- list_versions.include?(version)
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
- def install_version(version, switch = true, clean = true, install = true, progress = true, url = nil, show_release_notes = true)
146
- dmg_path = get_dmg(version, progress, url)
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
- $stderr.puts 'The specified Apple developer account credentials are incorrect.'
203
- exit(1)
332
+ raise 'The specified Apple developer account credentials are incorrect.'
204
333
  rescue Spaceship::Client::NoUserCredentialsError
205
- $stderr.puts <<-HELP
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
- $stderr.puts 'Please enable Spotlight indexing for /Applications.'
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(source, CACHE_DIR, nil, nil, progress)
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
- license_path = "#{@path}/Contents/Resources/English.lproj/License.rtf"
482
- license_id = IO.read(license_path).match(/\bEA\d{4}\b/)
483
- license_plist_path = '/Library/Preferences/com.apple.dt.Xcode.plist'
484
- `sudo rm -rf #{license_plist_path}`
485
- `sudo /usr/libexec/PlistBuddy -c "add :IDELastGMLicenseAgreedTo string #{license_id}" #{license_plist_path}`
486
- `sudo /usr/libexec/PlistBuddy -c "add :IDEXcodeVersionForAgreedToGMLicense string #{@version}" #{license_plist_path}`
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
- :private
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module XcodeInstall
2
- VERSION = '2.3.1'.freeze
2
+ VERSION = '2.4.0'.freeze
3
3
  end
@@ -18,7 +18,7 @@ module XcodeInstall
18
18
  curl = XcodeInstall::Curl.new
19
19
  result = nil
20
20
  XcodeInstall.silence_stderr do
21
- result = curl.fetch('http://0.0.0.0/test')
21
+ result = curl.fetch(url: 'http://0.0.0.0/test')
22
22
  end
23
23
  result.should == false
24
24
  end
@@ -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
@@ -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
@@ -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.3.1
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: 2017-10-04 00:00:00.000000000 Z
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:
@@ -1,10 +0,0 @@
1
- language: ruby
2
- os: osx
3
- osx_image: xcode7.3
4
- rvm: 2.0.0-p598
5
- cache: bundler
6
- before_install:
7
- - gem install bundler
8
- script:
9
- - bundle exec rake spec
10
- - bundle exec rake rubocop