Merge pull request #10863 from colinux/maintenance-tasks-template
ETQ dev, je peux programmer des maintenance tasks à jouer lors de leur déploiement
This commit is contained in:
commit
fbb9405e50
40 changed files with 349 additions and 41 deletions
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class BackfillBulkMessagesWithProcedureIdTask < MaintenanceTasks::Task
|
class BackfillBulkMessagesWithProcedureIdTask < MaintenanceTasks::Task
|
||||||
|
# Périmètre: envoi d’un email groupé aux usagers ayant dossiers en brouillon.
|
||||||
|
# Change la manière dont ces messages sont liés aux démarches.
|
||||||
|
# 2024-03-12-01 PR #10071
|
||||||
def collection
|
def collection
|
||||||
BulkMessage
|
BulkMessage
|
||||||
.where(procedure: nil)
|
.where(procedure: nil)
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class BackfillCityNameTask < MaintenanceTasks::Task
|
class BackfillCityNameTask < MaintenanceTasks::Task
|
||||||
|
# corrige des données du champ adresse suite à un bug
|
||||||
|
# introduit pendant quelques jours début mars
|
||||||
|
# 2024-04-09-02 PR #10290
|
||||||
attribute :champ_ids, :string
|
attribute :champ_ids, :string
|
||||||
validates :champ_ids, presence: true
|
validates :champ_ids, presence: true
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class BackfillClonedChampsPrivatePieceJustificativesTask < MaintenanceTasks::Task
|
class BackfillClonedChampsPrivatePieceJustificativesTask < MaintenanceTasks::Task
|
||||||
|
# Supprime les PJ d’annotations privées
|
||||||
|
# qui étaient conservées par erreur lorsqu’un dossier était cloné
|
||||||
|
# 2024-05-27-01 PR #10435
|
||||||
def collection
|
def collection
|
||||||
Dossier.en_brouillon.where.not(parent_dossier_id: nil)
|
Dossier.en_brouillon.where.not(parent_dossier_id: nil)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class BackfillClosingReasonInClosedProceduresTask < MaintenanceTasks::Task
|
class BackfillClosingReasonInClosedProceduresTask < MaintenanceTasks::Task
|
||||||
|
# Remet les messages de cloture d'une démarche proprement (sinon affichage KO).
|
||||||
|
# Suite de UpdateClosingReasonIfNoReplacedByIdTask
|
||||||
|
# 2024-05-27-01 PR #9930
|
||||||
def collection
|
def collection
|
||||||
Procedure
|
Procedure
|
||||||
.with_discarded
|
.with_discarded
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class BackfillCommuneCodeFromNameTask < MaintenanceTasks::Task
|
class BackfillCommuneCodeFromNameTask < MaintenanceTasks::Task
|
||||||
|
# corrige structure champs commune pour une démarche donnée. Suite à un bug ?
|
||||||
|
# 2024-05-31-01 PR #10469
|
||||||
|
|
||||||
attribute :procedure_id, :string
|
attribute :procedure_id, :string
|
||||||
validates :procedure_id, presence: true
|
validates :procedure_id, presence: true
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class BackfillDepartementServicesTask < MaintenanceTasks::Task
|
class BackfillDepartementServicesTask < MaintenanceTasks::Task
|
||||||
|
# Fait le lien service – département pour permettre
|
||||||
|
# le filtrage des démarches par département
|
||||||
|
# 2023-10-30-01 PR #9647
|
||||||
def collection
|
def collection
|
||||||
Service.where.not(etablissement_infos: nil)
|
Service.where.not(etablissement_infos: nil)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class BackfillDeposeAtOnDeletedDossiersTask < MaintenanceTasks::Task
|
class BackfillDeposeAtOnDeletedDossiersTask < MaintenanceTasks::Task
|
||||||
|
# Améliore les stats à propos des dates de dépôts pour les dossiers supprimés
|
||||||
|
# 2024-04-05-01 PR #10259
|
||||||
def collection
|
def collection
|
||||||
DeletedDossier.where(depose_at: nil)
|
DeletedDossier.where(depose_at: nil)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class BackfillEffectifAnnuelAnneeTask < MaintenanceTasks::Task
|
class BackfillEffectifAnnuelAnneeTask < MaintenanceTasks::Task
|
||||||
|
# API entreprise: rattrape les informations d'effectif
|
||||||
|
# 2024-05-27-01 PR #10053
|
||||||
def collection
|
def collection
|
||||||
Etablissement.where.not(entreprise_effectif_annuel: nil).where(entreprise_effectif_annuel_annee: nil)
|
Etablissement.where.not(entreprise_effectif_annuel: nil).where(entreprise_effectif_annuel_annee: nil)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class BackfillInvalidDossiersForTiersTask < MaintenanceTasks::Task
|
class BackfillInvalidDossiersForTiersTask < MaintenanceTasks::Task
|
||||||
|
# Corrige les dossiers declarés pour un tiers mais sans avoir renseigné les infos du tiers
|
||||||
|
# 2024-05-22-01
|
||||||
def collection
|
def collection
|
||||||
Dossier.where(for_tiers: true).where(mandataire_first_name: nil)
|
Dossier.where(for_tiers: true).where(mandataire_first_name: nil)
|
||||||
end
|
end
|
||||||
|
|
24
app/tasks/maintenance/concerns/runnable_on_deploy_concern.rb
Normal file
24
app/tasks/maintenance/concerns/runnable_on_deploy_concern.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Maintenance
|
||||||
|
module RunnableOnDeployConcern
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
class_methods do
|
||||||
|
def run_on_first_deploy
|
||||||
|
@run_on_first_deploy = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_on_deploy?
|
||||||
|
return false unless @run_on_first_deploy
|
||||||
|
|
||||||
|
task = MaintenanceTasks::TaskDataShow.new(name)
|
||||||
|
|
||||||
|
return false if task.completed_runs.not_errored.any?
|
||||||
|
return false if task.active_runs.any?
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
25
app/tasks/maintenance/concerns/statements_helpers_concern.rb
Normal file
25
app/tasks/maintenance/concerns/statements_helpers_concern.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Maintenance
|
||||||
|
module StatementsHelpersConcern
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
# Execute block in transaction with a local statement timeout.
|
||||||
|
# A value of 0 disable the timeout.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# def collection
|
||||||
|
# with_statement_timeout("5min") do
|
||||||
|
# Dossier.all
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
def with_statement_timeout(timeout)
|
||||||
|
ApplicationRecord.transaction do
|
||||||
|
ApplicationRecord.connection.execute("SET LOCAL statement_timeout = '#{timeout}'")
|
||||||
|
yield
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class CreatePreviewsForPjOfLatestDossiersTask < MaintenanceTasks::Task
|
class CreatePreviewsForPjOfLatestDossiersTask < MaintenanceTasks::Task
|
||||||
|
# Génère les vignettes de PJ existantes pour les dossiers déposés entre 2 dates (facultatif)
|
||||||
|
# Elles sont affichées dans le nouvel onglet "Pièces jointes" des instructeurs.
|
||||||
|
# 2024-07-11-01
|
||||||
attribute :start_text, :string
|
attribute :start_text, :string
|
||||||
validates :start_text, presence: true
|
validates :start_text, presence: true
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class CreateVariantsForPjOfLatestDossiersTask < MaintenanceTasks::Task
|
class CreateVariantsForPjOfLatestDossiersTask < MaintenanceTasks::Task
|
||||||
|
# Génère les vignettes de fichiers PDF pour les dossiers déposés entre 2 dates (facultatif)
|
||||||
|
# Elles sont affichées dans le nouvel onglet "Pièces jointes" des instructeurs.
|
||||||
|
# 2024-07-11-01
|
||||||
attribute :start_text, :string
|
attribute :start_text, :string
|
||||||
validates :start_text, presence: true
|
validates :start_text, presence: true
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class DeleteDraftRevisionTypeDeChampsTask < MaintenanceTasks::Task
|
class DeleteDraftRevisionTypeDeChampsTask < MaintenanceTasks::Task
|
||||||
csv_collection
|
# Modifie le form d’une démarche à partir d’un CSV (dev spécifique Fonds Verts).
|
||||||
|
|
||||||
# See UpdateDraftRevisionTypeDeChampsTask for more information
|
# See UpdateDraftRevisionTypeDeChampsTask for more information
|
||||||
# Just add delete_flag with "true" to effectively remove the type de champ from the draft.
|
# Just add delete_flag with "true" in CSV to effectively remove the type de champ from the draft.
|
||||||
|
csv_collection
|
||||||
|
|
||||||
def process(row)
|
def process(row)
|
||||||
return unless row["delete_flag"] == "true"
|
return unless row["delete_flag"] == "true"
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class DestroyIncompleteBulkMessagesTask < MaintenanceTasks::Task
|
class DestroyIncompleteBulkMessagesTask < MaintenanceTasks::Task
|
||||||
|
# Périmètre: envoi d’un email groupé aux usagers ayant dossiers en brouillon.
|
||||||
|
# Change la manière dont ces messages sont liés aux démarches.
|
||||||
|
# Suite de BackfillBulkMessagesWithProcedureIdTask
|
||||||
|
# 2024-03-12-01 PR #10071
|
||||||
def collection
|
def collection
|
||||||
BulkMessage.where(procedure: nil).where.missing(:groupe_instructeurs)
|
BulkMessage.where(procedure: nil).where.missing(:groupe_instructeurs)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class DestroyProcedureWithoutAdministrateurAndWithoutDossierTask < MaintenanceTasks::Task
|
class DestroyProcedureWithoutAdministrateurAndWithoutDossierTask < MaintenanceTasks::Task
|
||||||
|
# suppression de procédures closes sans admin et sans dossier
|
||||||
|
# 2024-03-18-01 PR #10125
|
||||||
def collection
|
def collection
|
||||||
Procedure.with_discarded.where.missing(:administrateurs, :dossiers)
|
Procedure.with_discarded.where.missing(:administrateurs, :dossiers)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class DisableRemainingInvalidMonAvisTask < MaintenanceTasks::Task
|
class DisableRemainingInvalidMonAvisTask < MaintenanceTasks::Task
|
||||||
|
# Supprime les codes d’intégration « mon avis » invalides
|
||||||
|
# 2024-03-18-01 PR #10120
|
||||||
def collection
|
def collection
|
||||||
# rubocop:disable DS/Unscoped
|
# rubocop:disable DS/Unscoped
|
||||||
Procedure.unscoped.where.not(monavis_embed: nil)
|
Procedure.unscoped.where.not(monavis_embed: nil)
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class FixDecimalNumberWithSpacesTask < MaintenanceTasks::Task
|
class FixDecimalNumberWithSpacesTask < MaintenanceTasks::Task
|
||||||
|
# normalise les champs nombres en y supprimant les éventuels espaces
|
||||||
|
# 2024-07-01-01 PR #10554
|
||||||
|
|
||||||
ANY_SPACES = /[[:space:]]/
|
ANY_SPACES = /[[:space:]]/
|
||||||
def collection
|
def collection
|
||||||
Champs::DecimalNumberChamp.where.not(value: nil)
|
Champs::DecimalNumberChamp.where.not(value: nil)
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class FixDureeConservationGreaterThanMaxDureeConservationTask < MaintenanceTasks::Task
|
class FixDureeConservationGreaterThanMaxDureeConservationTask < MaintenanceTasks::Task
|
||||||
|
# Corrige la durée de conservation des dossiers :
|
||||||
|
# pour toutes les démarches dont la durée de conservation est supérieure
|
||||||
|
# à celle de l'instance, on prend la durée max de DS (12 mois)
|
||||||
|
# 2024-05-27-01 PR #10107
|
||||||
def collection
|
def collection
|
||||||
Procedure.where('duree_conservation_dossiers_dans_ds > max_duree_conservation_dossiers_dans_ds')
|
Procedure.where('duree_conservation_dossiers_dans_ds > max_duree_conservation_dossiers_dans_ds')
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class FixOpenProceduresWithClosingReasonTask < MaintenanceTasks::Task
|
class FixOpenProceduresWithClosingReasonTask < MaintenanceTasks::Task
|
||||||
|
# Corrige des démarches avec un motif de fermerture alors qu’elles ont été publiées
|
||||||
|
# 2024-05-27-01 PR #10181
|
||||||
def collection
|
def collection
|
||||||
Procedure
|
Procedure
|
||||||
.with_discarded
|
.with_discarded
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class MoveDolToColdStorageTask < MaintenanceTasks::Task
|
class MoveDolToColdStorageTask < MaintenanceTasks::Task
|
||||||
|
# Opération de rattrapage suite à un cron qui ne fonctionnait plus.
|
||||||
|
# Permet de déplacer toutes les traces fonctionnelles (DossierOperationLog)
|
||||||
|
# vers le stockage object plutot que de les conserver en BDD
|
||||||
|
# 2024-04-15-01
|
||||||
attribute :start_text, :string
|
attribute :start_text, :string
|
||||||
validates :start_text, presence: true
|
validates :start_text, presence: true
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,11 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class RecomputeBlobChecksumTask < MaintenanceTasks::Task
|
class RecomputeBlobChecksumTask < MaintenanceTasks::Task
|
||||||
|
# Avant février 2024, les filigranes ont corrompu les hash des fichiers.
|
||||||
|
# Régulièrement, des dossiers en brouillon étaient déposés avec ce problème
|
||||||
|
# (on retrouve les fichiers corrompu dans l'onglet retry de sidekiq).
|
||||||
|
# Cette tache recalcule les hashes.
|
||||||
|
# 2024-05-27-01
|
||||||
attribute :blob_ids, :string
|
attribute :blob_ids, :string
|
||||||
validates :blob_ids, presence: true
|
validates :blob_ids, presence: true
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class SamsungBrowserIsSupportedTask < MaintenanceTasks::Task
|
class SamsungBrowserIsSupportedTask < MaintenanceTasks::Task
|
||||||
|
# Corrige une donnée si le navigateur utilisé
|
||||||
|
# dans l’historique des Traitements des dossiers
|
||||||
|
# 2024-02-21-01
|
||||||
def collection
|
def collection
|
||||||
Traitement.where(browser_name: 'Samsung Browser', browser_version: 12..)
|
Traitement.where(browser_name: 'Samsung Browser', browser_version: 12..)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class SpreadDossierDeletionTask < MaintenanceTasks::Task
|
class SpreadDossierDeletionTask < MaintenanceTasks::Task
|
||||||
|
# Contourne un égorgement de suppression de millions de dossiers qui aurait eu lieu le même jour
|
||||||
|
# 2024-05-27-01 PR #10062
|
||||||
ERROR_OCCURED_AT = Date.new(2024, 2, 14)
|
ERROR_OCCURED_AT = Date.new(2024, 2, 14)
|
||||||
ERROR_OCCURED_RANGE = ERROR_OCCURED_AT.at_midnight..(ERROR_OCCURED_AT + 1.day)
|
ERROR_OCCURED_RANGE = ERROR_OCCURED_AT.at_midnight..(ERROR_OCCURED_AT + 1.day)
|
||||||
SPREAD_DURATION_IN_DAYS = 150
|
SPREAD_DURATION_IN_DAYS = 150
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Maintenance
|
||||||
|
class T20241009NoopAttemptRunOnDeployTask < MaintenanceTasks::Task
|
||||||
|
# Documentation: cette tâche ne fait rien mais sert à vérifier
|
||||||
|
# qu'elle sera bien exécutée sur le déploiement suivant
|
||||||
|
# pour remplacer after party.
|
||||||
|
|
||||||
|
include RunnableOnDeployConcern
|
||||||
|
include StatementsHelpersConcern
|
||||||
|
|
||||||
|
# Uncomment only if this task MUST run imperatively on its first deployment.
|
||||||
|
# If possible, leave commented for manual execution later.
|
||||||
|
run_on_first_deploy
|
||||||
|
|
||||||
|
def collection
|
||||||
|
1.upto(10).to_a
|
||||||
|
end
|
||||||
|
|
||||||
|
def process(element)
|
||||||
|
# NOOP
|
||||||
|
end
|
||||||
|
|
||||||
|
def count
|
||||||
|
10
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class UpdateClosingReasonIfNoReplacedByIdTask < MaintenanceTasks::Task
|
class UpdateClosingReasonIfNoReplacedByIdTask < MaintenanceTasks::Task
|
||||||
|
# Remet les messages de cloture d'une démarche proprement (sinon affichage KO).
|
||||||
|
# Avant BackfillClosingReasonInClosedProceduresTask
|
||||||
|
# 2024-03-21-01 PR #10158
|
||||||
def collection
|
def collection
|
||||||
Procedure
|
Procedure
|
||||||
.with_discarded
|
.with_discarded
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class UpdateConditionsBasedOnCommuneOrEpciChampTask < MaintenanceTasks::Task
|
class UpdateConditionsBasedOnCommuneOrEpciChampTask < MaintenanceTasks::Task
|
||||||
|
# Met à jour les conditions et règles de routage
|
||||||
|
# pour les champs communes et ECPI suite à l'ajout de nouveaux opérateurs
|
||||||
|
# Voir aussi UpdateRoutingRulesBasedOnCommuneOrEpciChampTask
|
||||||
|
# 2023-12-20-01 PR #9850
|
||||||
include Logic
|
include Logic
|
||||||
|
|
||||||
def collection
|
def collection
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class UpdateDraftRevisionTypeDeChampsTask < MaintenanceTasks::Task
|
class UpdateDraftRevisionTypeDeChampsTask < MaintenanceTasks::Task
|
||||||
|
# Modifie le form d’une démarche à partir d’un CSV (dev pour les Fonds Verts)
|
||||||
|
|
||||||
csv_collection
|
csv_collection
|
||||||
|
|
||||||
# CSV structure:
|
# CSV structure:
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class UpdateRoutingRulesBasedOnCommuneOrEpciChampTask < MaintenanceTasks::Task
|
class UpdateRoutingRulesBasedOnCommuneOrEpciChampTask < MaintenanceTasks::Task
|
||||||
|
# Ces 2 tâches mettent à jour les conditions et règles de routage
|
||||||
|
# pour les champs communes et ECPI suite à l'ajout de nouveaux opérateurs
|
||||||
|
# Voir aussi UpdateConditionsBasedOnCommuneOrEpciChampTask
|
||||||
|
# 2023-12-20-01 PR #9850
|
||||||
include Logic
|
include Logic
|
||||||
|
|
||||||
def collection
|
def collection
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class UpdateServiceEtablissementInfosTask < MaintenanceTasks::Task
|
class UpdateServiceEtablissementInfosTask < MaintenanceTasks::Task
|
||||||
|
# Géocode les services à partir des établissements
|
||||||
|
# 2024-05-27-01 PR #10106
|
||||||
|
|
||||||
# No more 20 geocoding by 10 seconds window
|
# No more 20 geocoding by 10 seconds window
|
||||||
THROTTLE_LIMIT = 20
|
THROTTLE_LIMIT = 20
|
||||||
THROTTLE_PERIOD = 10.seconds
|
THROTTLE_PERIOD = 10.seconds
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
module Maintenance
|
module Maintenance
|
||||||
class UpdateZonesTask < MaintenanceTasks::Task
|
class UpdateZonesTask < MaintenanceTasks::Task
|
||||||
|
# Synchronise les zones en base à partir du fichier de config zones.yml
|
||||||
|
# 2024-05-27-01 PR #10077
|
||||||
def collection
|
def collection
|
||||||
config = Psych.safe_load(Rails.root.join("config", "zones.yml").read)
|
config = Psych.safe_load(Rails.root.join("config", "zones.yml").read)
|
||||||
config['ministeres']
|
config['ministeres']
|
||||||
|
|
|
@ -37,6 +37,9 @@ FileUtils.chdir APP_ROOT do
|
||||||
puts "\n== Running after_party tasks =="
|
puts "\n== Running after_party tasks =="
|
||||||
system! 'bin/rails after_party:run'
|
system! 'bin/rails after_party:run'
|
||||||
|
|
||||||
|
puts "\n== Running on deploy maintenance tasks =="
|
||||||
|
system! 'bin/rails deploy:maintenance_tasks'
|
||||||
|
|
||||||
puts "\n== Removing old logs =="
|
puts "\n== Removing old logs =="
|
||||||
system! 'bin/rails log:clear'
|
system! 'bin/rails log:clear'
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ module TPS
|
||||||
Rails.autoloaders.main.ignore(Rails.root.join('lib/cops'))
|
Rails.autoloaders.main.ignore(Rails.root.join('lib/cops'))
|
||||||
Rails.autoloaders.main.ignore(Rails.root.join('lib/linters'))
|
Rails.autoloaders.main.ignore(Rails.root.join('lib/linters'))
|
||||||
Rails.autoloaders.main.ignore(Rails.root.join('lib/tasks/task_helper.rb'))
|
Rails.autoloaders.main.ignore(Rails.root.join('lib/tasks/task_helper.rb'))
|
||||||
|
Rails.autoloaders.main.collapse('app/tasks/maintenance/concerns')
|
||||||
config.paths.add Rails.root.join('spec/mailers/previews').to_s, eager_load: true
|
config.paths.add Rails.root.join('spec/mailers/previews').to_s, eager_load: true
|
||||||
config.autoload_paths << "#{Rails.root}/app/jobs/concerns"
|
config.autoload_paths << "#{Rails.root}/app/jobs/concerns"
|
||||||
|
|
||||||
|
|
|
@ -113,6 +113,29 @@
|
||||||
],
|
],
|
||||||
"note": ""
|
"note": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"warning_type": "SQL Injection",
|
||||||
|
"warning_code": 0,
|
||||||
|
"fingerprint": "7dc4935d5b68365bedb8f6b953f01b396cff4daa533c98ee56a84249ca5a1f90",
|
||||||
|
"check_name": "SQL",
|
||||||
|
"message": "Possible SQL injection",
|
||||||
|
"file": "app/tasks/maintenance/concerns/statements_helpers_concern.rb",
|
||||||
|
"line": 19,
|
||||||
|
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
|
||||||
|
"code": "ApplicationRecord.connection.execute(\"SET LOCAL statement_timeout = '#{timeout}'\")",
|
||||||
|
"render_path": null,
|
||||||
|
"location": {
|
||||||
|
"type": "method",
|
||||||
|
"class": "Maintenance::StatementsHelpersConcern",
|
||||||
|
"method": "with_statement_timeout"
|
||||||
|
},
|
||||||
|
"user_input": "timeout",
|
||||||
|
"confidence": "Medium",
|
||||||
|
"cwe_id": [
|
||||||
|
89
|
||||||
|
],
|
||||||
|
"note": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"warning_type": "Cross-Site Scripting",
|
"warning_type": "Cross-Site Scripting",
|
||||||
"warning_code": 2,
|
"warning_code": 2,
|
||||||
|
|
20
config/initializers/maintenance_tasks.rb
Normal file
20
config/initializers/maintenance_tasks.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
Rails.application.config.after_initialize do
|
||||||
|
if defined?(Rails::Generators)
|
||||||
|
require "generators/maintenance_tasks/task_generator"
|
||||||
|
|
||||||
|
class MaintenanceTasks::TaskGenerator
|
||||||
|
alias_method :original_assign_names!, :assign_names!
|
||||||
|
source_paths << Rails.root.join("lib/templates/maintenance_tasks")
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Prefix the task name with a date so the tasks are better sorted.
|
||||||
|
def assign_names!(name)
|
||||||
|
timestamped_name = "T#{Date.current.strftime("%Y%m%d")}#{name}"
|
||||||
|
original_assign_names!(timestamped_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
17
lib/generators/after_party/task/task_generator.rb
Normal file
17
lib/generators/after_party/task/task_generator.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails/generators'
|
||||||
|
|
||||||
|
Rails.application.config.after_initialize do
|
||||||
|
module AfterParty
|
||||||
|
module Generators
|
||||||
|
class TaskGenerator
|
||||||
|
prepend Module.new {
|
||||||
|
def invoke_all
|
||||||
|
warn "[DEPRECATION] 'after_party:task' is deprecated. Use 'rails generate maintenance_tasks:task #{name}' instead."
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,42 +1,14 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
def domains_for_stage
|
namespace :deploy do
|
||||||
if ENV['DOMAINS'].present?
|
task maintenance_tasks: :environment do
|
||||||
ENV['DOMAINS'].split
|
tasks = MaintenanceTasks::Task
|
||||||
else
|
.load_all
|
||||||
raise "DOMAINS is empty. It must be something like DOMAINS='web1.dev web2.dev'"
|
.filter { _1.respond_to?(:run_on_deploy?) && _1.run_on_deploy? }
|
||||||
end
|
|
||||||
end
|
tasks.each do |task|
|
||||||
|
Rails.logger.info { "MaintenanceTask run on deploy #{task.name}" }
|
||||||
task :setup do
|
MaintenanceTasks::Runner.run(name: task.name)
|
||||||
domains = domains_for_stage
|
end
|
||||||
|
|
||||||
domains.each do |domain|
|
|
||||||
sh "mina setup domain=#{domain}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
task :deploy do
|
|
||||||
domains = domains_for_stage
|
|
||||||
branch = ENV.fetch('BRANCH')
|
|
||||||
|
|
||||||
domains.each do |domain|
|
|
||||||
sh "mina deploy domain=#{domain} branch=#{branch} force_asset_precompile=true"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
task :post_deploy do
|
|
||||||
domains = domains_for_stage
|
|
||||||
branch = ENV.fetch('BRANCH')
|
|
||||||
|
|
||||||
sh "mina post_deploy domain=#{domains.first} branch=#{branch}"
|
|
||||||
end
|
|
||||||
|
|
||||||
task :rollback do
|
|
||||||
domains = domains_for_stage
|
|
||||||
branch = ENV.fetch('BRANCH')
|
|
||||||
|
|
||||||
domains.each do |domain|
|
|
||||||
sh "mina rollback service:restart_puma service:reload_nginx service:restart_delayed_job domain=#{domain} branch=#{branch}"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
32
lib/templates/maintenance_tasks/task.rb.tt
Normal file
32
lib/templates/maintenance_tasks/task.rb.tt
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module <%= tasks_module %>
|
||||||
|
<% module_namespacing do -%>
|
||||||
|
class <%= class_name %>Task < MaintenanceTasks::Task
|
||||||
|
# Documentation: cette tâche modifie les données pour…
|
||||||
|
|
||||||
|
include RunnableOnDeployConcern
|
||||||
|
include StatementsHelpersConcern
|
||||||
|
|
||||||
|
# Uncomment only if this task MUST run imperatively on its first deployment.
|
||||||
|
# If possible, leave commented for manual execution later.
|
||||||
|
# run_on_first_deploy
|
||||||
|
|
||||||
|
def collection
|
||||||
|
# Collection to be iterated over
|
||||||
|
# Must be Active Record Relation or Array
|
||||||
|
end
|
||||||
|
|
||||||
|
def process(element)
|
||||||
|
# The work to be done in a single iteration of the task.
|
||||||
|
# This should be idempotent, as the same element may be processed more
|
||||||
|
# than once if the task is interrupted and resumed.
|
||||||
|
end
|
||||||
|
|
||||||
|
def count
|
||||||
|
# Optionally, define the number of rows that will be iterated over
|
||||||
|
# This is used to track the task's progress
|
||||||
|
end
|
||||||
|
end
|
||||||
|
<% end -%>
|
||||||
|
end
|
|
@ -0,0 +1,53 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Maintenance::RunnableOnDeployConcern do
|
||||||
|
let(:test_class) do
|
||||||
|
Class.new do
|
||||||
|
include Maintenance::RunnableOnDeployConcern
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.run_on_deploy?' do
|
||||||
|
context 'when run_on_first_deploy is not set' do
|
||||||
|
it 'returns false' do
|
||||||
|
expect(test_class.run_on_deploy?).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when run_on_first_deploy is set' do
|
||||||
|
before do
|
||||||
|
test_class.run_on_first_deploy
|
||||||
|
allow(MaintenanceTasks::TaskDataShow).to receive(:new).and_return(task_data_show)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:task_data_show) { instance_double(MaintenanceTasks::TaskDataShow, completed_runs: completed_runs, active_runs: active_runs) }
|
||||||
|
let(:completed_runs) { double(ActiveRecord::Relation, not_errored: not_errored_runs) }
|
||||||
|
let(:active_runs) { [] }
|
||||||
|
let(:not_errored_runs) { [] }
|
||||||
|
|
||||||
|
context 'when there are no run yet' do
|
||||||
|
it 'returns true' do
|
||||||
|
expect(test_class.run_on_deploy?).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are completed runs without errors' do
|
||||||
|
let(:not_errored_runs) { [instance_double(MaintenanceTasks::Run)] }
|
||||||
|
|
||||||
|
it 'returns false' do
|
||||||
|
expect(test_class.run_on_deploy?).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are active runs' do
|
||||||
|
let(:active_runs) { [instance_double(MaintenanceTasks::Run)] }
|
||||||
|
|
||||||
|
it 'returns false' do
|
||||||
|
expect(test_class.run_on_deploy?).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,31 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Maintenance::StatementsHelpersConcern do
|
||||||
|
let(:dummy_class) do
|
||||||
|
Class.new do
|
||||||
|
include Maintenance::StatementsHelpersConcern
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:instance) { dummy_class.new }
|
||||||
|
|
||||||
|
describe '#with_statement_timeout' do
|
||||||
|
it 'applies the statement timeout and raises an error for long-running queries' do
|
||||||
|
expect {
|
||||||
|
instance.with_statement_timeout('1ms') do
|
||||||
|
# Cette requête devrait prendre plus de 1ms et donc déclencher un timeout
|
||||||
|
ActiveRecord::Base.connection.execute("SELECT pg_sleep(1)")
|
||||||
|
end
|
||||||
|
}.to raise_error(ActiveRecord::StatementInvalid, /canceling statement due to statement timeout/i)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows queries to complete within the timeout and returns the result' do
|
||||||
|
result = instance.with_statement_timeout('1s') do
|
||||||
|
ActiveRecord::Base.connection.execute("SELECT 42 AS answer").first['answer']
|
||||||
|
end
|
||||||
|
expect(result).to eq 42
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue