u3d 1.1.5 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -27,6 +27,7 @@ require 'u3d/installation'
27
27
  require 'fileutils'
28
28
  require 'file-tail'
29
29
  require 'pathname'
30
+ require 'zip'
30
31
 
31
32
  module U3d
32
33
  DEFAULT_LINUX_INSTALL = '/opt/'.freeze
@@ -55,7 +56,7 @@ module U3d
55
56
  def self.install_modules(files, version, installation_path: nil)
56
57
  installer = Installer.create
57
58
  files.each do |name, file, info|
58
- UI.header "Installing #{info['title']} (#{name})"
59
+ UI.header "Installing #{info.name} (#{name})"
59
60
  UI.message 'Installing with ' + file
60
61
  installer.install(file, version, installation_path: installation_path, info: info)
61
62
  end
@@ -86,6 +87,77 @@ module U3d
86
87
  return [] if list.empty?
87
88
  list.sort { |a, b| UnityVersionComparator.new(a.version) <=> UnityVersionComparator.new(b.version) }
88
89
  end
90
+
91
+ protected
92
+
93
+ def install_po(file_path, version, info: nil)
94
+ unity = installed.find { |u| u.version == version }
95
+ root_path = package_destination(info, unity.root_path)
96
+
97
+ target_path = File.join(root_path, File.basename(file_path))
98
+ Utils.ensure_dir(File.dirname(target_path))
99
+
100
+ UI.verbose "Copying #{file_path} to #{target_path}"
101
+ FileUtils.cp(file_path, target_path)
102
+
103
+ UI.success "Successfully installed language file #{File.basename(file_path)}"
104
+ end
105
+
106
+ def install_zip(file_path, version, info: nil)
107
+ unity = installed.find { |u| u.version == version }
108
+ root_path = package_destination(info, unity.root_path)
109
+
110
+ UI.verbose("Unzipping #{file_path} to #{root_path}")
111
+
112
+ unless File.directory?(root_path)
113
+ Utils.get_write_access(File.dirname(root_path)) do
114
+ Utils.ensure_dir(root_path)
115
+ end
116
+ end
117
+
118
+ Zip::File.open(file_path) do |zip_file|
119
+ zip_file.each do |entry|
120
+ target_path = File.join(root_path, entry.name)
121
+ Utils.ensure_dir(File.dirname(target_path))
122
+ zip_file.extract(entry, target_path) unless File.exist?(target_path)
123
+ end
124
+ end
125
+
126
+ if info && info.rename_from && info.rename_to
127
+ rename_from = info.rename_from.gsub(/{UNITY_PATH}/, unity.root_path)
128
+ rename_to = info.rename_to.gsub(/{UNITY_PATH}/, unity.root_path)
129
+ Utils.ensure_dir(rename_to)
130
+ UI.verbose("Renaming from #{rename_from} to #{rename_to}")
131
+ if File.file? rename_from
132
+ FileUtils.mv(rename_from, rename_to)
133
+ else
134
+ Dir.glob(rename_from + '/*').each { |path| FileUtils.mv(path, rename_to) }
135
+ end
136
+ end
137
+
138
+ UI.success "Successfully unizpped #{File.basename(file_path)} at #{root_path}"
139
+ end
140
+
141
+ def package_destination(info, unity_root_path)
142
+ if info && info.destination
143
+ info.destination.gsub(/{UNITY_PATH}/, unity_root_path)
144
+ else
145
+ unity_root_path
146
+ end
147
+ end
148
+
149
+ def extra_installation_paths
150
+ return [] if ENV['U3D_EXTRA_PATHS'].nil?
151
+ ENV['U3D_EXTRA_PATHS'].strip.split(File::PATH_SEPARATOR)
152
+ end
153
+
154
+ def find_installations_with_path(default_root_path: '', postfix: [])
155
+ ([default_root_path] | extra_installation_paths).map do |path|
156
+ UI.verbose "Looking for installed Unity version under #{path}"
157
+ pattern = File.join([path] + postfix)
158
+ Dir.glob(pattern).map { |found_path| yield found_path }
159
+ end.flatten
160
+ end
89
161
  end
90
162
 
91
163
  # deprecated
@@ -113,17 +185,18 @@ module U3d
113
185
  paths.map { |path| MacInstallation.new(root_path: path) }
114
186
  end
115
187
 
116
- # rubocop:disable UnusedMethodArgument
117
- def install(file_path, version, installation_path: nil, info: {})
188
+ def install(file_path, version, installation_path: nil, info: nil)
118
189
  # rubocop:enable UnusedMethodArgument
119
190
  extension = File.extname(file_path)
120
- raise "Installation of #{extension} files is not supported on Mac" if extension != '.pkg'
191
+ raise "Installation of #{extension} files is not supported on Mac" unless %w[.zip .po .pkg].include? extension
121
192
  path = installation_path || DEFAULT_MAC_INSTALL
122
- install_pkg(
123
- file_path,
124
- version: version,
125
- target_path: path
126
- )
193
+ if extension == '.po'
194
+ install_po(file_path, version, info: info)
195
+ elsif extension == '.zip'
196
+ install_zip(file_path, version, info: info)
197
+ else
198
+ install_pkg(file_path, version: version, target_path: path)
199
+ end
127
200
  end
128
201
 
129
202
  def install_pkg(file_path, version: nil, target_path: nil)
@@ -169,9 +242,14 @@ module U3d
169
242
  private
170
243
 
171
244
  def list_installed_paths
172
- find = File.join(DEFAULT_MAC_INSTALL, 'Applications', 'Unity*', 'Unity.app')
173
- paths = Dir[find]
174
- paths = paths.map { |u| Pathname.new(u).parent.to_s }
245
+ paths = find_installations_with_path(
246
+ default_root_path: DEFAULT_MAC_INSTALL,
247
+ postfix: %w[
248
+ Applications
249
+ Unity*
250
+ Unity.app
251
+ ]
252
+ ) { |u| Pathname.new(u).parent.to_s }
175
253
  UI.verbose "Found list_installed_paths: #{paths}"
176
254
  paths
177
255
  end
@@ -213,12 +291,12 @@ module U3d
213
291
  paths.map { |path| LinuxInstallation.new(root_path: path) }
214
292
  end
215
293
 
216
- # rubocop:disable UnusedMethodArgument, PerceivedComplexity
217
- def install(file_path, version, installation_path: nil, info: {})
294
+ # rubocop:disable PerceivedComplexity
295
+ def install(file_path, version, installation_path: nil, info: nil)
218
296
  # rubocop:enable UnusedMethodArgument, PerceivedComplexity
219
297
  extension = File.extname(file_path)
220
298
 
221
- raise "Installation of #{extension} files is not supported on Linux" unless ['.sh', '.xz', '.pkg'].include? extension
299
+ raise "Installation of #{extension} files is not supported on Linux" unless ['.zip', '.po', '.sh', '.xz', '.pkg'].include? extension
222
300
  if extension == '.sh'
223
301
  path = installation_path || DEFAULT_LINUX_INSTALL
224
302
  install_sh(file_path, installation_path: path)
@@ -230,6 +308,10 @@ module U3d
230
308
  new_path = File.join(DEFAULT_LINUX_INSTALL, format(UNITY_DIR_LINUX, version: version))
231
309
  path = installation_path || new_path
232
310
  install_pkg(file_path, installation_path: path)
311
+ elsif extension == '.po'
312
+ install_po(file_path, version, info: info)
313
+ elsif extension == '.zip'
314
+ install_zip(file_path, version, info: info)
233
315
  end
234
316
 
235
317
  # Forces sanitation for installation of 'weird' versions eg 5.6.1xf1Linux
@@ -332,17 +414,25 @@ module U3d
332
414
  end
333
415
 
334
416
  def list_installed_paths
335
- find = File.join(DEFAULT_LINUX_INSTALL, 'unity-editor-*', 'Editor')
336
- paths = Dir[find]
337
- paths = paths.map { |u| Pathname.new(u).parent.to_s }
417
+ paths = find_installations_with_path(
418
+ default_root_path: DEFAULT_LINUX_INSTALL,
419
+ postfix: %w[
420
+ unity-editor-*
421
+ Editor
422
+ ]
423
+ ) { |u| Pathname.new(u).parent.to_s }
338
424
  UI.verbose "Found list_installed_paths: #{paths}"
339
425
  paths
340
426
  end
341
427
 
342
428
  def debian_installed_paths
343
- find = File.join(DEFAULT_LINUX_INSTALL, 'Unity', 'Editor')
344
- paths = Dir[find]
345
- paths = paths.map { |u| Pathname.new(u).parent.to_s }
429
+ paths = find_installations_with_path(
430
+ default_root_path: DEFAULT_LINUX_INSTALL,
431
+ postfix: %w[
432
+ Unity
433
+ Editor
434
+ ]
435
+ ) { |u| Pathname.new(u).parent.to_s }
346
436
  UI.verbose "Found debian_installed_paths: #{paths}"
347
437
  paths
348
438
  end
@@ -362,29 +452,37 @@ module U3d
362
452
  end
363
453
 
364
454
  def installed
365
- find = File.join(DEFAULT_WINDOWS_INSTALL, 'Unity*', 'Editor', 'Uninstall.exe')
366
- Dir[find].map { |path| WindowsInstallation.new(root_path: File.expand_path('../..', path)) }
455
+ find_installations_with_path(
456
+ default_root_path: DEFAULT_WINDOWS_INSTALL,
457
+ postfix: %w[
458
+ Unity*
459
+ Editor
460
+ Uninstall.exe
461
+ ]
462
+ ) { |path| WindowsInstallation.new(root_path: File.expand_path('../..', path)) }
367
463
  end
368
464
 
369
- def install(file_path, version, installation_path: nil, info: {})
465
+ def install(file_path, version, installation_path: nil, info: nil)
370
466
  extension = File.extname(file_path)
371
- raise "Installation of #{extension} files is not supported on Windows" unless %w[.exe .msi].include? extension
467
+ raise "Installation of #{extension} files is not supported on Windows" unless %w[.po .zip .exe .msi].include? extension
372
468
  path = installation_path || File.join(DEFAULT_WINDOWS_INSTALL, format(UNITY_DIR, version: version))
373
- install_exe(
374
- file_path,
375
- installation_path: path,
376
- info: info
377
- )
469
+ if extension == '.po'
470
+ install_po(file_path, version, info: info)
471
+ elsif extension == '.zip'
472
+ install_zip(file_path, version, info: info)
473
+ else
474
+ install_exe(file_path, installation_path: path, info: info)
475
+ end
378
476
  end
379
477
 
380
- def install_exe(file_path, installation_path: nil, info: {})
478
+ def install_exe(file_path, installation_path: nil, info: nil)
381
479
  installation_path ||= DEFAULT_WINDOWS_INSTALL
382
480
  final_path = U3dCore::Helper.windows_path(installation_path)
383
481
  Utils.ensure_dir(final_path)
384
482
  begin
385
483
  command = nil
386
- if info['cmd']
387
- command = info['cmd']
484
+ if info.command
485
+ command = info.command
388
486
  if /msiexec/ =~ command
389
487
  command.sub!(/{FILENAME}/, '"' + U3dCore::Helper.windows_path(file_path) + '"')
390
488
  else
@@ -400,7 +498,7 @@ module U3d
400
498
  rescue StandardError => e
401
499
  UI.error "Failed to install package at #{file_path}: #{e}"
402
500
  else
403
- UI.success "Successfully installed #{info['title']}"
501
+ UI.success "Successfully installed #{info.name}"
404
502
  end
405
503
  end
406
504
 
@@ -232,7 +232,7 @@ module U3d
232
232
  begin
233
233
  message = string % params.merge(@context)
234
234
  rescue KeyError => e
235
- UI.error("[U3D] Rule #{@active_rule} captures were incomplete: #{e.message}")
235
+ UI.error("[U3D] Rule '#{@active_rule}' captures were incomplete: #{e.message}")
236
236
  end
237
237
  message
238
238
  end
@@ -0,0 +1,145 @@
1
+ ## --- BEGIN LICENSE BLOCK ---
2
+ # Copyright (c) 2016-present WeWantToKnow AS
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ ## --- END LICENSE BLOCK ---
22
+
23
+ module U3d
24
+ class UnityModule
25
+ # Basic module attributes
26
+ attr_reader :id, :name, :description, :url
27
+ # Validation attributes
28
+ attr_reader :installed_size, :download_size, :checksum
29
+ # Internal attributes
30
+ attr_reader :os, :depends_on, :command, :destination, :rename_from, :rename_to
31
+
32
+ # rubocop:disable Metrics/ParameterLists
33
+ def initialize(
34
+ id:, name: nil, description: nil, url: nil,
35
+ installed_size: nil, download_size: nil, checksum: nil,
36
+ os: nil, depends_on: nil, command: nil, destination: nil, rename_from: nil, rename_to: nil
37
+ )
38
+ @id = id.downcase
39
+ @name = name
40
+ @description = description
41
+ @url = url
42
+ @installed_size = installed_size
43
+ @download_size = download_size
44
+ @checksum = checksum
45
+ @os = os
46
+ @depends_on = depends_on
47
+ @command = command
48
+ @destination = destination
49
+ @rename_from = rename_from
50
+ @rename_to = rename_to
51
+ end
52
+ # rubocop:enable Metrics/ParameterLists
53
+
54
+ def depends_on?(other)
55
+ return true if other.id == 'unity'
56
+ return false unless depends_on
57
+
58
+ other.id == depends_on || other.name == depends_on
59
+ end
60
+
61
+ class << self
62
+ def load_modules(version, cached_versions, os: U3dCore::Helper.operating_system, offline: false)
63
+ if version.is_a? Array
64
+ UI.verbose "Loading modules for several versions: #{version}"
65
+ load_versions_modules(version, cached_versions, os, offline)
66
+ else
67
+ UI.verbose "Loading modules for version #{version}"
68
+ load_version_modules(version, cached_versions, os, offline)
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ # Optimized version of load_version_modules that only makes one HTTP call
75
+ def load_versions_modules(versions, cached_versions, os, offline)
76
+ ini_modules = versions
77
+ .map { |version| [version, INIModulesParser.load_ini(version, cached_versions, os: os, offline: offline)] }
78
+ .map do |version, ini_data|
79
+ url_root = cached_versions[version]
80
+ modules = ini_data.map { |k, v| module_from_ini_data(k, v, url_root, os) }
81
+ [version, modules]
82
+ end.to_h
83
+
84
+ HubModulesParser.download_modules(os: os) unless offline
85
+ hub_modules = versions
86
+ .map { |version| [version, HubModulesParser.load_modules(version, os: os, offline: true)] }
87
+ .map do |version, json_data|
88
+ modules = json_data.map { |data| module_from_json_data(data, os) }
89
+ [version, modules]
90
+ end.to_h
91
+
92
+ return ini_modules.merge(hub_modules) do |_version, ini_version_modules, json_version_modules|
93
+ (ini_version_modules + json_version_modules).uniq(&:id)
94
+ end
95
+ end
96
+
97
+ def load_version_modules(version, cached_versions, os, offline)
98
+ ini_data = INIModulesParser.load_ini(version, cached_versions, os: os, offline: offline)
99
+ url_root = cached_versions[version]
100
+ ini_modules = ini_data.map { |k, v| module_from_ini_data(k, v, url_root, os) }
101
+
102
+ json_data = HubModulesParser.load_modules(version, os: os, offline: offline)
103
+ json_modules = json_data.map { |data| module_from_json_data(data, os) }
104
+
105
+ return (ini_modules + json_modules).uniq(&:id)
106
+ end
107
+
108
+ def module_from_ini_data(module_key, entries, url_root, os)
109
+ url = entries['url']
110
+ url = url_root + url unless /^http/ =~ url
111
+
112
+ UnityModule.new(
113
+ id: module_key,
114
+ name: entries['title'],
115
+ description: entries['description'],
116
+ url: url,
117
+ download_size: entries['size'],
118
+ installed_size: entries['installedsize'],
119
+ checksum: entries['md5'],
120
+ command: entries['cmd'],
121
+ depends_on: entries['sync'],
122
+ os: os
123
+ )
124
+ end
125
+
126
+ def module_from_json_data(entries, os)
127
+ UnityModule.new(
128
+ id: entries['id'],
129
+ name: entries['name'],
130
+ description: entries['description'],
131
+ url: entries['downloadUrl'],
132
+ download_size: entries['downloadSize'],
133
+ installed_size: entries['installedSize'],
134
+ checksum: entries['checksum'],
135
+ destination: entries['destination'],
136
+ rename_from: entries['renameFrom'],
137
+ rename_to: entries['renameTo'],
138
+ command: entries['cmd'],
139
+ depends_on: entries['sync'],
140
+ os: os
141
+ )
142
+ end
143
+ end
144
+ end
145
+ end
@@ -20,43 +20,47 @@
20
20
  # SOFTWARE.
21
21
  ## --- END LICENSE BLOCK ---
22
22
 
23
- require 'u3d/iniparser'
23
+ require 'u3d/ini_modules_parser'
24
24
 
25
25
  module U3d
26
26
  class UnityVersionDefinition
27
- attr_accessor :version, :os, :url, :ini
27
+ attr_accessor :version, :os, :url
28
+ attr_reader :packages
29
+
30
+ private
31
+
32
+ attr_writer :packages
33
+
34
+ public
28
35
 
29
36
  def initialize(version, os, cached_versions, offline: false)
30
37
  @version = version
31
38
  @os = os
32
39
  # Cache is assumed to be correct
33
40
  @url = cached_versions ? cached_versions[version] : nil
34
- begin
35
- @ini = INIparser.load_ini(version, cached_versions, os: os, offline: offline)
36
- rescue StandardError => e
37
- # FIXME: weird that we catch this here
38
- UI.error "Could not load INI file for version #{@version} on #{@os}: #{e}"
39
- @ini = nil
40
- end
41
+ @packages = UnityModule.load_modules(version, cached_versions, os: os, offline: offline)
41
42
  end
42
43
 
43
44
  def available_packages
44
- @ini.keys
45
+ @packages.map(&:id)
46
+ end
47
+
48
+ def available_package?(package)
49
+ available_packages.include? package.downcase
45
50
  end
46
51
 
47
- def available_package?(p)
48
- available_packages.include? p
52
+ def [](package)
53
+ return nil unless available_package? package
54
+ @packages.find { |pack| pack.id == package.downcase }
49
55
  end
50
56
 
51
- def [](key)
52
- return nil unless @ini
53
- @ini[key]
57
+ def ini
58
+ UI.deprecated 'UnityVersionDefinition no longer exposes the raw ini data'
59
+ return nil
54
60
  end
55
61
 
56
- def size_in_bytes(package)
57
- return nil unless @ini
58
- return -1 unless @ini[package] && @ini[package]['size']
59
- @os == :win ? @ini[package]['size'] * 1024 : @ini[package]['size']
62
+ def ini=(_value)
63
+ UI.deprecated 'UnityVersionDefinition no longer exposes the raw ini data'
60
64
  end
61
65
  end
62
66
  end