QUA-001, volet dialecte.
`postgresql://` ne veut pas dire "le pilote installe" : SQLAlchemy y lit
psycopg2 et importe ce module a la creation du moteur. requirements.txt
epingle psycopg 3 (`psycopg[binary]`) et pas psycopg2. Une installation
propre demarree sur cette URL leve donc
ModuleNotFoundError: No module named 'psycopg2'
avant la premiere requete. Verifie dans le .venv du depot, et c est
exactement la forme que Render distribue -- celle que docs/deployment.md et
docs/database-restore.md donnaient en exemple.
normalise_database_url() nomme le pilote quand l URL n en nomme pas.
`postgres://` (alias hérite, abandonne par SQLAlchemy en 1.4) est traite de
meme. Une URL qui nomme deja son pilote est laissee telle quelle, y compris
`postgresql+psycopg2://` : un environnement qui a psycopg2 garde le choix.
La normalisation a lieu apres l application de la configuration passee en
argument, pour couvrir aussi les appels de test. backup.py n avait pas
besoin d etre touche : il retirait deja le suffixe +pilote.
Documentation alignee sur les trois fichiers qui donnaient l exemple, dont
docs/deployment.md qui proposait sqlite:/// pour DATABASE_URL alors que
create_app refuse de demarrer sans PostgreSQL.
Reste de QUA-001, dit franchement
- les trois paquets parasites (dotenv, login, discord) ne sont plus dans
requirements.txt : deja retires. psycopg est deja epingle.
- la consolidation vers des groupes de dependances n est PAS faite. Le
deploiement est un miroir de fichiers lftp sans etape de construction ;
les groupes PEP 735 demandent pip >= 25.1 sur une machine dont on ne
peut pas verifier la version d ici. A revoir avec OPS-011.
14 tests, dont trois qui prouvent que l echec est reel et non theorique.
Co-Authored-By: Claude Opus 5 <[email protected]>
DATA-002 / OPS-001. backup.py ciblait SQLite : import sqlite3, DATABASE_PATH
par defaut instance/team_tryouts.db, et l'API de sauvegarde sqlite3. La
production tourne sur PostgreSQL, donc le fichier n'existait pas. Le script
affichait "[WARNING] Database not found... Skipping database backup" -- puis,
main() ne suivant que le resultat de la verification, **sortait avec le code
0**. Toute tache planifiee surveillant le code de sortie voyait vert alors
qu'aucune sauvegarde n'avait jamais ete produite.
Il n'existait donc aucune sauvegarde applicative de la base.
Reecriture
pg_dump en --format=custom : compresse, et pg_restore permet une
restauration selective, ce qu'un dump SQL a plat ne permet pas.
parse_database_url accepte les suffixes de dialecte SQLAlchemy
(postgresql+psycopg://) que pg_dump ne comprend pas, et refuse
explicitement une URL SQLite -- le cas exact qui passait en silence.
Le mot de passe ne figure jamais dans la ligne de commande : il serait
visible de tout processus capable de lister argv. Il passe par PGPASSWORD.
Il est egalement absent des messages affiches, qui atterrissent dans les
journaux du planificateur.
verify_backup lit l'archive avec pg_restore --list et exige au moins une
table : une archive illisible ne se restaure pas, et une archive sans
table signifie que le dump a vise la mauvaise cible. Les deux sont des
echecs silencieux qu'il vaut mieux attraper maintenant que pendant un
incident.
Le code de sortie vaut 0 uniquement si le dump a ete produit ET verifie.
L'archive des documents est conservee : les contrats signes n'existent que
sur disque, la base ne stocke que des chemins. Restaurer l'une sans l'autre
laisse des lignes pointant vers des fichiers absents.
docs/database-restore.md
Procedure de restauration testable sur une base jetable, requetes de
controle, demarrage de l'application sur la copie restauree, plan de
reprise par scenario. ENABLE_DISCORD_BOT=false y est signale comme non
optionnel : sans lui, l'exercice demarre un vrai bot et envoie de vraies
notifications a de vraies personnes, a partir de donnees restaurees.
Les points ouverts sont listes tels quels : aucune copie hors site, pas de
chiffrement au repos, aucune planification, et l'exercice de restauration
n'a jamais ete effectue.
17 tests sur ce qui est verifiable sans serveur PostgreSQL : analyse de
l'URL, construction de la commande, non-fuite du mot de passe, et surtout
codes de sortie -- le silence ne vaut plus succes.
Co-Authored-By: Claude Opus 5 <[email protected]>