5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-07-07 09:15:45 +00:00
panel/app/models/deploy.rb

161 lines
3.6 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
require 'open3'
# Este modelo implementa los distintos tipos de alojamiento que provee
# Sutty.
#
# TODO: Cambiar el nombre a algo que no sea industrial/militar.
class Deploy < ApplicationRecord
# Un sitio puede tener muchas formas de publicarse.
belongs_to :site
# Puede tener muchos access logs a través del hostname
has_many :access_logs, primary_key: 'hostname', foreign_key: 'host'
# Siempre generar el hostname
after_initialize :default_hostname!
# Eliminar los archivos generados por el deploy.
before_destroy :remove_destination!
# Registro de las tareas ejecutadas
has_many :build_stats, dependent: :destroy
# Siempre tienen que pertenecer a un sitio
validates :site, presence: true
# El hostname tiene que ser único en toda la plataforma
validates :hostname, uniqueness: true
# Cada deploy puede implementar su propia validación
validates :hostname, hostname: true, unless: :implements_hostname_validation?
# Retrocompatibilidad: Encuentra el site_name a partir del hostname.
#
# @return [String,Nil]
def self.site_name_from_hostname(hostname)
where(hostname: hostname).includes(:site).pluck(:name).first
end
# Genera el hostname
#
# @return [String]
def default_hostname
raise NotImplementedError
end
# Devolver la URL
#
# @return [String]
def url
"https://#{hostname}"
end
# Ejecutar la tarea
#
# @return [Boolean]
def deploy
raise NotImplementedError
end
# El espacio ocupado por este deploy.
#
# @return [Integer]
def size
raise NotImplementedError
end
# Empezar a contar el tiempo
#
# @return [Time]
def time_start
@start = Time.now
end
# Detener el contador
#
# @return [Time]
def time_stop
@stop = Time.now
end
# Obtener la demora de la tarea
#
# @return [Float]
def time_spent_in_seconds
(@stop - @start).round(3)
end
# El directorio donde se almacenan las gemas.
#
# TODO: En un momento podíamos tenerlas todas compartidas y ahorrar
# espacio, pero bundler empezó a mezclar cosas.
#
# @return [String]
2019-09-19 13:20:27 +00:00
def gems_dir
2021-04-10 16:17:24 +00:00
@gems_dir ||= Rails.root.join('_storage', 'gems', site.name)
2019-09-19 13:20:27 +00:00
end
# Corre un comando, lo registra en la base de datos y devuelve el
# estado.
#
# @param [String]
# @return [Boolean]
def run(cmd)
r = nil
lines = []
time_start
Dir.chdir(site.path) do
Open3.popen2e(env, cmd, unsetenv_others: true) do |_, o, t|
r = t.value
# XXX: Tenemos que leer línea por línea porque en salidas largas
# se cuelga la IO
# TODO: Enviar a un websocket para ver el proceso en vivo?
o.each do |line|
lines << line
end
end
end
time_stop
build_stats.create action: readable_cmd(cmd),
log: lines.join,
seconds: time_spent_in_seconds,
bytes: size,
status: r&.success?
2020-10-04 00:32:32 +00:00
r&.success?
end
private
# Genera el hostname pero permitir la inicialización del valor. Luego
# validamos que sea el formato correcto.
#
# @return [Boolean]
def default_hostname!
self.hostname ||= default_hostname
true
end
# Elimina los archivos generados por el deploy
#
# @return [Boolean]
def remove_destination!
raise NotImplementedError
end
# Convierte el comando en una versión resumida.
#
# @param [String]
# @return [String]
def readable_cmd(cmd)
cmd.split(' -', 2).first.tr(' ', '_')
end
# Cada deploy puede decidir su propia validación
#
# @return [Boolean]
def implements_hostname_validation?
false
end
end