# frozen_string_literal: true # Sincroniza sitios a servidores remotos usando Rsync. El servidor # remoto tiene que tener rsync instalado. class DeployRsync < Deploy store :values, accessors: %i[hostname destination host_keys], coder: JSON DEPENDENCIES = %i[deploy_local deploy_zip].freeze def deploy(output: false) ssh? && rsync(output: output) end # El espacio remoto es el mismo que el local # # @return [Integer] def size deploy_local.size end # Devolver el destino o lanzar un error si no está configurado def destination values[:destination].tap do |d| raise(ArgumentError, 'destination no está configurado') if d.blank? end end # @return [String] def url "https://#{hostname}/" end private # Verificar la conexión SSH implementando Trust On First Use # # TODO: Medir el tiempo que tarda en iniciarse la conexión # # @return [Boolean] def ssh? return true if destination.start_with? 'rsync://' user, host = user_host ssh_available = false Net::SSH.start(host, user, verify_host_key: tofu, timeout: 5) do |ssh| if values[:host_keys].blank? # Guardar las llaves que se encontraron en la primera conexión values[:host_keys] = ssh.transport.host_keys.map do |host_key| "#{host_key.ssh_type} #{host_key.fingerprint}" end ssh_available = save else ssh_available = true end end ssh_available rescue Exception => e ExceptionNotifier.notify_exception(e, data: { site: site.id, hostname: host, user: user }) false end def env { 'HOME' => home_dir, 'PATH' => '/usr/bin', 'LANG' => ENV.fetch('LANG', nil) } end # Confiar en la primera llave que encontremos, fallar si cambian # # @return [Symbol] def tofu values[:host_keys].present? ? :always : :accept_new end # Devuelve el par user host # # @return [Array] def user_host @user_host ||= destination.split(':', 2).first.split('@', 2).tap do |d| next unless d.size == 1 d.insert(0, nil) end end # Sincroniza hacia el directorio remoto # # @return [Boolean] def rsync(output: false) run %(rsync -aviH --delete-after --timeout=5 #{Shellwords.escape source}/ #{Shellwords.escape destination}/), output: output end # El origen es el destino de la compilación # # @return [String] def source deploy_local.destination end def deploy_local @deploy_local ||= site.deploys.find_by(type: 'DeployLocal') end end