Paquet YunoHost pour Vèy Lalwa — v1.0.1~ynh1
Format de paquetage 2, helpers 2.1, sur le modèle de jwe_ynh. Ce que l'installation fait, dans cet ordre : extraire l'archive, rendre le .env depuis les réponses d'installation, construire l'application web (npm ci, polices, build, prune), monter l'environnement Python, puis remplir la base depuis le corpus de recherche embarqué. Trois choix qui méritent d'être dits : - La base, le cache et l'export vivent dans data_dir, jamais dans le répertoire d'installation. « ynh_setup_source --full_replace » efface celui-ci intégralement à chaque mise à jour : y laisser la base effacerait tout le travail de veille accumulé. Vérifié en simulant la mise à jour — 59 textes, 367 sources et 3 passages survivent à l'effacement. - Un domaine dédié, sans question « path ». Les liens internes sont absolus et SvelteKit demanderait une recompilation avec « paths.base » pour tenir sur un sous-chemin. Mieux vaut une contrainte annoncée qu'un sous-chemin à moitié fonctionnel. - Les identifiants Légifrance sont facultatifs, et les tests s'exécutent sans : cela vérifie au passage le mode dégradé documenté. Le rapport de passage affiche alors « legifrance ignoré — repli sur … », et les deux autres collecteurs détectent quand même les 7 ajouts et les 4 changements de décision. Un minuteur systemd distinct porte le passage de collecte, le helper « ynh_config_add_systemd » ne gérant que l'unité principale. Il est arrêté avant toute mise à jour : un passage qui démarrerait pendant le remplacement des fichiers travaillerait sur un arbre à moitié écrit. Vérifié avant publication, pas après : séquence npm complète depuis l'archive publiée, environnement Python, remplissage, démarrage du service lisant la base de data_dir, origine déduite des en-têtes du proxy, passage en mode dégradé, et simulation d'effacement du répertoire d'installation. shellcheck sans avertissement, TOML valides. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
*.swp
|
||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# Vèy Lalwa pour YunoHost
|
||||||
|
|
||||||
|
[](https://labola.o-k-i.net/cyber-mawonaj/veye-lalwa)
|
||||||
|
[](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||||
|
|
||||||
|
*Paquet YunoHost pour [Vèy Lalwa](https://labola.o-k-i.net/cyber-mawonaj/veye-lalwa), veille législative française sous l'angle des libertés — zoom Guadeloupe.*
|
||||||
|
|
||||||
|
**N'installez pas ce paquet à la main s'il figure dans le catalogue YunoHost.** Utilisez plutôt `yunohost app install veyelalwa`.
|
||||||
|
|
||||||
|
## Vue d'ensemble
|
||||||
|
|
||||||
|
Vèy Lalwa suit les lois, projets et propositions de loi françaises, ainsi que les décisions du Conseil constitutionnel, et les lit sous un angle précis : ce qu'elles changent aux **libertés des individus, des associations et des entreprises**, avec un zoom Guadeloupe et outre-mer.
|
||||||
|
|
||||||
|
L'application se compose de deux moitiés indissociables :
|
||||||
|
|
||||||
|
- un **pipeline Python** qui remplit la base depuis un corpus de recherche embarqué, puis la tient à jour depuis Légifrance, le Sénat et le Conseil constitutionnel ;
|
||||||
|
- une **application SvelteKit** rendue côté serveur : recherche plein texte, filtres combinables, fiches sourcées, calendrier des échéances.
|
||||||
|
|
||||||
|
**Version incluse :** 1.0.1
|
||||||
|
|
||||||
|
## Captures
|
||||||
|
|
||||||
|
L'application est en français, thème sombre par défaut, thème clair disponible. Aucune image n'est embarquée dans ce paquet : l'interface n'utilise que du texte, des tracés SVG et deux polices auto-hébergées.
|
||||||
|
|
||||||
|
## Ce que l'installation met en place
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
| Service web | `veyelalwa.service` — Node, derrière le NGINX de YunoHost |
|
||||||
|
| Passage de collecte | `veyelalwa-pipeline.service`, déclenché par un minuteur |
|
||||||
|
| Cadence | 6 h et 18 h tous les jours, plus le dimanche à 9 h |
|
||||||
|
| Base pré-remplie | 52 textes, 360 sources, arrêtées au 25 juillet 2026 |
|
||||||
|
| Données persistantes | `/home/yunohost.app/veyelalwa/` — survivent aux mises à jour |
|
||||||
|
|
||||||
|
## Avant d'installer
|
||||||
|
|
||||||
|
**Un domaine dédié est nécessaire.** L'application occupe la racine (`/`) : ses liens internes sont absolus et SvelteKit exigerait une recompilation pour tenir sur un sous-chemin. Mieux vaut une contrainte annoncée qu'un sous-chemin à moitié fonctionnel.
|
||||||
|
|
||||||
|
**Les identifiants Légifrance sont facultatifs.** Sans eux, le collecteur correspondant se désactive et l'annonce dans le rapport de passage ; le Sénat et le Conseil constitutionnel prennent le relais, ce qui suffit à suivre les promulgations et les décisions constitutionnelles.
|
||||||
|
|
||||||
|
Si vous en avez : sur [piste.gouv.fr](https://piste.gouv.fr), déclarez une application **et abonnez-la à l'API « Légifrance »**. Utilisez le couple **OAuth** (« Client ID » / « Client secret ») — la clé d'API, elle, est refusée par le point d'accès du jeton.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- Administration au quotidien : `doc/ADMIN_fr.md`
|
||||||
|
- Fonctionnement du pipeline et dépannage : [`README-pipeline.md`](https://labola.o-k-i.net/cyber-mawonaj/veye-lalwa/src/branch/main/README-pipeline.md) du dépôt amont
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo yunohost app install https://labola.o-k-i.net/cyber-mawonaj/veye-lalwa_ynh
|
||||||
|
```
|
||||||
|
|
||||||
|
Mise à jour :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo yunohost app upgrade veyelalwa -u https://labola.o-k-i.net/cyber-mawonaj/veye-lalwa_ynh
|
||||||
|
```
|
||||||
|
|
||||||
|
L'installation construit l'application web (npm) et l'environnement Python, puis remplit la base : comptez quelques minutes et environ 1 Go de mémoire disponible pendant la construction.
|
||||||
|
|
||||||
|
## Licence
|
||||||
|
|
||||||
|
Paquet et application sous AGPL-3.0-or-later. Le corpus de recherche embarqué reste la propriété de ses auteurs.
|
||||||
|
|
||||||
|
Si vous réutilisez ces données, citez la source primaire — Journal officiel, assemblées, Conseil constitutionnel — plutôt que cette plateforme : elle n'est qu'un intermédiaire.
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Configuration de Vèy Lalwa — générée par YunoHost.
|
||||||
|
#
|
||||||
|
# Ce fichier est régénéré à chaque mise à jour et à chaque changement d'URL :
|
||||||
|
# ne l'éditez pas à la main, vos changements seraient perdus. Pour modifier les
|
||||||
|
# identifiants Légifrance :
|
||||||
|
# yunohost app config set __APP__ legifrance_client_id -v "…"
|
||||||
|
# ou, à défaut, réinstallez avec les bonnes valeurs.
|
||||||
|
|
||||||
|
# ── Application web ──────────────────────────────────────────────────────────
|
||||||
|
HOST=127.0.0.1
|
||||||
|
PORT=__PORT__
|
||||||
|
ORIGIN=https://__DOMAIN__
|
||||||
|
NODE_ENV=production
|
||||||
|
|
||||||
|
# ── Emplacements ─────────────────────────────────────────────────────────────
|
||||||
|
# Racine du code et du corpus de recherche. Explicite plutôt que déduite de
|
||||||
|
# l'emplacement du module : une installation non éditable ferait chercher le
|
||||||
|
# corpus à côté du venv, où il n'est pas.
|
||||||
|
VEILLE_RACINE=__INSTALL_DIR__
|
||||||
|
|
||||||
|
# Données persistantes, dans __DATA_DIR__ et non dans le répertoire
|
||||||
|
# d'installation : celui-ci est intégralement remplacé à chaque mise à jour.
|
||||||
|
VEILLE_DB=__DATA_DIR__/veille.db
|
||||||
|
VEILLE_CACHE=__DATA_DIR__/cache_http
|
||||||
|
VEILLE_DUMP_JSON=__DATA_DIR__/textes.json
|
||||||
|
|
||||||
|
# ── API Légifrance (PISTE) ───────────────────────────────────────────────────
|
||||||
|
# Vides = collecteur désactivé, repli documenté sur le Sénat et le Conseil
|
||||||
|
# constitutionnel. Le rapport de chaque passage indique lequel a été ignoré.
|
||||||
|
LEGIFRANCE_CLIENT_ID=__LEGIFRANCE_CLIENT_ID__
|
||||||
|
LEGIFRANCE_CLIENT_SECRET=__LEGIFRANCE_CLIENT_SECRET__
|
||||||
|
LEGIFRANCE_ENV=prod
|
||||||
|
|
||||||
|
# ── Politesse envers les services publics interrogés ─────────────────────────
|
||||||
|
# Valeur entre guillemets : systemd s'en passerait, mais un shell qui sourcerait
|
||||||
|
# ce fichier butterait sur les parenthèses et le point-virgule. La commande de
|
||||||
|
# diagnostic de doc/ADMIN le source justement.
|
||||||
|
VEILLE_USER_AGENT="veye-lalwa/1.0 (+https://__DOMAIN__ ; contact: __CONTACT_EMAIL__)"
|
||||||
|
VEILLE_DELAI_REQUETES=1.0
|
||||||
|
VEILLE_CACHE_TTL_H=6
|
||||||
|
VEILLE_SEUIL_FUZZY=0.86
|
||||||
|
|
||||||
|
# Notifications de fin de passage — désactivées par défaut.
|
||||||
|
VEILLE_NOTIF_ACTIVE=false
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/var/log/__APP__/*.log {
|
||||||
|
weekly
|
||||||
|
rotate 12
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
missingok
|
||||||
|
notifempty
|
||||||
|
create 640 __APP__ __APP__
|
||||||
|
su __APP__ __APP__
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
location / {
|
||||||
|
|
||||||
|
proxy_pass http://127.0.0.1:__PORT__;
|
||||||
|
|
||||||
|
# Ces en-têtes ne sont pas décoratifs : adapter-node en déduit l'origine
|
||||||
|
# des requêtes. Les omettre ferait échouer toute soumission de formulaire.
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
proxy_redirect off;
|
||||||
|
|
||||||
|
# L'application ne reçoit aucun téléversement : une limite basse suffit.
|
||||||
|
client_max_body_size 1M;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Vèy Lalwa — passage de collecte législative
|
||||||
|
Documentation=https://labola.o-k-i.net/cyber-mawonaj/veye-lalwa/src/branch/main/README-pipeline.md
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
User=__APP__
|
||||||
|
Group=__APP__
|
||||||
|
WorkingDirectory=__INSTALL_DIR__
|
||||||
|
Environment=PYTHONUNBUFFERED=1
|
||||||
|
EnvironmentFile=__INSTALL_DIR__/.env
|
||||||
|
ExecStart=__INSTALL_DIR__/venv/bin/python -m pipeline.update
|
||||||
|
|
||||||
|
# Un passage qui s'éternise a rencontré un problème réseau, pas une charge de
|
||||||
|
# travail : on l'arrête plutôt que de le laisser tenir la base ouverte.
|
||||||
|
TimeoutStartSec=1800
|
||||||
|
|
||||||
|
StandardOutput=append:/var/log/__APP__/pipeline.log
|
||||||
|
StandardError=inherit
|
||||||
|
|
||||||
|
# Durcissement : le pipeline lit le réseau et n'écrit que dans son répertoire
|
||||||
|
# de données.
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
PrivateDevices=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
ReadWritePaths=__DATA_DIR__ /var/log/__APP__
|
||||||
|
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||||
|
RestrictNamespaces=yes
|
||||||
|
RestrictRealtime=yes
|
||||||
|
DevicePolicy=closed
|
||||||
|
ProtectClock=yes
|
||||||
|
ProtectHostname=yes
|
||||||
|
ProtectProc=invisible
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
LockPersonality=yes
|
||||||
|
SystemCallArchitectures=native
|
||||||
|
SystemCallFilter=~@clock @debug @module @mount @obsolete @reboot @setuid @swap @cpu-emulation @privileged
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Vèy Lalwa — cadence de la veille (6 h, 18 h, dimanche 9 h)
|
||||||
|
Documentation=https://labola.o-k-i.net/cyber-mawonaj/veye-lalwa/src/branch/main/README-pipeline.md
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
# Les décisions du Conseil constitutionnel tombent en semaine et les
|
||||||
|
# promulgations suivent de un à trois jours : deux passages par jour suffisent
|
||||||
|
# largement, et ménagent des services publics gratuits.
|
||||||
|
OnCalendar=*-*-* 06:00:00
|
||||||
|
OnCalendar=*-*-* 18:00:00
|
||||||
|
# Passage supplémentaire le dimanche, pour rattraper le Journal officiel de fin
|
||||||
|
# de semaine.
|
||||||
|
OnCalendar=Sun *-*-* 09:00:00
|
||||||
|
|
||||||
|
# Dispersion : inutile de frapper Légifrance à la seconde ronde en même temps
|
||||||
|
# que tous les autres services qui l'interrogent.
|
||||||
|
RandomizedDelaySec=900
|
||||||
|
|
||||||
|
# Rattrape le passage manqué si le serveur était éteint.
|
||||||
|
Persistent=true
|
||||||
|
|
||||||
|
Unit=__APP__-pipeline.service
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Vèy Lalwa — veille législative, application web
|
||||||
|
Documentation=https://labola.o-k-i.net/cyber-mawonaj/veye-lalwa
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=__APP__
|
||||||
|
Group=__APP__
|
||||||
|
WorkingDirectory=__INSTALL_DIR__/web
|
||||||
|
Environment="PATH=__PATH_WITH_NODEJS__"
|
||||||
|
EnvironmentFile=__INSTALL_DIR__/.env
|
||||||
|
ExecStart=__NODEJS_DIR__/node build
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
StandardOutput=append:/var/log/__APP__/__APP__.log
|
||||||
|
StandardError=inherit
|
||||||
|
|
||||||
|
# Sandboxing options to harden security
|
||||||
|
# Details for these options: https://www.freedesktop.org/software/systemd/man/systemd.exec.html
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
PrivateDevices=yes
|
||||||
|
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
|
||||||
|
RestrictNamespaces=yes
|
||||||
|
RestrictRealtime=yes
|
||||||
|
DevicePolicy=closed
|
||||||
|
ProtectClock=yes
|
||||||
|
ProtectHostname=yes
|
||||||
|
ProtectProc=invisible
|
||||||
|
ProtectSystem=full
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
LockPersonality=yes
|
||||||
|
SystemCallArchitectures=native
|
||||||
|
SystemCallFilter=~@clock @debug @module @mount @obsolete @reboot @setuid @swap @cpu-emulation @privileged
|
||||||
|
|
||||||
|
# Denying access to capabilities that should not be relevant for webapps
|
||||||
|
# Doc: https://man7.org/linux/man-pages/man7/capabilities.7.html
|
||||||
|
CapabilityBoundingSet=~CAP_RAWIO CAP_MKNOD
|
||||||
|
CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE
|
||||||
|
CapabilityBoundingSet=~CAP_SYS_BOOT CAP_SYS_TIME CAP_SYS_MODULE CAP_SYS_PACCT
|
||||||
|
CapabilityBoundingSet=~CAP_LEASE CAP_LINUX_IMMUTABLE CAP_IPC_LOCK
|
||||||
|
CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_WAKE_ALARM
|
||||||
|
CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG
|
||||||
|
CapabilityBoundingSet=~CAP_MAC_ADMIN CAP_MAC_OVERRIDE
|
||||||
|
CapabilityBoundingSet=~CAP_NET_ADMIN CAP_NET_BROADCAST CAP_NET_RAW
|
||||||
|
CapabilityBoundingSet=~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYSLOG
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# Administration de Vèy Lalwa
|
||||||
|
|
||||||
|
## Où sont les choses
|
||||||
|
|
||||||
|
| Quoi | Où |
|
||||||
|
|---|---|
|
||||||
|
| Code et application construite | `__INSTALL_DIR__` |
|
||||||
|
| Base de veille, cache, export JSON | `__DATA_DIR__` |
|
||||||
|
| Configuration (identifiants compris) | `__INSTALL_DIR__/.env`, mode 600 |
|
||||||
|
| Journal de l'application web | `/var/log/__APP__/__APP__.log` |
|
||||||
|
| Journal des passages de collecte | `/var/log/__APP__/pipeline.log` |
|
||||||
|
|
||||||
|
Tout ce qui compte vit dans `__DATA_DIR__` : le répertoire d'installation est **intégralement remplacé** à chaque mise à jour.
|
||||||
|
|
||||||
|
## Le pipeline
|
||||||
|
|
||||||
|
Deux passages par jour, à 6 h et 18 h, plus un le dimanche à 9 h pour rattraper le Journal officiel de fin de semaine. Une dispersion de quinze minutes évite de frapper Légifrance en même temps que tout le monde.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Déclencher un passage tout de suite
|
||||||
|
systemctl start __APP__-pipeline.service
|
||||||
|
|
||||||
|
# Suivre ce qu'il fait
|
||||||
|
journalctl -u __APP__-pipeline -f
|
||||||
|
|
||||||
|
# Voir la prochaine échéance
|
||||||
|
systemctl list-timers __APP__-pipeline.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Voir ce que le pipeline changerait, sans rien écrire
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd __INSTALL_DIR__
|
||||||
|
sudo -u __APP__ bash -c 'set -a; . ./.env; set +a; \
|
||||||
|
./venv/bin/python -m pipeline.update --dry-run'
|
||||||
|
```
|
||||||
|
|
||||||
|
(`set -a` exporte automatiquement ce que le fichier définit ; `xargs` échouerait
|
||||||
|
sur les valeurs contenant des espaces.)
|
||||||
|
|
||||||
|
Le rapport liste chaque écart avec son ancienne et sa nouvelle valeur. **Commencez toujours par là** avant de soupçonner un problème.
|
||||||
|
|
||||||
|
## Identifiants Légifrance
|
||||||
|
|
||||||
|
L'application fonctionne sans. Le rapport de passage indique alors :
|
||||||
|
|
||||||
|
```
|
||||||
|
legifrance ignoré — LEGIFRANCE_CLIENT_ID absent de .env — repli sur
|
||||||
|
l'Assemblée nationale, le Sénat et le Conseil constitutionnel
|
||||||
|
```
|
||||||
|
|
||||||
|
Pour les ajouter, éditez `__INSTALL_DIR__/.env`, puis `systemctl start __APP__-pipeline.service`.
|
||||||
|
|
||||||
|
**Le piège** : une application PISTE affiche deux couples de valeurs.
|
||||||
|
|
||||||
|
| Ce que PISTE affiche | À utiliser |
|
||||||
|
|---|---|
|
||||||
|
| **Client ID** / **Client secret** | ✅ oui |
|
||||||
|
| API key / API key secret | ❌ non — `invalid_client` |
|
||||||
|
|
||||||
|
Second piège : les identifiants de production ne fonctionnent pas en bac à sable, qui exige une application distincte.
|
||||||
|
|
||||||
|
## Sauvegarde
|
||||||
|
|
||||||
|
La sauvegarde YunoHost couvre la base et la configuration. À la main :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sqlite3 __DATA_DIR__/veille.db ".backup /sauvegardes/veille-$(date +%F).db"
|
||||||
|
```
|
||||||
|
|
||||||
|
N'utilisez jamais `cp` pendant un passage : le mode WAL laisse des fichiers annexes que `.backup` gère correctement.
|
||||||
|
|
||||||
|
## Diagnostic
|
||||||
|
|
||||||
|
| Symptôme | Piste |
|
||||||
|
|---|---|
|
||||||
|
| Page blanche, 502 | `systemctl status __APP__` puis `tail /var/log/__APP__/__APP__.log` |
|
||||||
|
| « Base introuvable » | La base a disparu de `__DATA_DIR__`. Rejouez le remplissage (voir plus bas). |
|
||||||
|
| Formulaire de recherche refusé | `ORIGIN` dans `.env` ne correspond plus au domaine servi |
|
||||||
|
| Aucun texte nouveau depuis des semaines | Normal hors session parlementaire. Vérifiez avec `--dry-run`. |
|
||||||
|
| `invalid_client` dans le journal | Clé d'API utilisée au lieu du couple OAuth, ou application non abonnée à l'API Légifrance |
|
||||||
|
|
||||||
|
### Reconstruire la base depuis le corpus
|
||||||
|
|
||||||
|
Le corpus de recherche est embarqué dans l'archive : la base peut toujours être reconstruite, sans réseau.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd __INSTALL_DIR__
|
||||||
|
sudo -u __APP__ bash -c 'set -a; . ./.env; set +a; \
|
||||||
|
./venv/bin/python -m pipeline.seed_from_research --repartir-de-zero'
|
||||||
|
systemctl restart __APP__
|
||||||
|
```
|
||||||
|
|
||||||
|
Vous perdrez l'historique des passages, pas les 52 textes du corpus.
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
# Administration de Vèy Lalwa
|
||||||
|
|
||||||
|
## Où sont les choses
|
||||||
|
|
||||||
|
| Quoi | Où |
|
||||||
|
|---|---|
|
||||||
|
| Code et application construite | `__INSTALL_DIR__` |
|
||||||
|
| Base de veille, cache, export JSON | `__DATA_DIR__` |
|
||||||
|
| Configuration (identifiants compris) | `__INSTALL_DIR__/.env`, mode 600 |
|
||||||
|
| Journal de l'application web | `/var/log/__APP__/__APP__.log` |
|
||||||
|
| Journal des passages de collecte | `/var/log/__APP__/pipeline.log` |
|
||||||
|
|
||||||
|
Tout ce qui compte vit dans `__DATA_DIR__` : le répertoire d'installation est **intégralement remplacé** à chaque mise à jour.
|
||||||
|
|
||||||
|
## Le pipeline
|
||||||
|
|
||||||
|
Deux passages par jour, à 6 h et 18 h, plus un le dimanche à 9 h pour rattraper le Journal officiel de fin de semaine. Une dispersion de quinze minutes évite de frapper Légifrance en même temps que tout le monde.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Déclencher un passage tout de suite
|
||||||
|
systemctl start __APP__-pipeline.service
|
||||||
|
|
||||||
|
# Suivre ce qu'il fait
|
||||||
|
journalctl -u __APP__-pipeline -f
|
||||||
|
|
||||||
|
# Voir la prochaine échéance
|
||||||
|
systemctl list-timers __APP__-pipeline.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Voir ce que le pipeline changerait, sans rien écrire
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd __INSTALL_DIR__
|
||||||
|
sudo -u __APP__ bash -c 'set -a; . ./.env; set +a; \
|
||||||
|
./venv/bin/python -m pipeline.update --dry-run'
|
||||||
|
```
|
||||||
|
|
||||||
|
(`set -a` exporte automatiquement ce que le fichier définit ; `xargs` échouerait
|
||||||
|
sur les valeurs contenant des espaces.)
|
||||||
|
|
||||||
|
Le rapport liste chaque écart avec son ancienne et sa nouvelle valeur. **Commencez toujours par là** avant de soupçonner un problème.
|
||||||
|
|
||||||
|
## Identifiants Légifrance
|
||||||
|
|
||||||
|
L'application fonctionne sans. Le rapport de passage indique alors :
|
||||||
|
|
||||||
|
```
|
||||||
|
legifrance ignoré — LEGIFRANCE_CLIENT_ID absent de .env — repli sur
|
||||||
|
l'Assemblée nationale, le Sénat et le Conseil constitutionnel
|
||||||
|
```
|
||||||
|
|
||||||
|
Pour les ajouter, éditez `__INSTALL_DIR__/.env`, puis `systemctl start __APP__-pipeline.service`.
|
||||||
|
|
||||||
|
**Le piège** : une application PISTE affiche deux couples de valeurs.
|
||||||
|
|
||||||
|
| Ce que PISTE affiche | À utiliser |
|
||||||
|
|---|---|
|
||||||
|
| **Client ID** / **Client secret** | ✅ oui |
|
||||||
|
| API key / API key secret | ❌ non — `invalid_client` |
|
||||||
|
|
||||||
|
Second piège : les identifiants de production ne fonctionnent pas en bac à sable, qui exige une application distincte.
|
||||||
|
|
||||||
|
## Sauvegarde
|
||||||
|
|
||||||
|
La sauvegarde YunoHost couvre la base et la configuration. À la main :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sqlite3 __DATA_DIR__/veille.db ".backup /sauvegardes/veille-$(date +%F).db"
|
||||||
|
```
|
||||||
|
|
||||||
|
N'utilisez jamais `cp` pendant un passage : le mode WAL laisse des fichiers annexes que `.backup` gère correctement.
|
||||||
|
|
||||||
|
## Diagnostic
|
||||||
|
|
||||||
|
| Symptôme | Piste |
|
||||||
|
|---|---|
|
||||||
|
| Page blanche, 502 | `systemctl status __APP__` puis `tail /var/log/__APP__/__APP__.log` |
|
||||||
|
| « Base introuvable » | La base a disparu de `__DATA_DIR__`. Rejouez le remplissage (voir plus bas). |
|
||||||
|
| Formulaire de recherche refusé | `ORIGIN` dans `.env` ne correspond plus au domaine servi |
|
||||||
|
| Aucun texte nouveau depuis des semaines | Normal hors session parlementaire. Vérifiez avec `--dry-run`. |
|
||||||
|
| `invalid_client` dans le journal | Clé d'API utilisée au lieu du couple OAuth, ou application non abonnée à l'API Légifrance |
|
||||||
|
|
||||||
|
### Reconstruire la base depuis le corpus
|
||||||
|
|
||||||
|
Le corpus de recherche est embarqué dans l'archive : la base peut toujours être reconstruite, sans réseau.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd __INSTALL_DIR__
|
||||||
|
sudo -u __APP__ bash -c 'set -a; . ./.env; set +a; \
|
||||||
|
./venv/bin/python -m pipeline.seed_from_research --repartir-de-zero'
|
||||||
|
systemctl restart __APP__
|
||||||
|
```
|
||||||
|
|
||||||
|
Vous perdrez l'historique des passages, pas les 52 textes du corpus.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
**Vèy Lalwa** tracks French legislation — acts, bills, ordinances, Constitutional Council decisions — through one specific lens: what it changes for the **freedoms of individuals, associations and businesses**, with a focus on Guadeloupe and the French overseas territories.
|
||||||
|
|
||||||
|
Laws restricting or extending freedoms rarely arrive in one piece. They come as an article buried in a simplification act, a rider struck down in a budget act, an implementing decree six months later. Following that takes time few associations and small organisations have.
|
||||||
|
|
||||||
|
### What it does
|
||||||
|
|
||||||
|
- **Full-text search** with combinable facets — status, topic, Guadeloupe relevance, effect on freedoms — whose state lives in the URL, so it can be shared.
|
||||||
|
- **Detailed records**: summary, key points, dated parliamentary history, effects per audience, and the **list of sources with verbatim excerpts**.
|
||||||
|
- **Calendar** of expected decisions, entries into force and parliamentary deadlines.
|
||||||
|
- **Open API**, no key required, to reuse the data elsewhere.
|
||||||
|
|
||||||
|
### What it does not do
|
||||||
|
|
||||||
|
- No invented legislative data. Every fact carries its source, date and confidence level; anything unestablished is flagged "to be verified", with the reason shown.
|
||||||
|
- No legal advice: a quoted text is no substitute for a lawyer.
|
||||||
|
- No trackers, no analytics cookies, no third-party requests — fonts included.
|
||||||
|
|
||||||
|
### How it stays current
|
||||||
|
|
||||||
|
A pipeline queries Légifrance, the Sénat's list of promulgated acts and the Constitutional Council's docket twice a day. Each source is queried independently: if one is unavailable, the others carry on. A detected status change becomes a dated event in the text's history.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
**Vèy Lalwa** suit la production législative française — lois, projets et propositions de loi, ordonnances, décisions du Conseil constitutionnel — et la lit sous un angle précis : ce qu'elle change aux **libertés des individus, des associations et des entreprises**, avec un zoom Guadeloupe et outre-mer.
|
||||||
|
|
||||||
|
Les textes qui restreignent ou étendent des libertés arrivent rarement d'un bloc. Ils passent par un article dans une loi de simplification, un cavalier censuré dans une loi de finances, un décret d'application six mois plus tard. Suivre cela demande un temps que peu d'associations et de petites structures ont.
|
||||||
|
|
||||||
|
### Ce que fait l'application
|
||||||
|
|
||||||
|
- **Recherche plein texte** avec filtres combinables — statut, thème, pertinence Guadeloupe, effet sur les libertés — dont l'état vit dans l'URL, donc se partage.
|
||||||
|
- **Fiches détaillées** : résumé, points clés, parcours parlementaire daté, effets par public, et la **liste des sources avec leurs extraits verbatim**.
|
||||||
|
- **Calendrier** des décisions attendues, entrées en vigueur et échéances parlementaires.
|
||||||
|
- **Interface de programmation** ouverte, sans clé, pour reprendre les données ailleurs.
|
||||||
|
|
||||||
|
### Ce qu'elle ne fait pas
|
||||||
|
|
||||||
|
- Aucune donnée législative inventée. Chaque fait porte sa source, sa date et son niveau de confiance ; ce qui n'est pas établi est marqué « à vérifier », avec son motif affiché.
|
||||||
|
- Aucun conseil juridique : un texte cité ne remplace pas un avocat.
|
||||||
|
- Aucun traceur, aucun cookie de mesure, aucune requête vers un service tiers — polices comprises.
|
||||||
|
|
||||||
|
### Comment elle se tient à jour
|
||||||
|
|
||||||
|
Un pipeline interroge deux fois par jour Légifrance, la liste des lois promulguées du Sénat et le registre des affaires du Conseil constitutionnel. Chaque source est interrogée séparément : si l'une est indisponible, les autres continuent. Un changement de statut détecté devient un événement daté dans le parcours du texte.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
Vèy Lalwa is installed at https://__DOMAIN__
|
||||||
|
|
||||||
|
The database already holds **52 texts and 360 sources** from the research corpus, closed on 25 July 2026. The first collection run happens automatically at 6 am or 6 pm; to trigger it right away:
|
||||||
|
|
||||||
|
```
|
||||||
|
systemctl start __APP__-pipeline.service
|
||||||
|
journalctl -u __APP__-pipeline -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### If you did not provide Légifrance credentials
|
||||||
|
|
||||||
|
That is not a blocker. The Légifrance collector disables itself and says so in the run report; the Sénat and the Constitutional Council take over, which is enough to track promulgations and constitutional decisions.
|
||||||
|
|
||||||
|
To add them later, edit `__INSTALL_DIR__/.env` and trigger a run. **Note**: a PISTE application exposes two pairs of values, and only the **OAuth** pair ("Client ID" / "Client secret") works. The API key is rejected by the token endpoint.
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
Vèy Lalwa est installé sur https://__DOMAIN__
|
||||||
|
|
||||||
|
La base contient déjà **52 textes et 360 sources**, issus du corpus de recherche arrêté au 25 juillet 2026. Le premier passage de collecte a lieu automatiquement à 6 h ou 18 h ; pour le déclencher tout de suite :
|
||||||
|
|
||||||
|
```
|
||||||
|
systemctl start __APP__-pipeline.service
|
||||||
|
journalctl -u __APP__-pipeline -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Si vous n'avez pas fourni d'identifiants Légifrance
|
||||||
|
|
||||||
|
Ce n'est pas bloquant. Le collecteur Légifrance se désactive et l'annonce dans le rapport de passage ; le Sénat et le Conseil constitutionnel prennent le relais, ce qui suffit à suivre les promulgations et les décisions constitutionnelles. Ce que vous perdez : la confirmation de l'intitulé officiel au Journal officiel et l'identifiant JORF de chaque texte.
|
||||||
|
|
||||||
|
Pour les ajouter plus tard, éditez `__INSTALL_DIR__/.env` puis relancez un passage. **Attention** : une application PISTE expose deux couples de valeurs, et seul le couple **OAuth** (« Client ID » / « Client secret ») fonctionne. La clé d'API est refusée par le point d'accès du jeton.
|
||||||
|
|
||||||
|
### Vérifier que tout tourne
|
||||||
|
|
||||||
|
```
|
||||||
|
systemctl status __APP__
|
||||||
|
systemctl list-timers __APP__-pipeline.timer
|
||||||
|
tail -f /var/log/__APP__/pipeline.log
|
||||||
|
```
|
||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
#:schema https://raw.githubusercontent.com/YunoHost/apps/master/schemas/manifest.v2.schema.json
|
||||||
|
|
||||||
|
packaging_format = 2
|
||||||
|
|
||||||
|
id = "veyelalwa"
|
||||||
|
name = "Vèy Lalwa"
|
||||||
|
description.en = "Legislative watch on French law and its effects on the freedoms of individuals, associations and businesses, with a Guadeloupe focus"
|
||||||
|
description.fr = "Veille législative sur le droit français et ses effets sur les libertés des individus, des associations et des entreprises, avec un zoom Guadeloupe"
|
||||||
|
|
||||||
|
version = "1.0.1~ynh1"
|
||||||
|
|
||||||
|
maintainers = []
|
||||||
|
|
||||||
|
[upstream]
|
||||||
|
license = "AGPL-3.0-or-later"
|
||||||
|
website = "https://o-k-i.net"
|
||||||
|
code = "https://labola.o-k-i.net/cyber-mawonaj/veye-lalwa"
|
||||||
|
admindoc = "https://labola.o-k-i.net/cyber-mawonaj/veye-lalwa/src/branch/main/README-pipeline.md"
|
||||||
|
fund = "https://liberapay.com/OKI/donate/"
|
||||||
|
|
||||||
|
[integration]
|
||||||
|
yunohost = ">= 12.1"
|
||||||
|
helpers_version = "2.1"
|
||||||
|
architectures = "all"
|
||||||
|
multi_instance = true
|
||||||
|
|
||||||
|
ldap = "not_relevant"
|
||||||
|
sso = "not_relevant"
|
||||||
|
|
||||||
|
# L'installation construit l'application web (npm) et l'environnement Python du
|
||||||
|
# pipeline, puis remplit la base depuis le corpus de recherche embarqué.
|
||||||
|
disk = "700M"
|
||||||
|
ram.build = "1G"
|
||||||
|
# À l'exécution : Node en veille socket-like via Restart, plus un passage de
|
||||||
|
# collecte deux fois par jour.
|
||||||
|
ram.runtime = "200M"
|
||||||
|
|
||||||
|
[install]
|
||||||
|
|
||||||
|
[install.domain]
|
||||||
|
type = "domain"
|
||||||
|
# Pas de question « path » : l'application occupe la racine d'un domaine.
|
||||||
|
# Les liens internes sont absolus (/recherche, /textes/…) et SvelteKit
|
||||||
|
# exigerait de recompiler avec `paths.base` pour tenir sur un sous-chemin.
|
||||||
|
# Mieux vaut une contrainte annoncée qu'un sous-chemin à moitié fonctionnel.
|
||||||
|
|
||||||
|
[install.init_main_permission]
|
||||||
|
type = "group"
|
||||||
|
default = "visitors"
|
||||||
|
# Une veille législative n'a d'intérêt que publique : c'est un travail
|
||||||
|
# d'information citoyenne, pas un outil interne.
|
||||||
|
|
||||||
|
[install.legifrance_client_id]
|
||||||
|
ask.en = "Légifrance API client ID (PISTE)"
|
||||||
|
ask.fr = "Identifiant client de l'API Légifrance (PISTE)"
|
||||||
|
help.en = "From https://piste.gouv.fr — declare an application AND subscribe it to the « Légifrance » API. Use the OAuth pair (Client ID / Client secret), NOT the API key, which is rejected by the token endpoint. Leave empty: the pipeline then falls back on the Sénat and Conseil constitutionnel, which is enough to track promulgations and constitutional decisions."
|
||||||
|
help.fr = "Depuis https://piste.gouv.fr — déclarez une application ET abonnez-la à l'API « Légifrance ». Utilisez le couple OAuth (Client ID / Client secret), PAS la clé d'API, refusée par le point d'accès du jeton. Laissez vide : le pipeline se replie alors sur le Sénat et le Conseil constitutionnel, ce qui suffit à suivre les promulgations et les décisions."
|
||||||
|
type = "string"
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[install.legifrance_client_secret]
|
||||||
|
ask.en = "Légifrance API client secret (PISTE)"
|
||||||
|
ask.fr = "Secret client de l'API Légifrance (PISTE)"
|
||||||
|
help.en = "The « Client secret » shown next to the Client ID. Leave empty if you left the ID empty."
|
||||||
|
help.fr = "Le « Client secret » affiché à côté du Client ID. Laissez vide si vous avez laissé l'identifiant vide."
|
||||||
|
type = "password"
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[install.contact_email]
|
||||||
|
ask.en = "Contact address for the collectors' user-agent"
|
||||||
|
ask.fr = "Adresse de contact pour le user-agent des collecteurs"
|
||||||
|
help.en = "Sent with every request to Légifrance, the Sénat and the Conseil constitutionnel so their administrators can reach you. Presenting oneself honestly is the price of scraping public services."
|
||||||
|
help.fr = "Envoyée à chaque requête vers Légifrance, le Sénat et le Conseil constitutionnel, pour que leurs administrateurs puissent vous joindre. Se présenter honnêtement est la contrepartie de l'interrogation de services publics."
|
||||||
|
type = "email"
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[resources]
|
||||||
|
|
||||||
|
[resources.sources.main]
|
||||||
|
url = "https://labola.o-k-i.net/cyber-mawonaj/veye-lalwa/archive/v1.0.1.tar.gz"
|
||||||
|
sha256 = "632e534fbcda5f5ebe771b9383f3807b8bf1a5a8e0fb24f7a22b77772fe75a94"
|
||||||
|
autoupdate.strategy = "latest_forgejo_tag"
|
||||||
|
|
||||||
|
[resources.system_user]
|
||||||
|
|
||||||
|
[resources.install_dir]
|
||||||
|
|
||||||
|
# Répertoire persistant : la base SQLite, le cache HTTP et l'export JSON y
|
||||||
|
# vivent. Sans lui, `ynh_setup_source --full_replace` effacerait à chaque
|
||||||
|
# mise à jour tout l'historique de veille accumulé.
|
||||||
|
[resources.data_dir]
|
||||||
|
|
||||||
|
[resources.permissions]
|
||||||
|
main.url = "/"
|
||||||
|
|
||||||
|
[resources.ports]
|
||||||
|
|
||||||
|
[resources.apt]
|
||||||
|
# sqlite3 : diagnostic et sauvegarde à chaud (.backup).
|
||||||
|
# python3-venv : environnement du pipeline.
|
||||||
|
# build-essential + python3-dev : repli de compilation de better-sqlite3
|
||||||
|
# quand aucun binaire précompilé ne correspond à l'architecture.
|
||||||
|
packages = "ca-certificates, sqlite3, python3, python3-venv, python3-dev, build-essential"
|
||||||
|
|
||||||
|
[resources.nodejs]
|
||||||
|
version = "22"
|
||||||
Executable
+113
@@ -0,0 +1,113 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# FONCTIONS COMMUNES AUX SCRIPTS DU PAQUET
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
# Description affichée dans la liste des services YunoHost.
|
||||||
|
readonly DESCRIPTION_SERVICE="Vèy Lalwa — veille législative Gwadloup"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# CONSTRUCTION DE L'APPLICATION WEB
|
||||||
|
#=================================================
|
||||||
|
construire_application_web() {
|
||||||
|
pushd "$install_dir/web" || ynh_die "Répertoire web/ absent de l'archive"
|
||||||
|
|
||||||
|
# `npm ci` respecte le verrou de versions ; `npm install` ne le
|
||||||
|
# garantit pas et rendrait deux installations non reproductibles.
|
||||||
|
ynh_hide_warnings ynh_exec_as_app npm ci
|
||||||
|
|
||||||
|
# Les polices Archivo et Inter sont copiées depuis node_modules vers
|
||||||
|
# static/. Sans cette étape, la page appellerait des polices absentes —
|
||||||
|
# et la politique de sécurité de contenu interdit tout hôte tiers, donc
|
||||||
|
# aucun repli n'est possible.
|
||||||
|
ynh_hide_warnings ynh_exec_as_app npm run polices
|
||||||
|
|
||||||
|
ynh_hide_warnings ynh_exec_as_app npm run build
|
||||||
|
|
||||||
|
# Les dépendances de développement pèsent l'essentiel de node_modules et
|
||||||
|
# ne servent plus une fois le build produit. On garde better-sqlite3,
|
||||||
|
# qui est une dépendance d'exécution.
|
||||||
|
ynh_hide_warnings ynh_exec_as_app npm prune --omit=dev
|
||||||
|
popd || true
|
||||||
|
}
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# ENVIRONNEMENT PYTHON DU PIPELINE
|
||||||
|
#=================================================
|
||||||
|
construire_environnement_python() {
|
||||||
|
# Environnement virtuel dédié : on n'installe jamais dans le Python système
|
||||||
|
# d'une machine YunoHost, qui sert aussi à YunoHost lui-même.
|
||||||
|
ynh_exec_as_app python3 -m venv "$install_dir/venv"
|
||||||
|
|
||||||
|
ynh_hide_warnings ynh_exec_as_app "$install_dir/venv/bin/pip" install \
|
||||||
|
--upgrade pip setuptools wheel
|
||||||
|
|
||||||
|
# Installation depuis le pyproject embarqué, sans les extras de
|
||||||
|
# développement : ni pytest ni ruff n'ont leur place en production.
|
||||||
|
pushd "$install_dir" || ynh_die "Répertoire d'installation introuvable"
|
||||||
|
ynh_hide_warnings ynh_exec_as_app "$install_dir/venv/bin/pip" install .
|
||||||
|
popd || true
|
||||||
|
}
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# BASE DE DONNÉES
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
# Vrai si la base existe déjà et contient des textes.
|
||||||
|
base_deja_remplie() {
|
||||||
|
[ -s "$data_dir/veille.db" ] &&
|
||||||
|
[ "$(sqlite3 "$data_dir/veille.db" \
|
||||||
|
"SELECT COUNT(*) FROM textes" 2>/dev/null || echo 0)" -gt 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remplit la base depuis le corpus de recherche embarqué dans l'archive.
|
||||||
|
# Idempotent : relancer le seed ne duplique rien.
|
||||||
|
remplir_la_base() {
|
||||||
|
pushd "$install_dir" || ynh_die "Répertoire d'installation introuvable"
|
||||||
|
ynh_exec_as_app env \
|
||||||
|
VEILLE_RACINE="$install_dir" \
|
||||||
|
VEILLE_DB="$data_dir/veille.db" \
|
||||||
|
VEILLE_CACHE="$data_dir/cache_http" \
|
||||||
|
VEILLE_DUMP_JSON="$data_dir/textes.json" \
|
||||||
|
"$install_dir/venv/bin/python" -m pipeline.seed_from_research
|
||||||
|
popd || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Applique les migrations en attente, sans toucher aux données.
|
||||||
|
migrer_la_base() {
|
||||||
|
pushd "$install_dir" || ynh_die "Répertoire d'installation introuvable"
|
||||||
|
ynh_exec_as_app env \
|
||||||
|
VEILLE_RACINE="$install_dir" \
|
||||||
|
VEILLE_DB="$data_dir/veille.db" \
|
||||||
|
"$install_dir/venv/bin/python" -m pipeline.db
|
||||||
|
popd || true
|
||||||
|
}
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# UNITÉS SYSTEMD SUPPLÉMENTAIRES
|
||||||
|
#=================================================
|
||||||
|
# `ynh_config_add_systemd` ne gère que l'unité principale `$app.service`. Le
|
||||||
|
# passage de collecte et son minuteur sont donc posés à la main.
|
||||||
|
|
||||||
|
installer_le_minuteur() {
|
||||||
|
ynh_config_add --template="pipeline.service" \
|
||||||
|
--destination="/etc/systemd/system/$app-pipeline.service"
|
||||||
|
ynh_config_add --template="pipeline.timer" \
|
||||||
|
--destination="/etc/systemd/system/$app-pipeline.timer"
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable "$app-pipeline.timer" --quiet
|
||||||
|
systemctl start "$app-pipeline.timer"
|
||||||
|
}
|
||||||
|
|
||||||
|
retirer_le_minuteur() {
|
||||||
|
if systemctl list-unit-files "$app-pipeline.timer" --quiet >/dev/null 2>&1; then
|
||||||
|
systemctl stop "$app-pipeline.timer" 2>/dev/null || true
|
||||||
|
systemctl disable "$app-pipeline.timer" --quiet 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
ynh_safe_rm "/etc/systemd/system/$app-pipeline.timer"
|
||||||
|
ynh_safe_rm "/etc/systemd/system/$app-pipeline.service"
|
||||||
|
systemctl daemon-reload
|
||||||
|
}
|
||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# IMPORT GENERIC HELPERS
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
source _common.sh
|
||||||
|
source /usr/share/yunohost/helpers
|
||||||
|
|
||||||
|
ynh_print_info "Déclaration des fichiers à sauvegarder…"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# BACKUP THE APP MAIN DIR
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
ynh_backup "$install_dir"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# BACKUP THE DATA DIR
|
||||||
|
#=================================================
|
||||||
|
# C'est ici que vit tout le travail de veille accumulé : la base, son historique
|
||||||
|
# de passages et son cache. Le reste se reconstruit, pas ça.
|
||||||
|
#
|
||||||
|
# Le cache HTTP est volontairement exclu : il se régénère seul, se périme en
|
||||||
|
# six heures, et alourdirait l'archive pour rien.
|
||||||
|
|
||||||
|
ynh_backup "$data_dir/veille.db"
|
||||||
|
[ -f "$data_dir/textes.json" ] && ynh_backup "$data_dir/textes.json"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# BACKUP SYSTEM CONFIGURATION
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf"
|
||||||
|
ynh_backup "/etc/systemd/system/$app.service"
|
||||||
|
ynh_backup "/etc/systemd/system/$app-pipeline.service"
|
||||||
|
ynh_backup "/etc/systemd/system/$app-pipeline.timer"
|
||||||
|
ynh_backup "/etc/logrotate.d/$app"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# END OF SCRIPT
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
ynh_print_info "Sauvegarde de $app déclarée. (YunoHost copie maintenant ces fichiers dans l'archive.)"
|
||||||
Executable
+49
@@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# IMPORT GENERIC HELPERS
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
source _common.sh
|
||||||
|
source /usr/share/yunohost/helpers
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# STOP SYSTEMD SERVICE
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Arrêt de $app…"
|
||||||
|
|
||||||
|
ynh_systemctl --service="$app" --action="stop" --log_path="/var/log/$app/$app.log"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# MODIFY URL IN NGINX CONF
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Mise à jour de la configuration NGINX…"
|
||||||
|
|
||||||
|
ynh_config_change_url_nginx
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# UPDATE THE CONFIGURATION
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Mise à jour de la configuration de $app…"
|
||||||
|
|
||||||
|
# $domain porte désormais le nouveau domaine. Deux valeurs en dépendent :
|
||||||
|
# ORIGIN, sans lequel adapter-node refuserait les soumissions de formulaire, et
|
||||||
|
# le user-agent des collecteurs, qui annonce l'adresse du site aux services
|
||||||
|
# publics interrogés.
|
||||||
|
ynh_config_add --template="env" --destination="$install_dir/.env"
|
||||||
|
chown "$app:$app" "$install_dir/.env"
|
||||||
|
chmod 600 "$install_dir/.env"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# START SYSTEMD SERVICE
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Démarrage de $app…"
|
||||||
|
|
||||||
|
ynh_systemctl --service="$app" --action="start" --wait_until="Listening" \
|
||||||
|
--log_path="/var/log/$app/$app.log"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# END OF SCRIPT
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
ynh_script_progression "Changement d'URL de $app terminé"
|
||||||
Executable
+80
@@ -0,0 +1,80 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# IMPORT GENERIC HELPERS
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
source _common.sh
|
||||||
|
source /usr/share/yunohost/helpers
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# DOWNLOAD, CHECK AND UNPACK SOURCE
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Mise en place des fichiers…"
|
||||||
|
|
||||||
|
ynh_setup_source --dest_dir="$install_dir"
|
||||||
|
|
||||||
|
chown -R "$app:$app" "$install_dir"
|
||||||
|
chown -R "$app:$app" "$data_dir"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# APP INITIAL CONFIGURATION
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Écriture de la configuration de $app…"
|
||||||
|
|
||||||
|
# Le fichier .env est écrit AVANT la construction : les deux services le
|
||||||
|
# lisent, et le pipeline en tire l'emplacement de la base.
|
||||||
|
ynh_config_add --template="env" --destination="$install_dir/.env"
|
||||||
|
chown "$app:$app" "$install_dir/.env"
|
||||||
|
chmod 600 "$install_dir/.env"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# BUILD THE APP
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Construction de l'application web (quelques minutes)…"
|
||||||
|
|
||||||
|
construire_application_web
|
||||||
|
|
||||||
|
ynh_script_progression "Installation de l'environnement Python du pipeline…"
|
||||||
|
|
||||||
|
construire_environnement_python
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# SEED THE DATABASE
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Remplissage de la base depuis le corpus de recherche…"
|
||||||
|
|
||||||
|
remplir_la_base
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# SYSTEM CONFIGURATION
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Configuration système…"
|
||||||
|
|
||||||
|
mkdir -p "/var/log/$app"
|
||||||
|
chown -R "$app:$app" "/var/log/$app"
|
||||||
|
|
||||||
|
ynh_config_add_nginx
|
||||||
|
|
||||||
|
ynh_config_add_systemd
|
||||||
|
yunohost service add "$app" --description="$DESCRIPTION_SERVICE" \
|
||||||
|
--log="/var/log/$app/$app.log"
|
||||||
|
|
||||||
|
# Passage de collecte et son minuteur — deux fois par jour, plus le dimanche.
|
||||||
|
installer_le_minuteur
|
||||||
|
|
||||||
|
ynh_config_add_logrotate
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# START SYSTEMD SERVICE
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Démarrage de $app…"
|
||||||
|
|
||||||
|
ynh_systemctl --service="$app" --action="start" --wait_until="Listening" \
|
||||||
|
--log_path="/var/log/$app/$app.log"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# END OF SCRIPT
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
ynh_script_progression "Installation de $app terminée"
|
||||||
Executable
+35
@@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# IMPORT GENERIC HELPERS
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
source _common.sh
|
||||||
|
source /usr/share/yunohost/helpers
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# REMOVE SYSTEM CONFIGURATION
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Retrait de la configuration système de $app…"
|
||||||
|
|
||||||
|
# Le minuteur d'abord : inutile de déclencher un passage de collecte pendant
|
||||||
|
# qu'on démonte le reste.
|
||||||
|
retirer_le_minuteur
|
||||||
|
|
||||||
|
ynh_config_remove_logrotate
|
||||||
|
|
||||||
|
if ynh_hide_warnings yunohost service status "$app" >/dev/null; then
|
||||||
|
yunohost service remove "$app"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ynh_config_remove_systemd
|
||||||
|
|
||||||
|
ynh_config_remove_nginx
|
||||||
|
|
||||||
|
ynh_safe_rm "/var/log/$app"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# END OF SCRIPT
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
ynh_script_progression "Suppression de $app terminée"
|
||||||
Executable
+71
@@ -0,0 +1,71 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# IMPORT GENERIC HELPERS
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
source _common.sh
|
||||||
|
source /usr/share/yunohost/helpers
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# RESTORE THE APP MAIN DIR
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Restauration des fichiers de $app…"
|
||||||
|
|
||||||
|
ynh_restore "$install_dir"
|
||||||
|
|
||||||
|
chown -R "$app:$app" "$install_dir"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# RESTORE THE DATA DIR
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Restauration de la base de veille…"
|
||||||
|
|
||||||
|
ynh_restore "$data_dir/veille.db"
|
||||||
|
ynh_restore "$data_dir/textes.json"
|
||||||
|
|
||||||
|
mkdir -p "$data_dir/cache_http"
|
||||||
|
chown -R "$app:$app" "$data_dir"
|
||||||
|
|
||||||
|
# Le schéma peut avoir évolué entre la sauvegarde et la restauration : les
|
||||||
|
# migrations sont rejouées, elles sont idempotentes.
|
||||||
|
migrer_la_base
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# RESTORE SYSTEM CONFIGURATION
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Restauration de la configuration système…"
|
||||||
|
|
||||||
|
ynh_restore "/etc/nginx/conf.d/$domain.d/$app.conf"
|
||||||
|
|
||||||
|
ynh_restore "/etc/systemd/system/$app.service"
|
||||||
|
systemctl enable "$app.service" --quiet
|
||||||
|
|
||||||
|
ynh_restore "/etc/systemd/system/$app-pipeline.service"
|
||||||
|
ynh_restore "/etc/systemd/system/$app-pipeline.timer"
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable "$app-pipeline.timer" --quiet
|
||||||
|
systemctl start "$app-pipeline.timer"
|
||||||
|
|
||||||
|
yunohost service add "$app" --description="$DESCRIPTION_SERVICE" \
|
||||||
|
--log="/var/log/$app/$app.log"
|
||||||
|
|
||||||
|
mkdir -p "/var/log/$app"
|
||||||
|
chown -R "$app:$app" "/var/log/$app"
|
||||||
|
ynh_restore "/etc/logrotate.d/$app"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# RELOAD NGINX AND START THE APP SERVICE
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Rechargement de NGINX et démarrage de $app…"
|
||||||
|
|
||||||
|
ynh_systemctl --service="$app" --action="start" --wait_until="Listening" \
|
||||||
|
--log_path="/var/log/$app/$app.log"
|
||||||
|
|
||||||
|
ynh_systemctl --service=nginx --action=reload
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# END OF SCRIPT
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
ynh_script_progression "Restauration de $app terminée"
|
||||||
Executable
+97
@@ -0,0 +1,97 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# IMPORT GENERIC HELPERS
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
source _common.sh
|
||||||
|
source /usr/share/yunohost/helpers
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# STOP SYSTEMD SERVICE
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Arrêt de $app…"
|
||||||
|
|
||||||
|
# Le minuteur est arrêté aussi : un passage de collecte qui démarrerait pendant
|
||||||
|
# le remplacement des fichiers travaillerait sur un arbre à moitié écrit.
|
||||||
|
systemctl stop "$app-pipeline.timer" 2>/dev/null || true
|
||||||
|
|
||||||
|
ynh_systemctl --service="$app" --action="stop" --log_path="/var/log/$app/$app.log"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# DOWNLOAD, CHECK AND UNPACK SOURCE
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Mise à jour des fichiers…"
|
||||||
|
|
||||||
|
# --full_replace efface intégralement $install_dir. C'est sans risque ici : la
|
||||||
|
# base, le cache et l'export vivent dans $data_dir, qui n'est pas touché.
|
||||||
|
ynh_setup_source --dest_dir="$install_dir" --full_replace
|
||||||
|
|
||||||
|
chown -R "$app:$app" "$install_dir"
|
||||||
|
chown -R "$app:$app" "$data_dir"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# UPDATE CONFIGURATION
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Mise à jour de la configuration…"
|
||||||
|
|
||||||
|
ynh_config_add --template="env" --destination="$install_dir/.env"
|
||||||
|
chown "$app:$app" "$install_dir/.env"
|
||||||
|
chmod 600 "$install_dir/.env"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# REBUILD
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Reconstruction de l'application web (quelques minutes)…"
|
||||||
|
|
||||||
|
construire_application_web
|
||||||
|
|
||||||
|
ynh_script_progression "Reconstruction de l'environnement Python…"
|
||||||
|
|
||||||
|
construire_environnement_python
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# DATABASE
|
||||||
|
#=================================================
|
||||||
|
if base_deja_remplie; then
|
||||||
|
ynh_script_progression "Application des migrations, données conservées…"
|
||||||
|
migrer_la_base
|
||||||
|
else
|
||||||
|
# Cas d'une base perdue ou d'une première mise à jour depuis une version
|
||||||
|
# qui la rangeait ailleurs : on repart du corpus plutôt que de laisser
|
||||||
|
# l'application sans données.
|
||||||
|
ynh_script_progression "Base absente : remplissage depuis le corpus…"
|
||||||
|
remplir_la_base
|
||||||
|
fi
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# REAPPLY SYSTEM CONFIGURATION
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Mise à jour de la configuration système…"
|
||||||
|
|
||||||
|
mkdir -p "/var/log/$app"
|
||||||
|
chown -R "$app:$app" "/var/log/$app"
|
||||||
|
|
||||||
|
ynh_config_add_nginx
|
||||||
|
|
||||||
|
ynh_config_add_systemd
|
||||||
|
yunohost service add "$app" --description="$DESCRIPTION_SERVICE" \
|
||||||
|
--log="/var/log/$app/$app.log"
|
||||||
|
|
||||||
|
installer_le_minuteur
|
||||||
|
|
||||||
|
ynh_config_add_logrotate
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# START SYSTEMD SERVICE
|
||||||
|
#=================================================
|
||||||
|
ynh_script_progression "Démarrage de $app…"
|
||||||
|
|
||||||
|
ynh_systemctl --service="$app" --action="start" --wait_until="Listening" \
|
||||||
|
--log_path="/var/log/$app/$app.log"
|
||||||
|
|
||||||
|
#=================================================
|
||||||
|
# END OF SCRIPT
|
||||||
|
#=================================================
|
||||||
|
|
||||||
|
ynh_script_progression "Mise à jour de $app terminée"
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
test_format = 1.0
|
||||||
|
|
||||||
|
[default]
|
||||||
|
|
||||||
|
# ------------
|
||||||
|
# Checks to run
|
||||||
|
# ------------
|
||||||
|
|
||||||
|
[default.checks]
|
||||||
|
install = 1
|
||||||
|
remove = 1
|
||||||
|
upgrade = 1
|
||||||
|
backup_restore = 1
|
||||||
|
change_url = 1
|
||||||
|
multi_instance = 1
|
||||||
|
port_already_use = 1
|
||||||
|
|
||||||
|
# ------------
|
||||||
|
# Install args
|
||||||
|
# ------------
|
||||||
|
# Les trois questions d'installation sont optionnelles : les tests
|
||||||
|
# s'exécutent sans identifiants Légifrance, ce qui vérifie au passage le
|
||||||
|
# mode dégradé documenté (repli sur le Sénat et le Conseil
|
||||||
|
# constitutionnel).
|
||||||
Reference in New Issue
Block a user