capistrano-jdk-installer 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,471 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "capistrano-jdk-installer/version"
4
+ require "fileutils"
5
+ require "json"
6
+ require "logger"
7
+ require "mechanize"
8
+ require "uri"
9
+
10
+ module Capistrano
11
+ module JDKInstaller
12
+ class JDKInstallerError < StandardError
13
+ end
14
+
15
+ class JDKInstallerParseError < JDKInstallerError
16
+ end
17
+
18
+ class JDKInstallerFile
19
+ MECHANIZE_USER_AGENT = "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)"
20
+ def initialize(release, data, options={})
21
+ @release = release
22
+ @data = data
23
+ @options = options.dup
24
+ @logger = @options[:logger]
25
+ end
26
+ attr_reader :release, :options
27
+
28
+ def logger
29
+ @logger ||= Logger.new(STDOUT)
30
+ end
31
+
32
+ def filepath
33
+ @filepath ||= @data["filepath"]
34
+ end
35
+ alias file filepath
36
+
37
+ def name
38
+ @name ||= @data["name"]
39
+ end
40
+ alias to_s name
41
+
42
+ def title
43
+ @title ||= @data["title"]
44
+ end
45
+
46
+ def platform
47
+ @platform ||= case name
48
+ when /jdk-\d+(?:u\d+)?-(\w+-\w+)\.\w+/ then $1.downcase
49
+ when /j2?dk-\d+_\d+_\d+(?:_\d+)?-(\w+-\w+)\.\w+/ then $1.downcase
50
+ else
51
+ raise(JDKInstallerParseError.new("Could not parse JDK file name: #{name}"))
52
+ end
53
+ end
54
+
55
+ def version
56
+ release.version
57
+ end
58
+
59
+ def major_version
60
+ release.major_version
61
+ end
62
+
63
+ def minor_version
64
+ release.minor_version
65
+ end
66
+
67
+ def update_number
68
+ release.update_number
69
+ end
70
+
71
+ def inner_version
72
+ release.inner_version
73
+ end
74
+
75
+ def licpath
76
+ release.licpath
77
+ end
78
+ alias license_path licpath
79
+
80
+ def lictitle
81
+ release.lictitle
82
+ end
83
+ alias license_title lictitle
84
+
85
+ def mechanize
86
+ @mechanize ||= ::Mechanize.new { |agent|
87
+ agent.user_agent = MECHANIZE_USER_AGENT
88
+ agent.cookie_jar.add!(::Mechanize::Cookie.new("gpw_e24", ".", :domain => "oracle.com", :path => "/", :secure => false, :for_domain => true))
89
+ agent.ssl_version = :TLSv1 # we have to declare TLS version explicitly to avoid problems on LP:965371
90
+ }
91
+ end
92
+
93
+ def download(filename, options={})
94
+ options = @options.merge(options)
95
+ username = options.fetch(:username, "")
96
+ password = options.fetch(:password, "")
97
+
98
+ logger.debug("Download JDK archive from #{filepath}....")
99
+ page = mechanize.get(filepath)
100
+ 1.upto(16) do # to avoid infinity loop...
101
+ if page.uri.host == "login.oracle.com" # login.oracle.com doesn't return proper Content-Type
102
+ if page.is_a?(::Mechanize::File)
103
+ page = ::Mechanize::Page.new(page.uri, page.response, page.body, page.code, mechanize)
104
+ end
105
+ form = page.form_with
106
+ form["ssousername"] = username
107
+ form["password"] = password
108
+ page = mechanize.submit(form)
109
+ else
110
+ page.save(filename)
111
+ logger.debug("Wrote #{page.body.size} bytes to #{filename}.")
112
+ break
113
+ end
114
+ end
115
+ end
116
+
117
+ def uri
118
+ @uri ||= URI.parse(filepath)
119
+ end
120
+
121
+ def basename
122
+ File.basename(uri.path)
123
+ end
124
+
125
+ def install_path(options={})
126
+ options = @options.merge(options)
127
+ case platform
128
+ when /macosx/i
129
+ v = if update_number
130
+ "jdk%s_%02d.jdk" % [inner_version, update_number]
131
+ else
132
+ "jdk%s.jdk" % [inner_version]
133
+ end
134
+ File.join("/Library", "Java", "JavaVirtualMachines", v, "Contents", "Home")
135
+ else
136
+ v = if update_number
137
+ "jdk%s_%02d" % [inner_version, update_number]
138
+ else
139
+ "jdk%s" % [inner_version]
140
+ end
141
+ if options.key?(:path)
142
+ File.join(options[:path], v)
143
+ else
144
+ v
145
+ end
146
+ end
147
+ end
148
+
149
+ def install_command(filename, destination, options={})
150
+ execute = []
151
+ execute << "mkdir -p #{File.dirname(destination).dump}"
152
+ case filename
153
+ when /\.(bin|sh)$/
154
+ execute << "( cd #{File.dirname(destination).dump} && yes | sh #{filename.dump} )"
155
+ when /\.dmg$/
156
+ if update_number
157
+ pkg = File.join("/Volumes", "JDK %d Update %02d" % [major_version, update_number],
158
+ "JDK %d Update %02d.pkg" % [major_version, update_number])
159
+ else
160
+ pkg = File.join("/Volumes", "JDK %d" % [major_version], "JDK %d" % [major_version])
161
+ end
162
+ execute << "open #{filenamedump}"
163
+ execute << "( while test \! -f #{pkg.dump}; do sleep 1; done )"
164
+ execute << "open #{pkg.dump}"
165
+ execute << "( while test \! -d #{destination.dump}; do sleep 1; done )"
166
+ when /\.(tar\.(gz|bz2)|tgz|tbz2)$/
167
+ execute << "tar xf #{filename.dump} -C #{File.dirname(destination).dump}"
168
+ when /\.zip$/
169
+ execute << "( cd #{File.dirname(destination).dump} && unzip #{filename.dump} )"
170
+ else
171
+ execute << "true"
172
+ end
173
+ execute.join(" && ")
174
+ end
175
+ end
176
+
177
+ class JDKInstallerRelease
178
+ include Enumerable
179
+ def initialize(version, data, options={})
180
+ @version = version
181
+ @data = data
182
+ @options = options.dup
183
+ @logger = @options[:logger]
184
+ end
185
+ attr_reader :version, :options
186
+
187
+ def logger
188
+ @logger ||= Logger.new(STDOUT)
189
+ end
190
+
191
+ def files
192
+ @files ||= @data["files"].map { |file|
193
+ JDKInstallerFile.new(self, file, @options)
194
+ }
195
+ end
196
+ alias to_a files
197
+
198
+ def each(&block)
199
+ self.to_a.each(&block)
200
+ end
201
+
202
+ def find_by_platform(platform)
203
+ platform = platform.to_s
204
+ self.find { |f| f.platform == platform }
205
+ end
206
+
207
+ def licpath
208
+ @licpath ||= @data["licpath"]
209
+ end
210
+ alias license_path licpath
211
+
212
+ def lictitle
213
+ @lictitle ||= @data["lictitle"]
214
+ end
215
+ alias license_title lictitle
216
+
217
+ def name
218
+ @name ||= @data["name"]
219
+ end
220
+ alias to_s name
221
+
222
+ def title
223
+ @title ||= @data["title"]
224
+ end
225
+
226
+ def version_info
227
+ @version_info ||= case name
228
+ when /j2sdk-(1\.4\.(\d+))(?:[_u]([0-9]+))?/ then [ $1, "1.4", $2, $3 ]
229
+ when /jdk-(1\.5\.(\d+))(?:[_u]([0-9]+))?/ then [ $1, "5", $2, $3 ]
230
+ when /jdk-(\d+)(?:u(\d+))?/ then [ "1.#{$1}.0", $1, 0, $2 ]
231
+ else
232
+ raise(JDKInstallerParseError.new("Could not parse JDK release name: #{name}"))
233
+ end
234
+ end
235
+
236
+ def inner_version
237
+ @inner_version ||= version_info[0] # e.g. "1.7.0"
238
+ end
239
+
240
+ def major_version
241
+ @major_version ||= version_info[1] # e.g. "7"
242
+ if @major_version != version.major_version
243
+ raise(JDKInstallerParseError.new("Major version mismatch (got=#{@major_version}, expected=#{version.major_version})"))
244
+ end
245
+ @major_version
246
+ end
247
+
248
+ def minor_version
249
+ @minor_version ||= version_info[2] # e.g. "0"
250
+ end
251
+
252
+ def update_number
253
+ @update_number ||= version_info[3] # e.g. "6"
254
+ end
255
+ end
256
+
257
+ class JDKInstallerVersion
258
+ include Enumerable
259
+ def initialize(data, options={})
260
+ @data = data
261
+ @options = options.dup
262
+ @logger = @options[:logger]
263
+ end
264
+ attr_reader :options
265
+
266
+ def logger
267
+ @logger ||= Logger.new(STDOUT)
268
+ end
269
+
270
+ def name
271
+ @name ||= @data["name"]
272
+ end
273
+ alias to_s name
274
+
275
+ def releases
276
+ @releases ||= @data["releases"].map { |release|
277
+ JDKInstallerRelease.new(self, release, @options)
278
+ }
279
+ end
280
+ alias to_a releases
281
+
282
+ def each(&block)
283
+ self.to_a.each(&block)
284
+ end
285
+
286
+ def find_by_update_number(update_number, options={})
287
+ update_number = update_number.to_s
288
+ self.find { |r| r.update_number == update_number }
289
+ end
290
+
291
+ def major_version
292
+ case name
293
+ when /JDK ((?:\d+\.)?\d+)/i then $1
294
+ else
295
+ raise(JDKInstallerParseError.new("Could not parse JDK version name: #{name}"))
296
+ end
297
+ end
298
+ end
299
+
300
+ class JDKInstallerVersions
301
+ include Enumerable
302
+ JSON_VERSION = 2
303
+ class << self
304
+ def parse(s, options={})
305
+ s = s.sub(/\A[^{]*/, "").sub(/[^}]*\z/, "").strip # remove leading & trailing JS code from response
306
+ i = new(JSON.load(s), options)
307
+ i.versions
308
+ end
309
+ end
310
+
311
+ def initialize(data, options={})
312
+ @data = data
313
+ @options = options.dup
314
+ @logger = @options[:logger]
315
+
316
+ if @data["version"] != JSON_VERSION
317
+ raise(JDKInstallerParseError.new("JSON version mismatch (got=#{@data["version"]}, expected=#{JSON_VERSION})"))
318
+ end
319
+ end
320
+ attr_reader :options
321
+
322
+ def logger
323
+ @logger ||= Logger.new(STDOUT)
324
+ end
325
+
326
+ def versions
327
+ @versions ||= @data["data"].map { |version|
328
+ JDKInstallerVersion.new(version, @options)
329
+ }
330
+ end
331
+ alias to_a versions
332
+
333
+ def each(&block)
334
+ self.to_a.each(&block)
335
+ end
336
+
337
+ def find_by_major_version(version, options={})
338
+ version = version.to_s
339
+ self.find { |v| v.major_version == version }
340
+ end
341
+ end
342
+
343
+ class JDKInstaller
344
+ JDK_INSTALLER_URI = "http://updates.jenkins-ci.org/updates/hudson.tools.JDKInstaller.json"
345
+ JDK_INSTALLER_TTL = 259200
346
+ class << self
347
+ def major_version(version_name)
348
+ case version_name
349
+ when /^1_4_\d+(?:_[0-9]+)?$/ then "1.4"
350
+ when /^1_5_\d+(?:_[0-9]+)?$/ then "5"
351
+ when /^(\d+)(?:u\d+)?$/ then $1
352
+ else
353
+ raise(JDKInstallerParseError.new("Could not parse JDK version name: #{version_name}"))
354
+ end
355
+ end
356
+
357
+ def platform_string(version_name, ostype, arch, options={})
358
+ major_version = major_version(version_name)
359
+ ostype = ostype.to_s.strip.downcase
360
+ arch = arch.to_s.strip.downcase
361
+ case major_version
362
+ when /^(?:1\.4|5)$/
363
+ arch = case arch
364
+ when /^(?:i[3-7]86)$/i then "i586"
365
+ when /^(?:amd64|x86_64)$/i then "amd64"
366
+ else
367
+ arch
368
+ end
369
+ else
370
+ ostype = case ostype
371
+ when /^Darwin$/i then "macosx"
372
+ else
373
+ ostype
374
+ end
375
+ arch = case arch
376
+ when /^(?:i[3-7]86)$/i then ostype == "macosx" ? "x64" : "i586"
377
+ when /^(?:amd64|x86_64)$/i then "x64"
378
+ else
379
+ arch
380
+ end
381
+ end
382
+ "#{ostype}-#{arch}"
383
+ end
384
+ end
385
+
386
+ def initialize(options={})
387
+ @uri = ( options.delete(:uri) || JDK_INSTALLER_URI )
388
+ @ttl = ( options.delete(:ttl) || JDK_INSTALLER_TTL )
389
+ @file = if options.key?(:file)
390
+ options.delete(:file)
391
+ else
392
+ Tempfile.new("jdk-installer")
393
+ end
394
+ @keep_stale = options.fetch(:keep_stale, false)
395
+ @options = options.dup
396
+ @logger = @options[:logger]
397
+ update
398
+ end
399
+
400
+ def logger
401
+ @logger ||= Logger.new(STDOUT)
402
+ end
403
+
404
+ def mechanize
405
+ @mechanize ||= ::Mechanize.new { |agent|
406
+ agent.ssl_version = :TLSv1 # we have to declare TLS version explicitly to avoid problems on LP:965371
407
+ }
408
+ end
409
+
410
+ def expired?(t=Time.now)
411
+ not(File.file?(@file)) or ( File.mtime(@file) + @ttl < t )
412
+ end
413
+
414
+ def update
415
+ if expired?
416
+ logger.info("The cache of JDKInstaller.json has been expired. (ttl=#{@ttl})")
417
+ update!
418
+ end
419
+ end
420
+
421
+ def update!
422
+ begin
423
+ page = mechanize.get(@uri)
424
+ write(page.body)
425
+ rescue ::Mechanize::Error => error
426
+ logger.info("Could not update JDKInstaller.json from #{@uri}. (#{error})")
427
+ if @keep_stale
428
+ logger.info("Try to use stale JDKInstaller.json at #{@file}.")
429
+ else
430
+ raise
431
+ end
432
+ end
433
+ end
434
+
435
+ def write(s)
436
+ if @file.respond_to?(:write)
437
+ @file.write(s)
438
+ else
439
+ FileUtils.mkdir_p(File.dirname(@file))
440
+ File.write(@file, s)
441
+ end
442
+ end
443
+
444
+ def read()
445
+ if @file.respond_to?(:read)
446
+ @file.read
447
+ else
448
+ File.read(@file)
449
+ end
450
+ end
451
+
452
+ def versions
453
+ @versions ||= JDKInstallerVersions.parse(read, @options)
454
+ end
455
+
456
+ def releases
457
+ @releases ||= versions.map { |version| version.releases }.flatten
458
+ end
459
+
460
+ def files
461
+ @files ||= releases.map { |release| release.files }.flatten
462
+ end
463
+
464
+ def [](regex)
465
+ files.find { |file| regex === file.to_s }
466
+ end
467
+ end
468
+ end
469
+ end
470
+
471
+ # vim:set ft=ruby sw=2 ts=2 :