capistrano-jdk-installer 0.0.6 → 0.1.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.
@@ -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 :