u3d 1.1.5 → 1.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.
@@ -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