OPS-011, en partie. Ce que le workflow garantit maintenant : - rien ne part d un arbre casse. La suite, ruff check et ruff format tournent sur le runner de deploiement avant tout envoi. Une CI verte sur GitHub ne prouve rien ici : le deploiement se declenche a la main, sur ce que la branche contient a cet instant ; - seuls les fichiers nommes partent. La charge est une liste blanche — app/, wsgi.py, requirements.txt — et non l arbre de travail moins neuf exclusions. C est par cette porte que clear_db.py, la suite de tests et les definitions de CI se sont retrouves sur le noeud de production ; - le deploiement est verifie. /health est interroge pendant deux minutes apres l envoi et le job echoue s il ne repond jamais « healthy ». Avant, un arbre a moitie televerse etait un deploiement vert. Ce qui n est pas garanti, et c est ecrit dans le fichier : la bascule n est pas atomique. Le miroir se fait sur place, donc pendant le transfert la production execute un melange de deux versions. En cherchant a fermer ce point, un defaut a part entiere est apparu. Les contrats etaient ranges a os.getcwd()/documents et leur chemin absolu ecrit en base. La racine de stockage suivait donc le repertoire depuis lequel le processus avait ete lance : redemarrer le serveur ailleurs envoie les nouveaux contrats dans un nouvel arbre et rend les anciens illisibles — la base continuant d affirmer qu ils sont la, la panne se manifeste par un 500 au telechargement, pas par quelque chose d actionnable. app/storage.py fixe la racine et DOCUMENTS_ROOT la deplace. Les nouvelles lignes gardent un chemin relatif, les anciennes gardent leur chemin absolu et continuent de resoudre : aucune migration de donnees n est necessaire, donc ce changement n attend pas Alembic. C etait aussi le troisieme pre-requis de la bascule par repertoires de version. Les deux autres sont hors d atteinte d ici — la commande de demarrage Pterodactyl doit pointer sur current/, et les repertoires partages doivent etre installes sur le noeud. Les deux sont decrits dans docs/deployment.md, avec la procedure de retour arriere qui manquait. 511 tests.
60 lines
2.3 KiB
Python
60 lines
2.3 KiB
Python
"""Where uploaded documents live, and how the database refers to them.
|
|
|
|
Contracts were stored at `os.path.join(os.getcwd(), 'documents', …)`,
|
|
evaluated at upload time, and the resulting absolute path was written into
|
|
`Contract.file_path`. The storage root therefore moved with whatever
|
|
directory the process happened to be started from. Two consequences:
|
|
|
|
- one latent: start the server from elsewhere and new contracts land in a
|
|
new tree while the old ones become unreadable — with the database still
|
|
saying they are there, so the failure surfaces as a 500 on download
|
|
rather than as anything a person could act on;
|
|
- one blocking: it rules out a release-directory deployment (OPS-011)
|
|
outright. Every stored path would point inside a release that is about
|
|
to be replaced, so the first switch would take every contract ever
|
|
uploaded with it.
|
|
|
|
New rows keep a path *relative* to the document root. Old rows keep their
|
|
absolute path and are returned untouched, so this change needs no data
|
|
migration and can ship before Alembic does (DB-002).
|
|
"""
|
|
|
|
import os
|
|
|
|
#: Environment override for the document root. What a release-directory
|
|
#: deployment sets, to a path outside the releases — alongside them, not
|
|
#: inside whichever one is current.
|
|
DOCUMENTS_ROOT_ENV = 'DOCUMENTS_ROOT'
|
|
|
|
#: Sub-directory holding uploaded contracts, under the document root.
|
|
CONTRACTS_DIR = 'contrats signés'
|
|
|
|
|
|
def documents_root():
|
|
"""Absolute path of the document store.
|
|
|
|
Falls back to `documents/` beside this package — the project root
|
|
wherever it is installed, rather than wherever the process was launched.
|
|
"""
|
|
configured = os.getenv(DOCUMENTS_ROOT_ENV)
|
|
if configured:
|
|
return os.path.abspath(configured)
|
|
package_dir = os.path.dirname(os.path.abspath(__file__))
|
|
return os.path.join(os.path.dirname(package_dir), 'documents')
|
|
|
|
|
|
def document_path(stored_path):
|
|
"""Absolute path of a document, from what the database holds.
|
|
|
|
Args:
|
|
stored_path: The value of Contract.file_path or signed_file_path.
|
|
Relative for rows written since this module existed, absolute
|
|
for the ones written before.
|
|
|
|
Returns:
|
|
str: An absolute path.
|
|
"""
|
|
if os.path.isabs(stored_path):
|
|
return stored_path
|
|
return os.path.join(documents_root(), stored_path)
|