lib/u3d/installer.rb in u3d-1.0.20 vs lib/u3d/installer.rb in u3d-1.0.21

- old
+ new

@@ -19,10 +19,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. ## --- END LICENSE BLOCK --- require 'u3d/utils' +require 'u3d_core/admin_tools' require 'u3d_core/core_ext/string' require 'u3d/installation' require 'fileutils' require 'file-tail' require 'pathname' @@ -30,36 +31,27 @@ module U3d DEFAULT_LINUX_INSTALL = '/opt/'.freeze DEFAULT_MAC_INSTALL = '/'.freeze DEFAULT_WINDOWS_INSTALL = 'C:/Program Files/'.freeze UNITY_DIR = "Unity_%<version>s".freeze + UNITY_DIR_LONG = "Unity_%<version>s_%<build_number>s".freeze UNITY_DIR_LINUX = "unity-editor-%<version>s".freeze + UNITY_DIR_LINUX_LONG = "unity-editor-%<version>s_%<build_number>s".freeze class Installer def self.create - installer = if Helper.mac? - MacInstaller.new - elsif Helper.linux? - LinuxInstaller.new - else - WindowsInstaller.new - end - sanitize_installs(installer) - installer + if Helper.mac? + MacInstaller.new + elsif Helper.linux? + LinuxInstaller.new + else + WindowsInstaller.new + end end def self.sanitize_installs(installer) - return unless UI.interactive? || Helper.test? - unclean = [] - installer.installed.each { |unity| unclean << unity unless unity.clean_install? } - return if unclean.empty? - UI.important("u3d can optionally standardize the existing Unity installation names and locations.") - UI.important("Check the documentation for more information:") - UI.important("** https://github.com/DragonBox/u3d/blob/master/README.md#default-installation-paths **") - unclean.each { |unity| installer.sanitize_install(unity, dry_run: true) } - return unless UI.confirm("#{unclean.count} Unity installation(s) will be moved. Proceed??") - unclean.each { |unity| installer.sanitize_install(unity) } + installer.sanitize_installs end def self.install_modules(files, version, installation_path: nil) installer = Installer.create files.each do |name, file, info| @@ -73,38 +65,55 @@ installer = Installer.create installer.uninstall(unity: unity) end end + class BaseInstaller + def sanitize_installs + return unless UI.interactive? || Helper.test? + unclean = [] + installed.each { |unity| unclean << unity unless unity.clean_install? } + return if unclean.empty? + UI.important("u3d can optionally standardize the existing Unity installation names and locations.") + UI.important("Check the documentation for more information:") + UI.important("** https://github.com/DragonBox/u3d/blob/master/README.md#default-installation-paths **") + unclean.each { |unity| sanitize_install(unity, dry_run: true) } + return unless UI.confirm("#{unclean.count} Unity installation(s) will be moved. Proceed??") + unclean.each { |unity| sanitize_install(unity) } + end + + def installed_sorted_by_versions + list = installed + return [] if list.empty? + # version -> installations + arraym = list.map { |a| [a.version, a] } + map = Hash[*arraym.flatten] + # sorted versions + vcomparators = map.keys.map { |k| UnityVersionComparator.new(k) } + sorted_keys = vcomparators.sort.map { |v| v.version.to_s } + sorted_keys.map { |k| map[k] } + end + end + + # deprecated class CommonInstaller def self.sanitize_install(source_path, new_path, command, dry_run: false) - if source_path == new_path - UI.important "sanitize_install does nothing if the path won't change (#{source_path})" - return - end - - if dry_run - UI.message "'#{source_path}' would move to '#{new_path}'" - else - UI.important "Moving '#{source_path}' to '#{new_path}'..." - U3dCore::CommandExecutor.execute(command: command, admin: true) - UI.success "Successfully moved '#{source_path}' to '#{new_path}'" - end - rescue StandardError => e - UI.error "Unable to move '#{source_path}' to '#{new_path}': #{e}" + UI.deprecated("Use U3dCore::AdminTools.move_files") + U3dCore::AdminTools.move_file(source_path, new_path, command, dry_run: dry_run) end end - class MacInstaller - def sanitize_install(unity, dry_run: false) + class MacInstaller < BaseInstaller + def sanitize_install(unity, long: false, dry_run: false) source_path = unity.root_path parent = File.expand_path('..', source_path) - new_path = File.join(parent, format(UNITY_DIR, version: unity.version)) + dir_name = format(long ? UNITY_DIR_LONG : UNITY_DIR, + version: unity.version, build_number: unity.build_number) + new_path = File.join(parent, dir_name) - command = "mv #{source_path.shellescape} #{new_path.shellescape}" - - CommonInstaller.sanitize_install(source_path, new_path, command, dry_run: dry_run) + moved = U3dCore::AdminTools.move_os_file(:mac, source_path, new_path, dry_run: dry_run) + unity.root_path = new_path if moved && !dry_run end def installed paths = (list_installed_paths + spotlight_installed_paths).uniq paths.map { |path| MacInstallation.new(root_path: path) } @@ -190,39 +199,45 @@ UI.verbose "Found spotlight_installed_paths: #{paths}" paths end end - class LinuxInstaller - def sanitize_install(unity, dry_run: false) + # rubocop:disable ClassLength + class LinuxInstaller < BaseInstaller + def sanitize_install(unity, long: false, dry_run: false) source_path = File.expand_path(unity.root_path) parent = File.expand_path('..', source_path) - new_path = File.join(parent, format(UNITY_DIR_LINUX, version: unity.version)) + dir_name = format(long ? UNITY_DIR_LINUX_LONG : UNITY_DIR_LINUX, + version: unity.version, build_number: unity.build_number) + new_path = File.join(parent, dir_name) - command = "mv #{source_path.shellescape} #{new_path.shellescape}" - - CommonInstaller.sanitize_install(source_path, new_path, command, dry_run: dry_run) + moved = U3dCore::AdminTools.move_os_file(:linux, source_path, new_path, dry_run: dry_run) + unity.root_path = new_path if moved && !dry_run end def installed paths = (list_installed_paths + debian_installed_paths).uniq paths.map { |path| LinuxInstallation.new(root_path: path) } end - # rubocop:disable UnusedMethodArgument + # rubocop:disable UnusedMethodArgument, PerceivedComplexity def install(file_path, version, installation_path: nil, info: {}) - # rubocop:enable UnusedMethodArgument + # rubocop:enable UnusedMethodArgument, PerceivedComplexity extension = File.extname(file_path) - raise "Installation of #{extension} files is not supported on Linux" unless ['.sh', '.xz'].include? extension + raise "Installation of #{extension} files is not supported on Linux" unless ['.sh', '.xz', '.pkg'].include? extension if extension == '.sh' path = installation_path || DEFAULT_LINUX_INSTALL install_sh(file_path, installation_path: path) elsif extension == '.xz' new_path = File.join(DEFAULT_LINUX_INSTALL, format(UNITY_DIR_LINUX, version: version)) path = installation_path || new_path install_xz(file_path, installation_path: path) + elsif extension == '.pkg' + new_path = File.join(DEFAULT_LINUX_INSTALL, format(UNITY_DIR_LINUX, version: version)) + path = installation_path || new_path + install_pkg(file_path, installation_path: path) end # Forces sanitation for installation of 'weird' versions eg 5.6.1xf1Linux unity = installed.select { |u| u.version == version }.first if unity @@ -260,10 +275,37 @@ UI.error "Failed to install xz file #{file} at #{installation_path}: #{e}" else UI.success 'Installation successful' end + def install_pkg(file, installation_path: nil) + raise 'Missing installation_path' unless installation_path + raise 'Only able to install pkg on top of existing Unity installs' unless File.exist? installation_path + raise 'Missing 7z' if `which 7z`.empty? + + Dir.mktmpdir do |tmp_dir| + UI.verbose "Working in tmp dir #{tmp_dir}" + + command = "7z -aos -o#{tmp_dir.shellescape} e #{file.shellescape}" + U3dCore::CommandExecutor.execute(command: command) + + target_location = pkg_install_path(installation_path, "#{tmp_dir}/PackageInfo") + + # raise "Path for #{target_location} already exists" if path File.exist? target_location + + command = "cd #{target_location.shellescape}; gzip -dc #{tmp_dir}/Payload | cpio -i '*' -" + command = "mkdir -p #{target_location.shellescape}; #{command}" # unless File.directory? installation_path + + U3dCore::CommandExecutor.execute(command: command, admin: true) + end + rescue StandardError => e + UI.verbose(e.backtrace.join("\n")) + UI.error "Failed to install pkg file #{file} at #{installation_path}: #{e}" + else + UI.success 'Installation successful' + end + def uninstall(unity: nil) UI.verbose("Uninstalling Unity at '#{unity.root_path}'...") command = "rm -r #{unity.root_path}" U3dCore::CommandExecutor.execute(command: command, admin: true) rescue StandardError => e @@ -272,10 +314,31 @@ UI.success "Successfully uninstalled '#{unity.root_path}'" end private + def pkg_install_path(unity_root_path, pinfo_path) + raise "PackageInfo not found under #{pinfo_path}" unless File.exist? pinfo_path + pinfo = File.read(pinfo_path) + require 'rexml/document' + d = REXML::Document.new(pinfo) + identifier = d.root.attributes['identifier'] + + case identifier + when 'com.unity3d.Documentation' + "#{unity_root_path}/Editor/Data/" + when 'com.unity3d.StandardAssets' + "#{unity_root_path}/Editor/Standard Assets/" + when 'com.unity3d.ExampleProject' + unity_root_path + else + install_location = d.root.attributes['install-location'] + raise "Not sure how to install this module with identifier #{identifier} install-location: #{install_location}" unless install_location.start_with? '/Applications/Unity/' + install_location.gsub(%(\/Applications\/Unity), "#{unity_root_path}/Editor/Data") + end + end + def list_installed_paths find = File.join(DEFAULT_LINUX_INSTALL, 'unity-editor-*', 'Editor') paths = Dir[find] paths = paths.map { |u| Pathname.new(u).parent.to_s } UI.verbose "Found list_installed_paths: #{paths}" @@ -288,23 +351,22 @@ paths = paths.map { |u| Pathname.new(u).parent.to_s } UI.verbose "Found debian_installed_paths: #{paths}" paths end end + # rubocop:enable ClassLength - class WindowsInstaller - def sanitize_install(unity, dry_run: false) + class WindowsInstaller < BaseInstaller + def sanitize_install(unity, long: false, dry_run: false) source_path = File.expand_path(unity.root_path) parent = File.expand_path('..', source_path) - new_path = File.join(parent, format(UNITY_DIR, version: unity.version)) + dir_name = format(long ? UNITY_DIR_LONG : UNITY_DIR, + version: unity.version, build_number: unity.build_number) + new_path = File.join(parent, dir_name) - source_path.tr!('/', '\\') - new_path.tr!('/', '\\') - - command = "move #{source_path.argescape} #{new_path.argescape}" - - CommonInstaller.sanitize_install(source_path, new_path, command, dry_run: dry_run) + moved = U3dCore::AdminTools.move_os_file(:win, source_path, new_path, dry_run: dry_run) + unity.root_path = new_path if moved && !dry_run end def installed find = File.join(DEFAULT_WINDOWS_INSTALL, 'Unity*', 'Editor', 'Uninstall.exe') Dir[find].map { |path| WindowsInstallation.new(root_path: File.expand_path('../..', path)) } @@ -321,26 +383,26 @@ ) end def install_exe(file_path, installation_path: nil, info: {}) installation_path ||= DEFAULT_WINDOWS_INSTALL - final_path = Utils.windows_path(installation_path) + final_path = U3dCore::Helper.windows_path(installation_path) Utils.ensure_dir(final_path) begin command = nil if info['cmd'] command = info['cmd'] if /msiexec/ =~ command - command.sub!(/{FILENAME}/, '"' + Utils.windows_path(file_path) + '"') + command.sub!(/{FILENAME}/, '"' + U3dCore::Helper.windows_path(file_path) + '"') else - command.sub!(/{FILENAME}/, file_path) + command.sub!(/{FILENAME}/, file_path.argescape) end command.sub!(/{INSTDIR}/, final_path) command.sub!(/{DOCDIR}/, final_path) command.sub!(/{MODULEDIR}/, final_path) command.sub!(%r{\/D=}, '/S /D=') unless %r{\/S} =~ command end - command ||= file_path.to_s + command ||= file_path.argescape U3dCore::CommandExecutor.execute(command: command, admin: true) rescue StandardError => e UI.error "Failed to install package at #{file_path}: #{e}" else UI.success "Successfully installed #{info['title']}"