xcode-installer 0.1.9 → 0.2.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,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ODg0YjUzMjk1MDFkOGMyYTc2ODczYjc4MDRjZGYxM2ZiMTk3ZGEzMg==
4
+ Y2VmNDYyM2JlYWVmMGM0NGQyNTY5Zjc1NmE1ZmJjZjEyM2VkY2YwZA==
5
5
  data.tar.gz: !binary |-
6
- NmQ5ZGEyNjA3MDZjNDM1OTFiNTZmMjA3NWE5ZWE0NTAzYmIzYjBiMQ==
6
+ NmY3NzQyZDExOTU5OTIyY2I1NDRhNjYyYTJmMDk5NmM3ZWEyYjE5NQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- MGVmNjZkMWYzNDZkY2JjZjA4NWE3N2FkNmU3ODFjMWQ5ZTUxOGRkZmI0MDE1
10
- MWJmYjJkYzA3MjliMjQwYzc5YjhlOWE4NjY4YzdmMWI5NjJhMGY4M2Q0NGM3
11
- MDYzMGMzYjZlZjk3MmE1Nzc0YTY1ZmRiNjIwZDI5ZDU5ZWRkZWY=
9
+ MDE0ODU0YThhMjZhZDQxMDg1MzA5ZGYwNGM2NmU0ZWQ1ZGU5NmIyYzg2OGM0
10
+ ZWEzNDNhNDk1YTA0OTkxNGYxZjg1ZWM2MTliNGRlZGUzZTg5ZjI5N2MyYzNm
11
+ YjMxMDgxNWFjY2UyZWEzMDRmN2JiZTk5Yzc4YTE4NmViOTgzOTI=
12
12
  data.tar.gz: !binary |-
13
- YmRlNGFkMDNjNzI3YmUwOTUzNzRiY2E0Yjk0ZGM5YTEwNzQ0YThjMjI5Njdk
14
- YWU1OTljNDVkZjY1YTRmYTA0YWI5OTU5MzUyNWNkMDIzZDhmODEyODYzMWQx
15
- OTYzZTQxZjEwY2IxYzEzYWI4ZGFhNGNjOWMyYzk3NGFlZWFmMmQ=
13
+ MmVmY2Q2NmZkZDBkNjIwMDE5NTQxZmE0ZDYwMGY0ODljYjMzZTA2MGI2Yjc3
14
+ ODhhOThhZGJjYmUyMWU0ZjA0ZGQ1OWYwMDY2NmFlYWM3ODNmYjNjYWE4ODEw
15
+ NDYxNjg4NTdlMjU0ZDVkNjE4MzU2MmVkYWE2YjViZTUyMjU2OWE=
data/HISTORY.md CHANGED
@@ -1,6 +1,11 @@
1
+ ## 0.2.0 (2013-10-22)
2
+
3
+ * install command working (shows progress bar)
4
+ * add xcode 5.0.1
5
+
1
6
  ## 0.1.9 (2013-09-20)
2
7
 
3
- * add xcode 5 (ga)
8
+ * add xcode 5
4
9
 
5
10
  ## 0.1.8 (2013-09-10)
6
11
 
data/bin/xcode-installer CHANGED
@@ -24,5 +24,6 @@ global_option '--verbose'
24
24
  require 'xcode-installer/apple-developer-center'
25
25
  require 'xcode-installer/xcode-versions'
26
26
  require 'xcode-installer/helpers'
27
+ require 'xcode-installer/release-manager'
27
28
  require 'xcode-installer/agent'
28
29
  require 'xcode-installer/commands'
@@ -1,3 +1,5 @@
1
+ require 'xcode-installer/download'
2
+
1
3
  command :'download' do |c|
2
4
  c.syntax = 'xcode-installer download [options]'
3
5
  c.option '--dry-run', 'Enables a HEAD request instead of downloading the file'
@@ -6,35 +8,5 @@ command :'download' do |c|
6
8
  c.summary = 'Initiates the download'
7
9
  c.description = ''
8
10
 
9
- c.action do |args, options|
10
- if options.release
11
- xcode_version = options.release
12
- else
13
- if options.pre_release
14
- xcode_version = XcodeInstaller::XcodeVersions::LATEST_DP
15
- else
16
- xcode_version = XcodeInstaller::XcodeVersions::LATEST_GA
17
- end
18
- end
19
-
20
- mgr = XcodeInstaller::XcodeVersions::ReleaseManager.new
21
- release = mgr.get_release(xcode_version, options.pre_release, 'gui')
22
-
23
- if release
24
- xcode_url = release['download_url']
25
- else
26
- puts "No Xcode release with number #{xcode_version}. Use the 'list' command to see a list of known releases."
27
- exit
28
- end
29
-
30
- puts "Downloading Xcode #{xcode_version}"
31
- puts xcode_url
32
-
33
- agent.verbose = options.verbose
34
- agent.dry_run = options.dry_run
35
- try {
36
- filename = agent.download(xcode_url)
37
- puts "File saved to: #{Dir.pwd}/#{filename}" if filename
38
- }
39
- end
11
+ c.action XcodeInstaller::Download, :action
40
12
  end
@@ -1,33 +1,12 @@
1
+ require 'xcode-installer/install'
2
+
1
3
  command :'install' do |c|
2
4
  c.syntax = 'xcode-installer install [options]'
3
5
  c.option '--no-trash', 'Prevents trashing .dmg after install'
4
- c.summary = 'Installs xcode from a previous download'
6
+ c.option '--release STRING', 'Used to specify an old or pre-release version of Xcode. Otherwise, latest GA release of Xcode is downloaded.'
7
+ c.option '--pre-release', 'Specifies to download the latest pre-release version of Xcode.'
8
+ c.summary = 'Installs xcode from a .dmg file, downloading it if not already present'
5
9
  c.description = 'NEEDS SOME WORK - Use download and mount .dmg manually for now'
6
10
 
7
- c.action do |args, options|
8
- files = Dir.glob('*.dmg')
9
- if files.length == 0
10
- puts 'No .dmg files found in current directory. Run the download command first.'
11
- return
12
- elsif files.length > 1
13
- puts 'Multiple .dmg files found in the current directory. Currently no support for specifying file.'
14
- return
15
- end
16
- dmg_file = files[0]
17
-
18
- # Mount disk image
19
- mountpoint = '/Volumes/Xcode'
20
- # system "hdid '#{dmg_file}' -mountpoint #{mountpoint}"
21
- system 'hdiutil attach -quiet xcode4620419895a.dmg'
22
-
23
- # Trash existing install (so command is rerunnable)
24
- destination = '/Applications/Xcode.app'
25
- Trash.new.throw_out(destination)
26
-
27
- # Copy into /Applications
28
- puts 'Copying Xcode.app into Applications directory (this can take a little while)'
29
- system "cp -R #{mountpoint}/Xcode.app #{destination}"
30
-
31
- system 'hdiutil detach -quiet #{mountpoint}'
32
- end
11
+ c.action XcodeInstaller::Install, :action
33
12
  end
@@ -1,54 +1,9 @@
1
+ require 'xcode-installer/list'
2
+
1
3
  command :'list' do |c|
2
4
  c.syntax = 'xcode-installer list [all|gui|cli]'
3
5
  c.summary = 'Lists the versions of Xcode available for downloading'
4
6
  c.description = 'Shows only the Xcode GUI versions by default. Specify "all" or "cli" to show command-line tools'
5
7
 
6
- c.action do |args, options|
7
- # puts "args: #{args}"
8
-
9
- show_all = args.include? 'all'
10
- show_gui = args.include? 'gui'
11
- show_cli = args.include? 'cli'
12
- # Show GUI when no args given
13
- show_gui = true if !show_all && !show_gui && !show_cli
14
-
15
- # latest = XcodeInstaller::XcodeVersions::LATEST
16
- mgr = XcodeInstaller::XcodeVersions::ReleaseManager.new
17
-
18
- gui_versions = mgr.get_all('gui')
19
- cli_versions = mgr.get_all('cli')
20
- puts cli_versions
21
-
22
- if show_all || show_gui
23
- title = 'Xcode GUI'
24
- table = Terminal::Table.new :title => title do |t|
25
- t << ['Version', 'Download URL']
26
- gui_versions.each do |release|
27
- t << :separator
28
-
29
- row = [release['version'], release['download_url']]
30
- t << row
31
- end
32
- end
33
- puts table
34
- end
35
-
36
- # Extra line between tables
37
- puts if show_all
38
-
39
- if show_all || show_cli
40
- title = 'Xcode Command-Line'
41
- table = Terminal::Table.new :title => title do |t|
42
- t << ['Version', 'Download URL']
43
- cli_versions.each do |release|
44
- t << :separator
45
-
46
- row = [release['version'], release['download_url']]
47
- t << row
48
- end
49
- end
50
- puts table
51
- end
52
-
53
- end
8
+ c.action XcodeInstaller::List, :action
54
9
  end
@@ -1,16 +1,9 @@
1
+ require 'xcode-installer/login'
2
+
1
3
  command :login do |c|
2
4
  c.syntax = 'xcode-installer login'
3
5
  c.summary = 'Save account credentials'
4
6
  c.description = ''
5
7
 
6
- c.action do |args, options|
7
- say_warning "You are already authenticated" if Security::InternetPassword.find(:server => XcodeInstaller::AppleDeveloperCenter::HOST)
8
-
9
- user = ask "Username:"
10
- pass = password "Password:"
11
-
12
- Security::InternetPassword.add(XcodeInstaller::AppleDeveloperCenter::HOST, user, pass)
13
-
14
- say_ok "Account credentials saved"
15
- end
8
+ c.action XcodeInstaller::Login, :action
16
9
  end
@@ -1,13 +1,9 @@
1
+ require 'xcode-installer/logout'
2
+
1
3
  command :logout do |c|
2
4
  c.syntax = 'xcode-installer logout'
3
5
  c.summary = 'Remove account credentials'
4
6
  c.description = ''
5
7
 
6
- c.action do |args, options|
7
- say_error "You are not authenticated" and abort unless Security::InternetPassword.find(:server => XcodeInstaller::AppleDeveloperCenter::HOST)
8
-
9
- Security::InternetPassword.delete(:server => XcodeInstaller::AppleDeveloperCenter::HOST)
10
-
11
- say_ok "Account credentials removed"
12
- end
8
+ c.action XcodeInstaller::Logout, :action
13
9
  end
@@ -0,0 +1,34 @@
1
+ #
2
+ # XcodeInstaller::Download (Command Class)
3
+ #
4
+ # Determines the requested Xcode release (with the help of the ReleaseManager class) and handles the download, showing a progress bar with ETA.
5
+ #
6
+
7
+ module XcodeInstaller
8
+ class Download
9
+ attr_accessor :release
10
+
11
+ def action(args, options)
12
+ mgr = XcodeInstaller::ReleaseManager.new
13
+ @release = mgr.get_release(options.release, options.pre_release)
14
+
15
+ if release
16
+ xcode_url = @release['download_url']
17
+ else
18
+ puts "No Xcode release with number #{options.release}. Use the 'list' command to see a list of known releases."
19
+ exit
20
+ end
21
+
22
+ puts "Downloading Xcode #{@release['version']}"
23
+ puts xcode_url
24
+
25
+ agent.verbose = options.verbose
26
+ agent.dry_run = options.dry_run
27
+ try {
28
+ filename = agent.download(xcode_url)
29
+ puts "File saved to: #{Dir.pwd}/#{filename}" if filename
30
+ }
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,530 @@
1
+ #
2
+ # XcodeInstaller::Install (Command Class)
3
+ #
4
+ # First, this command will look for the expected version of the Xcode installation .dmg file in the download (current) dir.
5
+ # If not found, the download will be initiated, afterwards the expected file will be verified.
6
+ # Installation will continue once the expected .dmg file is found. The .dmg will be mounted and the .app file copied out.
7
+ # By default, the version number will be inserted into the Xcode.app name. This will prevent collision with the Mac App Store
8
+ # installed version. An option may allow for this utility to overwrite the Xcode.app so that there is only a single copy installed.
9
+ # It would be nice to also set the newly installed Xcode to be the default on the command line using 'xcode-select --switch'.
10
+ #
11
+
12
+ require 'xcode-installer/download'
13
+ require 'ruby-progressbar'
14
+
15
+ module XcodeInstaller
16
+ class Install
17
+ attr_accessor :release, :version_suffix, :copied_kb, :copied_file_count, :progress_bar
18
+
19
+ def initialize
20
+ @copied_kb = 0
21
+ end
22
+
23
+ def action(args, options)
24
+ mgr = XcodeInstaller::ReleaseManager.new
25
+ @release = mgr.get_release(options.release, options.pre_release)
26
+ @version_suffix = "-#{@release['version']}"
27
+
28
+ files = Dir.glob(dmg_file_name)
29
+ if files.length == 0
30
+ puts '#{dmg_file_name} file not found in current directory. Run the download command first.'
31
+ return
32
+ elsif files.length > 1
33
+ puts 'Multiple #{dmg_file_name} files found in the current directory. Is this partition formatted with a case-insensitive disk format?'
34
+ return
35
+ end
36
+ dmg_file = files[0]
37
+ # TODO: if verbose...
38
+ # puts dmg_file
39
+
40
+ # Mount disk image
41
+ mountpoint = '/Volumes/Xcode'
42
+ # system "hdid '#{dmg_file}' -mountpoint #{mountpoint}"
43
+ system "hdiutil attach -quiet #{dmg_file}"
44
+
45
+ # Trash existing install (so command is rerunnable)
46
+ destination = "/Applications/Xcode#{version_suffix}.app"
47
+ Trash.new.throw_out(destination)
48
+
49
+ # TODO: Dynamically determine .app file name (DP releases have the version embedded)
50
+ copy("#{mountpoint}/Xcode.app", destination)
51
+
52
+ # system "hdiutil detach -quiet #{mountpoint}"
53
+ end
54
+
55
+ def dmg_file_name
56
+ return File.basename(@release['download_url'])
57
+ end
58
+
59
+ def copy(source_path, destination_path)
60
+ # Copy into /Applications
61
+ # puts 'Copying Xcode.app into Applications directory (this can take a little while)'
62
+ # TODO: wrap debug output with verbose checks
63
+ puts "#{source_path} -> #{destination_path}"
64
+ # system "cp -R #{source_path} #{destination_path}"
65
+
66
+ total_kb = dir_size(source_path)
67
+ # puts total_kb
68
+
69
+ # puts File.stat(source_path).size
70
+
71
+ @progress_bar = ProgressBar.create(:title => "Copying", :starting_at => 0, :total => @release['app_size_extracted'])
72
+ @copied_file_count = 0
73
+ cp_r(source_path, destination_path, {})
74
+ @progress_bar.finish
75
+
76
+ # block_size = File.stat(source_path).blksize
77
+ # puts @copied_kb
78
+
79
+ # in_name = "src_file.txt"
80
+ # out_name = "dest_file.txt"
81
+
82
+ # in_file = File.new(in_name, "r")
83
+ # out_file = File.new(out_name, "w")
84
+
85
+ # in_size = File.size(in_name)
86
+ # batch_bytes = ( in_size / 100 ).ceil
87
+ # total = 0
88
+ # p_bar = ProgressBar.new('Copying', 100)
89
+
90
+ # buffer = in_file.sysread(batch_bytes)
91
+ # while total < in_size do
92
+ # out_file.syswrite(buffer)
93
+ # p_bar.inc
94
+ # total += batch_bytes
95
+ # if (in_size - total) < batch_bytes
96
+ # batch_bytes = (in_size - total)
97
+ # end
98
+ # buffer = in_file.sysread(batch_bytes)
99
+ # end
100
+ # p_bar.finish
101
+ end
102
+
103
+ # Exmaple output of the du command:
104
+ # 2359828 /Volumes/Xcode/Xcode.app/
105
+ def dir_size(path)
106
+ output = `du -sk '#{path}' 2> /dev/null`
107
+ return output.split(" ").first.to_i * 1024
108
+ end
109
+
110
+ def accumulate_kbytes(path)
111
+ # @progress_bar.log path
112
+ # @progress_bar.increment
113
+ # @copied_kb += File.stat(path).size if File.exists?(path)
114
+ @copied_file_count++
115
+ if @copied_file_count.modulo(10000) == 0
116
+ @progress_bar.progress = dir_size("/Applications/Xcode-5.app")
117
+ end
118
+ end
119
+
120
+ ##########################################################################
121
+ # #
122
+ # The following code was copied out of fileutils.rb from ruby 1.9.3-p392 #
123
+ # #
124
+ ##########################################################################
125
+
126
+ def cp_r(src, dest, options = {})
127
+ # fu_check_options options, OPT_TABLE['cp_r']
128
+ # fu_output_message "cp -r#{options[:preserve] ? 'p' : ''}#{options[:remove_destination] ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
129
+ return if options[:noop]
130
+ options = options.dup
131
+ options[:dereference_root] = true unless options.key?(:dereference_root)
132
+ fu_each_src_dest(src, dest) do |s, d|
133
+ copy_entry s, d, options[:preserve], options[:dereference_root], options[:remove_destination]
134
+ end
135
+ end
136
+
137
+ def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
138
+ Entry_.new(src, nil, dereference_root).traverse do |ent|
139
+ destent = Entry_.new(dest, ent.rel, false)
140
+
141
+ # puts "#{dir_size(ent.path)} #{ent.path}"
142
+ accumulate_kbytes(ent.path)
143
+
144
+ File.unlink destent.path if remove_destination && File.file?(destent.path)
145
+ ent.copy destent.path
146
+ ent.copy_metadata destent.path if preserve
147
+ end
148
+ end
149
+
150
+ def fu_each_src_dest(src, dest)
151
+ fu_each_src_dest0(src, dest) do |s, d|
152
+ raise ArgumentError, "same file: #{s} and #{d}" if File.identical?(s, d)
153
+ yield s, d, File.stat(s)
154
+ end
155
+ end
156
+
157
+ def fu_each_src_dest0(src, dest)
158
+ if tmp = Array.try_convert(src)
159
+ tmp.each do |s|
160
+ s = File.path(s)
161
+ yield s, File.join(dest, File.basename(s))
162
+ end
163
+ else
164
+ src = File.path(src)
165
+ if File.directory?(dest)
166
+ yield src, File.join(dest, File.basename(src))
167
+ else
168
+ yield src, File.path(dest)
169
+ end
170
+ end
171
+ end
172
+
173
+ end # class Install
174
+
175
+ private
176
+
177
+ module StreamUtils_
178
+ private
179
+
180
+ def fu_windows?
181
+ /mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM
182
+ end
183
+
184
+ def fu_copy_stream0(src, dest, blksize = nil) #:nodoc:
185
+ IO.copy_stream(src, dest)
186
+ end
187
+
188
+ def fu_stream_blksize(*streams)
189
+ streams.each do |s|
190
+ next unless s.respond_to?(:stat)
191
+ size = fu_blksize(s.stat)
192
+ return size if size
193
+ end
194
+ fu_default_blksize()
195
+ end
196
+
197
+ def fu_blksize(st)
198
+ s = st.blksize
199
+ return nil unless s
200
+ return nil if s == 0
201
+ s
202
+ end
203
+
204
+ def fu_default_blksize
205
+ 1024
206
+ end
207
+ end
208
+
209
+ include StreamUtils_
210
+ extend StreamUtils_
211
+
212
+ class Entry_ #:nodoc: internal use only
213
+ include StreamUtils_
214
+
215
+ def initialize(a, b = nil, deref = false)
216
+ @prefix = @rel = @path = nil
217
+ if b
218
+ @prefix = a
219
+ @rel = b
220
+ else
221
+ @path = a
222
+ end
223
+ @deref = deref
224
+ @stat = nil
225
+ @lstat = nil
226
+ end
227
+
228
+ def inspect
229
+ "\#<#{self.class} #{path()}>"
230
+ end
231
+
232
+ def path
233
+ if @path
234
+ File.path(@path)
235
+ else
236
+ join(@prefix, @rel)
237
+ end
238
+ end
239
+
240
+ def prefix
241
+ @prefix || @path
242
+ end
243
+
244
+ def rel
245
+ @rel
246
+ end
247
+
248
+ def dereference?
249
+ @deref
250
+ end
251
+
252
+ def exist?
253
+ lstat! ? true : false
254
+ end
255
+
256
+ def file?
257
+ s = lstat!
258
+ s and s.file?
259
+ end
260
+
261
+ def directory?
262
+ s = lstat!
263
+ s and s.directory?
264
+ end
265
+
266
+ def symlink?
267
+ s = lstat!
268
+ s and s.symlink?
269
+ end
270
+
271
+ def chardev?
272
+ s = lstat!
273
+ s and s.chardev?
274
+ end
275
+
276
+ def blockdev?
277
+ s = lstat!
278
+ s and s.blockdev?
279
+ end
280
+
281
+ def socket?
282
+ s = lstat!
283
+ s and s.socket?
284
+ end
285
+
286
+ def pipe?
287
+ s = lstat!
288
+ s and s.pipe?
289
+ end
290
+
291
+ S_IF_DOOR = 0xD000
292
+
293
+ def door?
294
+ s = lstat!
295
+ s and (s.mode & 0xF000 == S_IF_DOOR)
296
+ end
297
+
298
+ def entries
299
+ opts = {}
300
+ opts[:encoding] = ::Encoding::UTF_8 if fu_windows?
301
+ Dir.entries(path(), opts)\
302
+ .reject {|n| n == '.' or n == '..' }\
303
+ .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) }
304
+ end
305
+
306
+ def stat
307
+ return @stat if @stat
308
+ if lstat() and lstat().symlink?
309
+ @stat = File.stat(path())
310
+ else
311
+ @stat = lstat()
312
+ end
313
+ @stat
314
+ end
315
+
316
+ def stat!
317
+ return @stat if @stat
318
+ if lstat! and lstat!.symlink?
319
+ @stat = File.stat(path())
320
+ else
321
+ @stat = lstat!
322
+ end
323
+ @stat
324
+ rescue SystemCallError
325
+ nil
326
+ end
327
+
328
+ def lstat
329
+ if dereference?
330
+ @lstat ||= File.stat(path())
331
+ else
332
+ @lstat ||= File.lstat(path())
333
+ end
334
+ end
335
+
336
+ def lstat!
337
+ lstat()
338
+ rescue SystemCallError
339
+ nil
340
+ end
341
+
342
+ def chmod(mode)
343
+ if symlink?
344
+ File.lchmod mode, path() if have_lchmod?
345
+ else
346
+ File.chmod mode, path()
347
+ end
348
+ end
349
+
350
+ def chown(uid, gid)
351
+ if symlink?
352
+ File.lchown uid, gid, path() if have_lchown?
353
+ else
354
+ File.chown uid, gid, path()
355
+ end
356
+ end
357
+
358
+ def copy(dest)
359
+ case
360
+ when file?
361
+ copy_file dest
362
+ when directory?
363
+ if !File.exist?(dest) and descendant_diretory?(dest, path)
364
+ raise ArgumentError, "cannot copy directory %s to itself %s" % [path, dest]
365
+ end
366
+ begin
367
+ Dir.mkdir dest
368
+ rescue
369
+ raise unless File.directory?(dest)
370
+ end
371
+ when symlink?
372
+ File.symlink File.readlink(path()), dest
373
+ when chardev?
374
+ raise "cannot handle device file" unless File.respond_to?(:mknod)
375
+ mknod dest, ?c, 0666, lstat().rdev
376
+ when blockdev?
377
+ raise "cannot handle device file" unless File.respond_to?(:mknod)
378
+ mknod dest, ?b, 0666, lstat().rdev
379
+ when socket?
380
+ raise "cannot handle socket" unless File.respond_to?(:mknod)
381
+ mknod dest, nil, lstat().mode, 0
382
+ when pipe?
383
+ raise "cannot handle FIFO" unless File.respond_to?(:mkfifo)
384
+ mkfifo dest, 0666
385
+ when door?
386
+ raise "cannot handle door: #{path()}"
387
+ else
388
+ raise "unknown file type: #{path()}"
389
+ end
390
+ end
391
+
392
+ def copy_file(dest)
393
+ File.open(path()) do |s|
394
+ File.open(dest, 'wb', s.stat.mode) do |f|
395
+ IO.copy_stream(s, f)
396
+ end
397
+ end
398
+ end
399
+
400
+ def copy_metadata(path)
401
+ st = lstat()
402
+ File.utime st.atime, st.mtime, path
403
+ begin
404
+ File.chown st.uid, st.gid, path
405
+ rescue Errno::EPERM
406
+ # clear setuid/setgid
407
+ File.chmod st.mode & 01777, path
408
+ else
409
+ File.chmod st.mode, path
410
+ end
411
+ end
412
+
413
+ def remove
414
+ if directory?
415
+ remove_dir1
416
+ else
417
+ remove_file
418
+ end
419
+ end
420
+
421
+ def remove_dir1
422
+ platform_support {
423
+ Dir.rmdir path().chomp(?/)
424
+ }
425
+ end
426
+
427
+ def remove_file
428
+ platform_support {
429
+ File.unlink path
430
+ }
431
+ end
432
+
433
+ def platform_support
434
+ return yield unless fu_windows?
435
+ first_time_p = true
436
+ begin
437
+ yield
438
+ rescue Errno::ENOENT
439
+ raise
440
+ rescue => err
441
+ if first_time_p
442
+ first_time_p = false
443
+ begin
444
+ File.chmod 0700, path() # Windows does not have symlink
445
+ retry
446
+ rescue SystemCallError
447
+ end
448
+ end
449
+ raise err
450
+ end
451
+ end
452
+
453
+ def preorder_traverse
454
+ stack = [self]
455
+ while ent = stack.pop
456
+ yield ent
457
+ stack.concat ent.entries.reverse if ent.directory?
458
+ end
459
+ end
460
+
461
+ alias traverse preorder_traverse
462
+
463
+ def postorder_traverse
464
+ if directory?
465
+ entries().each do |ent|
466
+ ent.postorder_traverse do |e|
467
+ yield e
468
+ end
469
+ end
470
+ end
471
+ yield self
472
+ end
473
+
474
+ private
475
+
476
+ $fileutils_rb_have_lchmod = nil
477
+
478
+ def have_lchmod?
479
+ # This is not MT-safe, but it does not matter.
480
+ if $fileutils_rb_have_lchmod == nil
481
+ $fileutils_rb_have_lchmod = check_have_lchmod?
482
+ end
483
+ $fileutils_rb_have_lchmod
484
+ end
485
+
486
+ def check_have_lchmod?
487
+ return false unless File.respond_to?(:lchmod)
488
+ File.lchmod 0
489
+ return true
490
+ rescue NotImplementedError
491
+ return false
492
+ end
493
+
494
+ $fileutils_rb_have_lchown = nil
495
+
496
+ def have_lchown?
497
+ # This is not MT-safe, but it does not matter.
498
+ if $fileutils_rb_have_lchown == nil
499
+ $fileutils_rb_have_lchown = check_have_lchown?
500
+ end
501
+ $fileutils_rb_have_lchown
502
+ end
503
+
504
+ def check_have_lchown?
505
+ return false unless File.respond_to?(:lchown)
506
+ File.lchown nil, nil
507
+ return true
508
+ rescue NotImplementedError
509
+ return false
510
+ end
511
+
512
+ def join(dir, base)
513
+ return File.path(dir) if not base or base == '.'
514
+ return File.path(base) if not dir or dir == '.'
515
+ File.join(dir, base)
516
+ end
517
+
518
+ if File::ALT_SEPARATOR
519
+ DIRECTORY_TERM = "(?=[/#{Regexp.quote(File::ALT_SEPARATOR)}]|\\z)".freeze
520
+ else
521
+ DIRECTORY_TERM = "(?=/|\\z)".freeze
522
+ end
523
+ SYSCASE = File::FNM_SYSCASE.nonzero? ? "-i" : ""
524
+
525
+ def descendant_diretory?(descendant, ascendant)
526
+ /\A(?#{SYSCASE}:#{Regexp.quote(ascendant)})#{DIRECTORY_TERM}/ =~ File.dirname(descendant)
527
+ end
528
+ end # class Entry_
529
+
530
+ end
@@ -0,0 +1,54 @@
1
+ #
2
+ # XcodeInstaller::List
3
+ #
4
+
5
+ module XcodeInstaller
6
+ class List
7
+
8
+ def action(args, options)
9
+
10
+ show_all = args.include? 'all'
11
+ show_gui = args.include? 'gui'
12
+ show_cli = args.include? 'cli'
13
+ # Show GUI when no args given
14
+ show_gui = true if !show_all && !show_gui && !show_cli
15
+
16
+ mgr = XcodeInstaller::ReleaseManager.new
17
+
18
+ gui_versions = mgr.get_all('gui')
19
+ cli_versions = mgr.get_all('cli')
20
+
21
+ if show_all || show_gui
22
+ title = 'Xcode GUI'
23
+ table = Terminal::Table.new :title => title do |t|
24
+ t << ['Version', 'Download URL']
25
+ gui_versions.each do |release|
26
+ t << :separator
27
+
28
+ row = [release['version'], release['download_url']]
29
+ t << row
30
+ end
31
+ end
32
+ puts table
33
+ end
34
+
35
+ # Extra line between tables
36
+ puts if show_all
37
+
38
+ if show_all || show_cli
39
+ title = 'Xcode Command-Line'
40
+ table = Terminal::Table.new :title => title do |t|
41
+ t << ['Version', 'Download URL']
42
+ cli_versions.each do |release|
43
+ t << :separator
44
+
45
+ row = [release['version'], release['download_url']]
46
+ t << row
47
+ end
48
+ end
49
+ puts table
50
+ end
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,20 @@
1
+ #
2
+ # XcodeInstaller::Login
3
+ #
4
+
5
+ module XcodeInstaller
6
+ class Login
7
+
8
+ def action(args, options)
9
+ say_warning "You are already authenticated" if Security::InternetPassword.find(:server => XcodeInstaller::AppleDeveloperCenter::HOST)
10
+
11
+ user = ask "Username:"
12
+ pass = password "Password:"
13
+
14
+ Security::InternetPassword.add(XcodeInstaller::AppleDeveloperCenter::HOST, user, pass)
15
+
16
+ say_ok "Account credentials saved"
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ #
2
+ # XcodeInstaller::Logout
3
+ #
4
+
5
+ module XcodeInstaller
6
+ class Logout
7
+
8
+ def action(args, options)
9
+ say_error "You are not authenticated" and abort unless Security::InternetPassword.find(:server => XcodeInstaller::AppleDeveloperCenter::HOST)
10
+
11
+ Security::InternetPassword.delete(:server => XcodeInstaller::AppleDeveloperCenter::HOST)
12
+
13
+ say_ok "Account credentials removed"
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,37 @@
1
+ require 'yaml'
2
+
3
+ module XcodeInstaller
4
+ class ReleaseManager
5
+ attr_accessor :data
6
+
7
+ def initialize
8
+ super
9
+ @data = YAML::load_file(File.join(File.dirname(File.expand_path(__FILE__)), 'xcode-versions.yml'))
10
+ end
11
+
12
+ def get_all(interface_type)
13
+ interface_type ||= 'gui'
14
+ list = data[interface_type]
15
+ return list
16
+ end
17
+
18
+ def get_release(version, include_beta)
19
+ version ||= 'latest'
20
+ include_beta ||= false
21
+ interface_type ||= 'gui'
22
+
23
+ list = data[interface_type]
24
+ if version == 'latest' && include_beta
25
+ version = LATEST_DP
26
+ elsif version == 'latest'
27
+ version = LATEST_GA
28
+ end
29
+ list.each { |release|
30
+ if release['version'] == version
31
+ return release
32
+ end
33
+ }
34
+ end
35
+ end
36
+
37
+ end
@@ -1,45 +1,10 @@
1
- require 'yaml'
2
-
3
1
  module XcodeInstaller
4
2
  module XcodeVersions
5
3
 
6
4
  # General availability
7
- LATEST_GA = '5'
5
+ LATEST_GA = '5.0.1'
8
6
  # Developer preview
9
7
  LATEST_DP = '5-GM'
10
8
 
11
- class ReleaseManager
12
- attr_accessor :data
13
-
14
- def initialize
15
- super
16
- @data = YAML::load_file(File.join(File.dirname(File.expand_path(__FILE__)), 'xcode-versions.yml'))
17
- end
18
-
19
- def get_all(interface_type)
20
- interface_type ||= 'gui'
21
- list = data[interface_type]
22
- return list
23
- end
24
-
25
- def get_release(version, include_beta, interface_type)
26
- version ||= 'latest'
27
- include_beta ||= false
28
- interface_type ||= 'gui'
29
-
30
- list = data[interface_type]
31
- if version == 'latest' && include_beta
32
- version = LATEST_DP
33
- elsif version == 'latest'
34
- version = LATEST_GA
35
- end
36
- list.each { |release|
37
- if release['version'] == version
38
- return release
39
- end
40
- }
41
- end
42
- end
43
-
44
9
  end
45
10
  end
@@ -1,5 +1,12 @@
1
1
  ---
2
2
  gui:
3
+ - version: '5.0.1'
4
+ download_url: https://developer.apple.com/downloads/download.action?path=Developer_Tools/xcode_5.0.1/xcode_5.0.1.dmg
5
+ release_date: 2013-10-22
6
+ interface_type: gui
7
+ size: 2194212232
8
+ last_modified: 'Tue, 22 Oct 2013 23:43:12 GMT'
9
+ product_build_version: 5A2053
3
10
  - version: '5'
4
11
  download_url: https://developer.apple.com/downloads/download.action?path=Developer_Tools/xcode_5/xcode_5.dmg
5
12
  release_date: 2013-09-18
@@ -1,5 +1,5 @@
1
1
  module XcodeInstaller
2
- VERSION = '0.1.9'
2
+ VERSION = '0.2.0'
3
3
 
4
4
  class UnsuccessfulAuthenticationError < RuntimeError; end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xcode-installer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Chatelain
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-09-21 00:00:00.000000000 Z
11
+ date: 2013-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: commander
@@ -146,7 +146,13 @@ files:
146
146
  - ./lib/xcode-installer/commands/logout.rb
147
147
  - ./lib/xcode-installer/commands.rb
148
148
  - ./lib/xcode-installer/credentials.rb
149
+ - ./lib/xcode-installer/download.rb
149
150
  - ./lib/xcode-installer/helpers.rb
151
+ - ./lib/xcode-installer/install.rb
152
+ - ./lib/xcode-installer/list.rb
153
+ - ./lib/xcode-installer/login.rb
154
+ - ./lib/xcode-installer/logout.rb
155
+ - ./lib/xcode-installer/release-manager.rb
150
156
  - ./lib/xcode-installer/xcode-versions.rb
151
157
  - ./lib/xcode-installer/xcode-versions.yml
152
158
  - ./lib/xcode-installer.rb