mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-23 05:06:22 +00:00
Merge branch 'informative-deploys' into issue-10464
This commit is contained in:
commit
96ea3715fd
14 changed files with 175 additions and 45 deletions
|
@ -3,6 +3,7 @@
|
|||
# Realiza el deploy de un sitio
|
||||
class DeployJob < ApplicationJob
|
||||
class DeployException < StandardError; end
|
||||
class DeployTimedOutException < DeployException; end
|
||||
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def perform(site, notify = true, time = Time.now)
|
||||
|
@ -16,8 +17,8 @@ class DeployJob < ApplicationJob
|
|||
# hora original para poder ir haciendo timeouts.
|
||||
if @site.building?
|
||||
if 10.minutes.ago >= time
|
||||
@site.update status: 'waiting'
|
||||
raise DeployException,
|
||||
notify = false
|
||||
raise DeployTimedOutException,
|
||||
"#{@site.name} la tarea estuvo más de 10 minutos esperando, volviendo al estado original"
|
||||
end
|
||||
|
||||
|
@ -27,21 +28,28 @@ class DeployJob < ApplicationJob
|
|||
|
||||
@site.update status: 'building'
|
||||
# Asegurarse que DeployLocal sea el primero!
|
||||
@deployed = { deploy_local: deploy_locally }
|
||||
@deployed = {
|
||||
deploy_local: {
|
||||
status: deploy_locally,
|
||||
seconds: deploy_local.build_stats.last.seconds,
|
||||
size: deploy_local.size,
|
||||
urls: [deploy_local.url]
|
||||
}
|
||||
}
|
||||
|
||||
# No es opcional
|
||||
unless @deployed[:deploy_local]
|
||||
@site.update status: 'waiting'
|
||||
notify_usuaries if notify
|
||||
|
||||
unless @deployed[:deploy_local][:status]
|
||||
# Hacer fallar la tarea
|
||||
raise DeployException, deploy_local.build_stats.last.log
|
||||
raise DeployException, "#{@site.name}: Falló la compilación"
|
||||
end
|
||||
|
||||
deploy_others
|
||||
|
||||
# Volver a la espera
|
||||
@site.update status: 'waiting'
|
||||
rescue DeployTimedOutException => e
|
||||
notify_exception e
|
||||
rescue DeployException => e
|
||||
notify_exception e, deploy_local
|
||||
ensure
|
||||
@site&.update status: 'waiting'
|
||||
|
||||
notify_usuaries if notify
|
||||
end
|
||||
|
@ -50,6 +58,18 @@ class DeployJob < ApplicationJob
|
|||
|
||||
private
|
||||
|
||||
# @param :exception [StandardError]
|
||||
# @param :deploy [Deploy]
|
||||
def notify_exception(exception, deploy = nil)
|
||||
data = {
|
||||
site: @site.id,
|
||||
deploy: deploy&.type,
|
||||
log: deploy&.build_stats&.last&.log
|
||||
}
|
||||
|
||||
ExceptionNotifier.notify_exception(exception, data: data)
|
||||
end
|
||||
|
||||
def deploy_local
|
||||
@deploy_local ||= @site.deploys.find_by(type: 'DeployLocal')
|
||||
end
|
||||
|
@ -60,7 +80,22 @@ class DeployJob < ApplicationJob
|
|||
|
||||
def deploy_others
|
||||
@site.deploys.where.not(type: 'DeployLocal').find_each do |d|
|
||||
@deployed[d.type.underscore.to_sym] = d.deploy
|
||||
begin
|
||||
status = d.deploy
|
||||
seconds = d.build_stats.last.try(:seconds)
|
||||
rescue StandardError => e
|
||||
status = false
|
||||
seconds = 0
|
||||
|
||||
notify_exception e, d
|
||||
end
|
||||
|
||||
@deployed[d.type.underscore.to_sym] = {
|
||||
status: status,
|
||||
seconds: seconds || 0,
|
||||
size: d.size,
|
||||
urls: d.respond_to?(:urls) ? d.urls : [d.url].compact
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
# Notifica excepciones a una instancia de Gitlab, como incidencias
|
||||
# nuevas o como comentarios a las incidencias pre-existentes.
|
||||
class GitlabNotifierJob < ApplicationJob
|
||||
class GitlabNotifierError < StandardError; end
|
||||
|
||||
include ExceptionNotifier::BacktraceCleaner
|
||||
|
||||
# Variables que vamos a acceder luego
|
||||
|
@ -18,22 +20,28 @@ class GitlabNotifierJob < ApplicationJob
|
|||
@issue_data = { count: 1 }
|
||||
# Necesitamos saber si el issue ya existía
|
||||
@cached = false
|
||||
@issue = {}
|
||||
|
||||
# Traemos los datos desde la caché si existen, sino generamos un
|
||||
# issue nuevo e inicializamos la caché
|
||||
@issue_data = Rails.cache.fetch(cache_key) do
|
||||
issue = client.new_issue confidential: true, title: title, description: description, issue_type: 'incident'
|
||||
@issue = client.new_issue confidential: true, title: title, description: description, issue_type: 'incident'
|
||||
@cached = true
|
||||
|
||||
{
|
||||
count: 1,
|
||||
issue: issue['iid'],
|
||||
issue: @issue['iid'],
|
||||
user_agents: [user_agent].compact,
|
||||
params: [request&.filtered_parameters].compact,
|
||||
urls: [url].compact
|
||||
}
|
||||
end
|
||||
|
||||
unless @issue['iid']
|
||||
Rails.cache.delete(cache_key)
|
||||
raise GitlabNotifierError, @issue.dig('message', 'title')&.join(', ')
|
||||
end
|
||||
|
||||
# No seguimos actualizando si acabamos de generar el issue
|
||||
return if cached
|
||||
|
||||
|
@ -104,6 +112,7 @@ class GitlabNotifierJob < ApplicationJob
|
|||
# @return [String]
|
||||
def description
|
||||
@description ||= ''.dup.tap do |d|
|
||||
d << log_section
|
||||
d << request_section
|
||||
d << javascript_section
|
||||
d << javascript_footer
|
||||
|
@ -151,6 +160,19 @@ class GitlabNotifierJob < ApplicationJob
|
|||
@client ||= GitlabApiClient.new
|
||||
end
|
||||
|
||||
# @return [String]
|
||||
def log_section
|
||||
return '' unless options[:log]
|
||||
|
||||
<<~LOG
|
||||
# Log
|
||||
|
||||
```
|
||||
#{options[:log]}
|
||||
```
|
||||
LOG
|
||||
end
|
||||
|
||||
# Muestra información de la petición
|
||||
#
|
||||
# @return [String]
|
||||
|
|
|
@ -8,21 +8,65 @@
|
|||
# TODO: Agregar firma GPG y header Autocrypt
|
||||
# TODO: Cifrar con GPG si le usuarie nos dio su llave
|
||||
class DeployMailer < ApplicationMailer
|
||||
include ActionView::Helpers::NumberHelper
|
||||
include ActionView::Helpers::DateHelper
|
||||
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
def deployed(which_ones)
|
||||
@usuarie = Usuarie.find(params[:usuarie])
|
||||
@site = @usuarie.sites.find(params[:site])
|
||||
@deploys = which_ones
|
||||
@deploy_local = @site.deploys.find_by(type: 'DeployLocal')
|
||||
def deployed(deploys)
|
||||
usuarie = Usuarie.find(params[:usuarie])
|
||||
site = usuarie.sites.find(params[:site])
|
||||
hostname = site.hostname
|
||||
|
||||
# Informamos a cada quien en su idioma y damos una dirección de
|
||||
# respuesta porque a veces les usuaries nos escriben
|
||||
I18n.with_locale(@usuarie.lang) do
|
||||
mail(to: @usuarie.email,
|
||||
reply_to: "sutty@#{Site.domain}",
|
||||
subject: I18n.t('deploy_mailer.deployed.subject',
|
||||
site: @site.name))
|
||||
I18n.with_locale(usuarie.lang) do
|
||||
subject = t('.subject', site: site.name)
|
||||
|
||||
@hi = t('.hi')
|
||||
@explanation = t('.explanation', fqdn: hostname)
|
||||
@help = t('.help')
|
||||
|
||||
@headers = %w[type status url seconds size].map do |header|
|
||||
t(".th.#{header}")
|
||||
end
|
||||
|
||||
@table = deploys.each_pair.map do |deploy, value|
|
||||
{
|
||||
title: t(".#{deploy}.title"),
|
||||
status: t(".#{deploy}.#{value[:status] ? 'success' : 'error'}"),
|
||||
urls: value[:urls],
|
||||
seconds: {
|
||||
human: distance_of_time_in_words(value[:seconds].seconds),
|
||||
machine: "PT#{value[:seconds]}S"
|
||||
},
|
||||
size: number_to_human_size(value[:size], precision: 2)
|
||||
}
|
||||
end
|
||||
|
||||
@terminal_table = Terminal::Table.new do |t|
|
||||
t << @headers
|
||||
t.add_separator
|
||||
@table.each do |row|
|
||||
row[:urls].each do |url|
|
||||
t << (row.map do |k, v|
|
||||
case k
|
||||
when :seconds then v[:human]
|
||||
when :urls then url
|
||||
else v
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
mail(to: usuarie.email, reply_to: "sutty@#{Site.domain}", subject: subject)
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
|
||||
private
|
||||
|
||||
def t(key, **args)
|
||||
I18n.t("deploy_mailer.deployed#{key}", **args)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,6 +18,10 @@ class Deploy < ApplicationRecord
|
|||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def url
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def limit
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
|
|
@ -20,6 +20,10 @@ class DeployAlternativeDomain < Deploy
|
|||
end
|
||||
|
||||
def destination
|
||||
File.join(Rails.root, '_deploy', hostname.gsub(/\.\z/, ''))
|
||||
@destination ||= File.join(Rails.root, '_deploy', hostname.gsub(/\.\z/, ''))
|
||||
end
|
||||
|
||||
def url
|
||||
"https://#{File.basename destination}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,6 +13,6 @@ class DeployHiddenService < DeployWww
|
|||
end
|
||||
|
||||
def url
|
||||
'http://' + fqdn
|
||||
"http://#{fqdn}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,6 +25,10 @@ class DeployLocal < Deploy
|
|||
1
|
||||
end
|
||||
|
||||
def url
|
||||
site.url
|
||||
end
|
||||
|
||||
# Obtener el tamaño de todos los archivos y directorios (los
|
||||
# directorios son archivos :)
|
||||
def size
|
||||
|
|
|
@ -16,6 +16,10 @@ class DeployPrivate < DeployLocal
|
|||
File.join(Rails.root, '_private', site.name)
|
||||
end
|
||||
|
||||
def url
|
||||
"#{ENV['PANEL_URL']}/sites/private/#{site.name}"
|
||||
end
|
||||
|
||||
# No usar recursos en compresión y habilitar los datos privados
|
||||
def env
|
||||
@env ||= super.merge({
|
||||
|
|
|
@ -29,6 +29,10 @@ class DeployWww < Deploy
|
|||
"www.#{site.hostname}"
|
||||
end
|
||||
|
||||
def url
|
||||
"https://www.#{site.hostname}/"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def remove_destination!
|
||||
|
|
|
@ -51,6 +51,10 @@ class DeployZip < Deploy
|
|||
"#{site.hostname}.zip"
|
||||
end
|
||||
|
||||
def url
|
||||
"#{site.url}#{file}"
|
||||
end
|
||||
|
||||
def path
|
||||
File.join(destination, file)
|
||||
end
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
%h1= t('.hi')
|
||||
%h1= @hi
|
||||
|
||||
= sanitize_markdown t('.explanation', fqdn: @deploy_local.site.hostname),
|
||||
tags: %w[p a strong em]
|
||||
= sanitize_markdown @explanation, tags: %w[p a strong em]
|
||||
|
||||
%table
|
||||
%thead
|
||||
%tr
|
||||
%th= t('.th.type')
|
||||
%th= t('.th.status')
|
||||
- @headers.each do |header|
|
||||
%th= header
|
||||
%tbody
|
||||
- @deploys.each do |deploy, value|
|
||||
- @table.each do |row|
|
||||
- row[:urls].each do |url|
|
||||
%tr
|
||||
%td= t(".#{deploy}.title")
|
||||
%td= value ? t(".#{deploy}.success") : t(".#{deploy}.error")
|
||||
%td= row[:title]
|
||||
%td= row[:status]
|
||||
%td= link_to_if url.present?, url, url
|
||||
%td
|
||||
%time{ datetime: row[:seconds][:machine] }= row[:seconds][:human]
|
||||
%td= row[:size]
|
||||
|
||||
= sanitize_markdown t('.help'), tags: %w[p a strong em]
|
||||
= sanitize_markdown @help, tags: %w[p a strong em]
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
= '# ' + t('.hi')
|
||||
= "# #{@hi}"
|
||||
\
|
||||
= t('.explanation', fqdn: @deploy_local.site.hostname)
|
||||
= @explanation
|
||||
\
|
||||
= Terminal::Table.new do |table|
|
||||
- table << [t('.th.type'), t('.th.status')]
|
||||
- table.add_separator
|
||||
- @deploys.each do |deploy, value|
|
||||
- table << [t(".#{deploy}.title"),
|
||||
value ? t(".#{deploy}.success") : t(".#{deploy}.error")]
|
||||
= @terminal_table
|
||||
\
|
||||
= t('.help')
|
||||
= @help
|
||||
|
|
|
@ -78,6 +78,9 @@ en:
|
|||
th:
|
||||
type: Type
|
||||
status: Status
|
||||
seconds: Duration
|
||||
size: Space used
|
||||
url: Address
|
||||
deploy_local:
|
||||
title: Build the site
|
||||
success: Success!
|
||||
|
|
|
@ -78,6 +78,9 @@ es:
|
|||
th:
|
||||
type: Tipo
|
||||
status: Estado
|
||||
seconds: Duración
|
||||
size: Espacio ocupado
|
||||
url: Dirección
|
||||
deploy_local:
|
||||
title: Generar el sitio
|
||||
success: ¡Éxito!
|
||||
|
|
Loading…
Reference in a new issue