# frozen_string_literal: true require 'open3' # Este modelo implementa los distintos tipos de alojamiento que provee # Sutty. # # Los datos se guardan en la tabla `deploys`. Para guardar los # atributos, cada modelo tiene que definir su propio `store # :attributes`. class Deploy < ApplicationRecord belongs_to :site belongs_to :rol, optional: true has_many :build_stats, dependent: :destroy DEPENDENCIES = [].freeze SOFT_DEPENDENCIES = [].freeze def deploy(**) raise NotImplementedError end def url raise NotImplementedError end # @return [Array] def urls [url].compact end def limit raise NotImplementedError end def size raise NotImplementedError end # Realizar tareas de limpieza. def cleanup!; end def time_start @start = Time.now end def time_stop @stop = Time.now end def time_spent_in_seconds (@stop - @start).round(3) end def home_dir site.path end # XXX: Ver DeployLocal#bundle def gems_dir @gems_dir ||= Rails.root.join('_storage', 'gems', site.name) end # Un entorno que solo tiene lo que necesitamos # # @return [Hash] def env # XXX: This doesn't support Windows paths :B paths = [File.dirname(`which bundle`), '/usr/local/bin', '/usr/bin', '/bin'] # Las variables de entorno extra no pueden superponerse al local. extra_env.merge({ 'HOME' => home_dir, 'PATH' => paths.join(':'), 'JEKYLL_ENV' => Rails.env, 'LANG' => ENV.fetch('LANG', nil) }) end # Corre un comando, lo registra en la base de datos y devuelve el # estado. # # @param [String] # @return [Boolean] def run(cmd, output: false) r = nil lines = [] time_start Open3.popen2e(env, cmd, unsetenv_others: true, chdir: site.path) do |_, o, t| # TODO: Enviar a un websocket para ver el proceso en vivo? Thread.new do o.each do |line| lines << line puts line if output end rescue IOError => e lines << e.message puts e.message if output end r = t.value end time_stop build_stats.create action: readable_cmd(cmd), log: lines.join, seconds: time_spent_in_seconds, bytes: size, status: r&.success? r&.success? end # Variables de entorno # # @return [Hash] def local_env @local_env ||= {} end # Devuelve opciones para jekyll build # # @return [String,nil] def flags_for_build(**args); end # Trae todas las dependencias # # @return [Array] def self.all_dependencies self::DEPENDENCIES | self::SOFT_DEPENDENCIES end private # Escribe el contenido en un archivo temporal y ejecuta el bloque # provisto con el archivo como parĂ¡metro # # @param :content [String] def with_tempfile(content) Tempfile.create(SecureRandom.hex) do |file| file.write content.to_s file.rewind file.close # @yieldparam :file [File] yield file end end # @param [String] # @return [String] def readable_cmd(cmd) cmd.split(' -', 2).first.tr(' ', '_') end def deploy_local @deploy_local ||= site.deploys.find_by(type: 'DeployLocal') end # Consigue todas las variables de entorno configuradas por otros # deploys. # # @return [Hash] def extra_env @extra_env ||= site.deployment_list.reduce({}) do |extra, deploy| extra.merge deploy.local_env end end end