xcode-installer 0.1.9 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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