Compare commits

...

27 commits

Author SHA1 Message Date
Alpinux
044d1c45e6 Reprendre la documentation de déploiement, qui vivait dans alpinux-owni
Ce dépôt d'infrastructure décrit la machine ; le déploiement de ce site-ci le
regarde lui, et le tenir ailleurs garantissait qu'il divergerait du code qu'il
décrit.

Repris tel quel depuis alpinux-owni/docs/, où il est supprimé en retour.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SFbwnJurBwTs7x7t93ecku
2026-09-27 13:00:18 +02:00
Alpinux
b13d33a2d5 Guide installer mint : modification des chemins poir les scripts 2026-09-26 00:01:12 +02:00
Cédrix
3c2773c7fa Faire prendre les scripts sur le serveur de la salle, d'abord
Le guide donnait l'adresse Internet en premier et l'adresse locale comme
raccourci. C'est l'inverse de ce qui se passe : en install party, la machine
n'a pas encore de réseau qui sorte, et c'est le serveur de la salle qui répond.
Les trois téléchargements commencent donc par « http://alpicache.lan », le nom
plutôt que l'adresse, puisqu'il suit le réseau si celui-ci doit déménager. Les
quatre noms servis ont été vérifiés sur la machine.

Un encadré donne le repli une fois pour toutes — « alpicache », « apt-proxy »,
« 10.0.0.1 » — cette dernière adresse valant même quand la résolution de noms
n'est pas encore en place. C'était jusqu'ici la seule adresse citée, sans que
rien n'explique laquelle essayer ni pourquoi.

Les adresses Internet, elles, désignent désormais alpinux.scripts, dépôt propre
à ces trois fichiers, sur sa branche « stable » : celle que le serveur
redistribue, et non celle où l'on travaille.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BssvzfDMeXVoUQHEcnavW7
2026-09-26 00:00:52 +02:00
Cédrix
15e50ff49b Compter les caractères et non les octets, dans le tableau des disques
Ces trois correctifs avaient été éprouvés sur alpicache jeudi soir — commit
5d606ab du dépôt du serveur, 22 h 56 — cinq minutes après que ce dépôt-ci ait
repris le relevé. Ils n'ont donc jamais été versés ici, et la version amont,
celle que le serveur redistribue, les ignorait. Les voilà.

« printf %-10s » compte les octets : « mécanique » en pèse dix pour neuf
signes, et le tableau des disques glissait d'un cran à partir de la deuxième
colonne. La fonction « colonne » compte les caractères, ce que Bash sait faire
quand la locale le permet — d'où la locale forcée plutôt qu'espérée.

Le contrôle affiche par ailleurs les chiffres sur lesquels il se fonde, et pas
seulement ce qui cloche : modèle, type, taille, état SMART, heures de marche
pour un disque mécanique ou usure pour un SSD, et les trois compteurs de
secteurs. On peut répondre à qui demande comment se porte son disque, même
quand tout va bien.

Cette version est désormais identique, au seul en-tête d'usage près, à celle
qui tourne sur la machine.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BssvzfDMeXVoUQHEcnavW7
2026-09-25 23:02:56 +02:00
Alpinux
c1a6d1f9a4 Guide installer mint : modification deposition du cadre 2026-09-25 22:37:17 +02:00
7818a39532 Verser le relevé de fiche matérielle dans le dépôt (#12) 2026-09-24 22:40:10 +00:00
6063e32443 Documenter le serveur qu'on emporte en install party (#11) 2026-09-24 22:39:17 +00:00
Alpinux
10110a9d6e Faire télécharger les deux scripts, sans quoi le relevé ne part jamais
install.sh n'appelle le relevé que s'il le trouve à côté de lui, et le
guide ne faisait télécharger qu'install.sh : dans le cas nominal, la
fiche n'aurait jamais été relevée.

Les deux fichiers se prennent maintenant ensemble, et un encadré dit
pourquoi — téléchargé seul, install.sh fonctionne, simplement sans
relevé.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:59:08 +02:00
Alpinux
1f2bb9f6f4 Faire voyager le verdict avec la fiche, au lieu de le refaire au serveur
Sur une machine réelle, un HP 250 G4 affichait devant son propriétaire
« NE PAS INSTALLER EN L'ÉTAT — 976 secteurs en attente », et apparaissait
« sain » sur le tableau de bord. La fiche ne transportait que le compteur
de secteurs réalloués, nul sur ce disque, et le serveur rejugeait
là-dessus. Deux juges, deux verdicts, et c'est le rassurant qu'on lisait.

Le verdict part désormais avec la fiche — champs « verdict » et
« motifs », ces derniers débarrassés de leurs couleurs — et le serveur
les affiche sans rien recalculer. Un seul juge, celui qui a vu le disque.

Les trois compteurs SMART cessent d'être confondus : les secteurs
réalloués disent l'usure et se comptent, ceux en attente et les
illisibles disent une panne en cours, et un seul suffit à déclencher
l'alerte. Ce ne sont pas des secteurs fatigués mais des données que le
disque n'arrive plus à relire. Les deux nouveaux compteurs voyagent aussi
dans la fiche.

Le relevé se chronomètre enfin, sur la collecte seule : la pause du
verdict attend quelqu'un devant l'écran, la mesurer reviendrait à
chronométrer la personne.

Constat, correctifs et version de référence : session alpicache.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:56:13 +02:00
Alpinux
c428d1d040 Reprendre le relevé éprouvé sur machine, avec ses trois correctifs
La session alpicache a essayé le relevé sur une machine réelle et en a
tiré trois défauts, tous vérifiés là-bas :

Le verdict ne sortait qu'en session live. Relancé après installation, le
relevé ne disait donc rien de l'état des disques — précisément ce dont on
voulait parler au propriétaire pendant qu'il est encore là. Il sort
maintenant à tous les coups, sauf en mode --montrer.

Un disque mécanique n'était signalé qu'au-delà de cinq ans de marche, ce
qui laissait passer sans un mot un disque de trois ans — alors que c'est
le premier frein d'une machine par ailleurs correcte. Il se signale
désormais quel que soit son âge, qui est simplement rappelé.

Le verdict défilait sans que personne ne le lise : trois secondes quand
tout va bien, vingt avec des réserves, et il attend une touche — deux
minutes au plus — quand il dit de ne pas installer. Jamais indéfiniment,
ni du tout sans terminal : le script tourne aussi appelé par install.sh.

La détection de session live est reprise dans sa version, plus fine que
celle que j'avais faite : /cdrom et /rofs comptent de nouveau, mais
seulement montés, et pour /cdrom seulement avec un système de fichiers
de média. Un répertoire vide ne prouve plus rien.

Seul l'en-tête reste de moi : le script se prend dans ce dépôt, pas sur
le serveur.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:41:19 +02:00
Alpinux
3d6635fd52 alpi-fiche : ne plus prendre un /cdrom vide pour une session live
L'installateur de Mint laisse derrière lui un /cdrom vide sur la machine
installée. La détection s'y fiait, et déclarait donc « session live » des
machines parfaitement installées — ce qui fausse aussi le chronométrage,
puisqu'il se calcule entre les deux relevés.

Restent les deux signes qui ne mentent pas : la racine montée en overlay
ou squashfs, et boot=casper sur la ligne du noyau. /rofs disparaît avec
/cdrom, n'ayant pas plus de valeur probante.

Constat de la session alpicache, sur une machine réelle. Je n'ai pas pu
le reproduire moi-même — la machine d'essai ne se laissait pas piloter —
mais le changement est sûr dans les deux sens : ces deux signes-là sont
présents sur toute session live, et aucun ne l'est sur un système
installé.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:38:04 +02:00
Alpinux
10f97b5048 Verser le relevé de fiche matérielle dans le dépôt
La demande initiale faisait télécharger un script depuis alpicache et
l'exécuter en root sur la machine de chaque participant. Le relevé est
désormais versionné ici, relu comme le reste, et appelé localement : rien
n'est tiré du réseau, rien n'est exécuté sans avoir été publié.

Le script vient de la session alpicache, éprouvé sur place. Il relève le
modèle, le processeur, la mémoire et le détail de ses connecteurs, les
disques et leur état SMART, puis envoie la fiche au serveur de la salle.
Depuis une session live, il rend en plus un verdict — bon, avec réserves,
ou ne pas installer — que le bénévole peut opposer à un disque qui
faiblit pendant que la machine est encore là.

install.sh l'appelle en tête de fichier, dans sa passe root, et seulement
s'il le trouve à côté de lui : une machine qui rejoue le script chez son
propriétaire n'envoie rien à personne.

L'en-tête du relevé disait « rien qui touche à la personne ». C'était
inexact : la fiche emporte le nom de la machine — souvent un prénom — et
l'adresse matérielle de la carte réseau. Les deux sont maintenant
nommés, dans le script comme dans le guide, avec la consigne qui va
avec : le dire à la personne dont on installe l'ordinateur.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:37:36 +02:00
6a82dbfd81 Ne pas régénérer GRUB quand il est déjà configuré (#15) 2026-09-24 20:34:59 +00:00
Cédrix
369a7f2b82 Ne pas régénérer GRUB quand il est déjà configuré
update-grub lance os-prober, qui sonde toutes les partitions à la recherche
d'autres systèmes, puis régénère la configuration entière : vingt à trente
secondes par machine, payées jusqu'ici même lorsqu'il n'y avait rien à changer.
Sur une install party de quinze machines, cela se voit.

La fonction vérifie donc d'abord, et sort si les deux réglages sont là.

Deux défauts corrigés au passage. Le sed insérait ses lignes avec
l'indentation du script : /etc/default/grub se retrouvait avec des directives
décalées, que GRUB accepte mais qui trompent la lecture quand on vient
déboguer. Et il ne faisait rien du tout si « GRUB_DEFAULT=0 » avait disparu,
ce qui arrive dès qu'un autre outil est passé avant nous — les deux lignes sont
désormais ajoutées si elles manquent, plutôt qu'insérées avant une ligne qui
peut ne plus exister.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XLTJ8DoPfvhZMK5Yxtrfx2
2026-09-24 22:34:39 +02:00
Alpinux
7074cbdc73 Corriger la correspondance des noms de code Mint
Je l'avais écrite de mémoire, et de mémoire elle était fausse : Xia
donnée pour la 22.2, Wilma pour la 22.1, et Zara absente alors que c'est
elle qui occupe la 22.2. Relevé par la session alpicache, recoupé sur
l'historique des versions de Linux Mint.

L'erreur n'était pas anodine ici : c'est par ces noms que le
pré-chargement désigne ce qu'il va tirer. Quelqu'un qui suivait la page
pour préparer une 22.2 aurait garni le cache de la 22.1, et s'en serait
aperçu le samedi matin — précisément ce que l'encadré cherche à éviter.

La correspondance est maintenant donnée en tableau, série 21 comprise.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:20:27 +02:00
Alpinux
7ea3e145a9 Rappeler que le cache ne sert que les versions qu'il connaît
Le 24/09, la première Mint 22.3 de la soirée a tiré 1,3 Go d'Internet
pour 82 Mo servis par le cache : le pré-chargeur visait encore les
versions précédentes. Une version absente de la liste ne profite à
personne, et cela ne se voit qu'au compteur.

Les noms de code sont donnés, puisque c'est par eux que le pré-chargement
désigne les versions : Zena pour la 22.3.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:18:22 +02:00
Alpinux
593eb05fe2 Ajouter la panne du cache qui s'est produite ce soir
apt renvoyait des « 503 Connection closed » en série et une installation
Mint s'est arrêtée là-dessus : le cache acceptait les requêtes sans
savoir où aller les chercher.

Le piège mérite d'être écrit, parce qu'il envoie chercher au mauvais
endroit : interroger le cache directement répondait parfaitement pendant
ce temps. Un bénévole qui teste ainsi conclut que tout va bien et
retourne fouiller la machine du participant.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:07:31 +02:00
Alpinux
a7eb79ceb1 Nommer la vue des noyaux comme le gestionnaire la nomme
« Analyseur de noyaux » n'existe pas : la vue s'ouvre par Affichage →
Noyaux Linux. Écrit de mémoire, vérifié ce soir sur la machine d'essai —
c'est la deuxième fois que cette page décrit une commande qui n'est pas
là, après les niveaux de mise à jour.

Elle s'ouvre d'ailleurs sur un avertissement qui méritait sa place ici :
un noyau plus récent peut coûter le Wi-Fi, l'accélération graphique ou
l'affichage, et la sortie de secours — Maj au démarrage, Advanced
options, noyau antérieur — se connaît avant d'en avoir besoin.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:07:20 +02:00
Alpinux
c2fbd50a7f Documenter le serveur qu'on emporte en install party
Le guide d'installation envoie chercher des scripts sur http://10.0.0.1/
sans jamais dire ce qu'est cette adresse, et le wiki ne mentionnait nulle
part le cache de paquets — alors que c'est lui qui rend possible quinze
installations simultanées sur la ligne d'une salle des fêtes.

La page décrit ce qu'un bénévole doit en savoir sur place : le réseau de
la salle et pourquoi les postes se branchent sur le switch et jamais sur
la box, les trois adresses utiles (page de santé, mandataire du cache,
scripts), les quatre cartes à surveiller, et surtout le geste qui change
tout — le mandataire à saisir pendant l'installation de Debian, contre
auto-apt-proxy installé après coup pour Mint et Ubuntu.

Puis ce qui coince : la salle sans réseau et le téléphone en secours, le
disque du cache à brancher avant d'allumer, et l'adresse en 169.254 qui
trahit un poste qui n'a reçu aucune réponse.

La configuration de la machine reste dans son propre dépôt : cette page
renvoie vers lui plutôt que de le recopier, et n'expose rien de ce qui
touche à son accès.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:07:20 +02:00
Alpinux
6baf484e74 install.sh : afficher le saut de ligne au lieu de l'écrire
« ==> Mise à jour du système\n » s'affichait tel quel, barre-n comprise,
sur l'écran des participants : le echo de cette ligne avait perdu son
-e. Relevé pendant une installation ce soir, à deux jours de l'install
party.

Au passage, « Configuraiton de dconf » devient une ligne de la même
famille que les autres — préfixe ==> et orthographe.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:07:03 +02:00
99fde2aef2 Merge pull request 'Publier aussi l'inventaire complet des pages' (#10) from flux-toutes-les-pages into main
Reviewed-on: #10
2026-09-23 11:22:42 +00:00
Alpinux
c9e2624466 Publier aussi l'inventaire complet des pages
derniers-articles.json s'arrête à vingt entrées : ce qu'il faut pour la
section de l'accueil, trop peu pour la page des mises à jour d'alpinux.org,
qui veut montrer tout ce que le wiki publie. Le build écrit donc un second
fichier, toutes-les-pages.json, sans plafond et avec la rubrique de chaque
page — la section de premier niveau de la navigation, celle à laquelle un
lecteur range les choses.

Un seul parcours de l'historique git produit les deux : le second est la
liste entière, le premier son début. derniers-articles.json ne bouge pas
d'un octet dans sa forme — mêmes quatre clés, même ordre — pour que
l'accueil continue de le lire sans rien savoir de tout ceci.

L'en-tête CORS couvre maintenant les deux fichiers, et seulement eux ; le
bloc n'est écrit que pour ceux qui manquent au .htaccess, de sorte qu'un
second build ne le duplique pas.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JYfYUZRcJmmEWJwAxZb4o2
2026-09-23 12:42:39 +02:00
Alpinux
cf92993ec2 Illustrer les trois guides de la messagerie
Vingt figures, une par écran décrit. Elles sont reconstituées avec la feuille
de style réelle de l'application et des données inventées : photographier une
session aurait demandé le SSO et fait courir le risque d'y laisser de vraies
adresses. Ici l'anonymat est acquis par construction — noms, adresses masquées
et IP de documentation.

Le commit précédent laissait des repères en commentaire à ces endroits ; ils
sont remplacés.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SsLBE8w2R235EG5an6Knb
2026-09-23 10:30:13 +02:00
Alpinux
df1c85e71c Dire comment les adresses sont masquées dans les archives
Elles ne sont plus escamotées mais transformées — « s****@a****.xxx ». Le
lecteur voit qu'un contact existait et peut le demander, au lieu de buter sur
une phrase amputée ; et l'adresse reste hors de portée d'un robot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SsLBE8w2R235EG5an6Knb
2026-09-23 09:26:18 +02:00
Alpinux
7e53572ec7 Merge remote-tracking branch 'origin/doc-derniers-articles' 2026-09-23 09:14:06 +02:00
Alpinux
dec4e1960f Découper la documentation de la messagerie par public
Une seule page obligeait le lecteur à traverser ce qui ne le concernait pas :
quelqu'un qui veut juste s'abonner n'a que faire des plafonds d'envoi, et le
bureau n'a pas besoin qu'on lui explique où cliquer pour se désabonner.

Trois guides, donc, et un sommaire qui aiguille. Chacun se lit seul.

Aucune adresse ni aucun nom réel dans les exemples, aucune URL non plus : les
écrans sont désignés par leurs intitulés de menu, qui ne changent pas quand le
site déménage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SsLBE8w2R235EG5an6Knb
2026-09-23 09:03:09 +02:00
Alpinux
ab24d6f72a Documenter la messagerie et les listes de diffusion
La page dit d'abord ce que font les gens : s'abonner, se désabonner, lire les
archives. Ensuite seulement ce que font le bureau et les administrateurs.

L'inscription mérite son explication : c'est la personne qui nous écrit en
citant un code, et non le site qui lui envoie un lien. On ne devine pas la
raison en voyant l'écran, alors elle est dite — nous n'écrivons jamais à une
adresse non vérifiée, et personne ne peut donc abonner quelqu'un d'autre.

La page « données personnelles » renvoyait à secretaire@ pour s'abonner sans
être adhérent. Ce n'est plus vrai : on s'abonne soi-même.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SsLBE8w2R235EG5an6Knb
2026-09-23 08:38:19 +02:00
36 changed files with 1741 additions and 73 deletions

2
.obsidian/app.json vendored
View file

@ -8,4 +8,4 @@
"alwaysUpdateLinks": true, "alwaysUpdateLinks": true,
"useMarkdownLinks": true, "useMarkdownLinks": true,
"newLinkFormat": "relative" "newLinkFormat": "relative"
} }

View file

@ -0,0 +1,702 @@
#!/bin/bash
# Relève la fiche d'une machine, et l'envoie au serveur de l'install party.
#
# Ce script vit dans le dépôt du wiki, à côté d'install.sh : il est lu et
# versionné comme lui, et c'est de là qu'il faut le prendre.
#
# wget https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-wiki/raw/branch/main/code/linux/linux-mint/alpi-fiche.sh
# chmod +x alpi-fiche.sh
# sudo ./alpi-fiche.sh --check # depuis la session live, avant d'installer
# sudo ./alpi-fiche.sh # après l'installation
#
# Sur le réseau de l'install party, plus court :
#
# wget http://10.0.0.1/alpi-fiche.sh && chmod +x alpi-fiche.sh && sudo ./alpi-fiche.sh --check
#
# Pourquoi la garder sur le serveur plutôt que sur le poste : trois semaines
# plus tard, quand quelqu'un rappelle parce que « ça rame », on n'a plus la
# machine sous la main. Savoir qu'elle a huit ans, quatre gigaoctets de
# mémoire et un disque mécanique qui commence à réallouer des secteurs répond
# à la question avant de l'avoir posée.
#
# Ce qui est envoyé : le modèle, le processeur, la mémoire, les disques et
# leur santé, le système installé.
#
# Deux de ces champs touchent à la personne, et il faut le dire plutôt que de
# prétendre le contraire — la première version de cet en-tête annonçait « rien
# qui touche à la personne », ce qui était faux :
#
# · le NOM DE LA MACHINE, choisi pendant l'installation, contient très
# souvent un prénom ;
# · l'ADRESSE MATÉRIELLE de la carte réseau identifie durablement l'appareil.
#
# Les deux servent : le nom pour s'y retrouver, l'adresse pour rapprocher la
# fiche du bail DHCP. Mais ils appartiennent à la personne dont on installe
# l'ordinateur — il faut le lui dire. Ne sont en revanche jamais envoyés : son
# nom d'utilisateur, ses fichiers, son historique, ni rien de ce qu'elle a
# installé. Le numéro de série est tronqué à quatre caractères.
#
# alpi-fiche relève et envoie
# alpi-fiche --montrer relève et affiche, sans rien envoyer
# alpi-fiche --check relève, envoie, et rend un verdict
#
# Le mode « --check » est fait pour tourner **depuis la clé USB, avant
# d'installer** : c'est le seul moment où l'on peut encore renoncer. Un disque
# qui se déclare défaillant, on ne passe pas une heure à installer dessus — on
# le dit à son propriétaire et on cherche une autre solution.
set -u
# Bash ne compte les caractères — et non les octets — que si la locale le dit.
# C'est ce qui aligne les colonnes du tableau des disques.
export LC_ALL="${LC_ALL:-C.UTF-8}"
# Le temps que met la collecte, en millisecondes.
#
# Une machine n'est relevée qu'une fois quand elle n'a pas connu de session
# live : il n'y a alors pas d'écart entre deux passages à mesurer. Cette durée-
# ci existe toujours, et elle se compare — elle dit ce que la machine met à
# s'interroger elle-même : lire le SMART de ses disques, réveiller ceux qui
# dorment, énumérer ses barrettes. Un plateau qui rame se voit là.
#
# On arrête le chronomètre à la fin de la collecte, avant le verdict : celui-ci
# attend une touche, et l'on mesurerait la personne devant l'écran.
DEBUT_NS="$(date +%s%N 2>/dev/null || echo 0)"
# smartctl et dmidecode vivent dans /usr/sbin, que le PATH d'un shell ordinaire
# ne contient pas toujours — un « command -v » y échouait alors que le binaire
# était là.
PATH="$PATH:/usr/sbin:/sbin"
SERVEUR="${ALPI_SERVEUR:-http://alpicache.lan}"
MONTRER=0
CHECK=0
case "${1:-}" in
--montrer) MONTRER=1 ;;
--check) CHECK=1 ;;
esac
if [ -t 1 ]; then
V=$'\e[32m'; O=$'\e[33m'; R=$'\e[31m'; B=$'\e[1m'; D=$'\e[2m'; Z=$'\e[0m'
else
V=""; O=""; R=""; B=""; D=""; Z=""
fi
# Ce dont le relevé a besoin, et qu'il installe lui-même plutôt que de rendre
# un verdict à trous.
#
# « état des disques inconnu » n'aide personne : c'est précisément la réponse
# qu'on était venu chercher. Les deux paquets pèsent moins de deux mégaoctets,
# ils sont dans le cache, et l'installation prend deux secondes sur le réseau
# de l'install party. On ne le fait qu'en root, qu'avec apt, et sans rien
# demander d'autre.
completer_outils() {
[ "$(id -u)" = "0" ] || return 0
command -v apt-get >/dev/null 2>&1 || return 0
local manquants=""
command -v smartctl >/dev/null 2>&1 || manquants="$manquants smartmontools"
command -v dmidecode >/dev/null 2>&1 || manquants="$manquants dmidecode"
[ -z "$manquants" ] && return 0
echo "==> Installation de ce qui manque au relevé :$manquants"
# Par le cache s'il répond, directement sinon. « -qq » pour ne pas noyer
# l'écran : ce n'est pas l'objet de la manœuvre.
local proxy=""
if command -v wget >/dev/null 2>&1 \
&& wget -q --spider --timeout=3 "$SERVEUR/" 2>/dev/null; then
proxy="-o Acquire::http::Proxy=http://apt-proxy:3142"
fi
DEBIAN_FRONTEND=noninteractive apt-get $proxy -qq -y install $manquants \
>/dev/null 2>&1 \
|| echo " (échec — le relevé se fera sans)"
# Ce qui vient d'arriver est dans /usr/sbin, que le PATH d'un shell
# ordinaire ne contient pas toujours.
PATH="$PATH:/usr/sbin:/sbin"
}
completer_outils
lire_dmi() {
cat "/sys/class/dmi/id/$1" 2>/dev/null | tr -d '\n' | sed 's/^ *//; s/ *$//'
}
echapper() {
# De quoi écrire du JSON sans rien installer : guillemets, antislashs et
# sauts de ligne, et c'est tout ce qui peut nous arriver ici.
printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g' | tr -d '\n\r'
}
champ() { printf '"%s":"%s"' "$1" "$(echapper "${2:-}")"; }
# --- La machine ------------------------------------------------------------
MODELE="$(lire_dmi product_name)"
MARQUE="$(lire_dmi sys_vendor)"
SERIE="$(lire_dmi product_serial)"
# Le numéro de série identifie un objet, pas une personne, mais il n'a rien à
# faire dans un fichier qu'on partage : on n'en garde que les quatre derniers
# caractères, assez pour distinguer deux machines du même modèle.
SERIE="${SERIE: -4}"
BIOS="$(lire_dmi bios_date)"
PROCESSEUR="$(awk -F': ' '/^model name/{print $2; exit}' /proc/cpuinfo)"
COEURS="$(nproc 2>/dev/null || echo '?')"
MEMOIRE_KO="$(awk '/^MemTotal/{print $2}' /proc/meminfo)"
SYSTEME=""
if [ -r /etc/os-release ]; then
SYSTEME="$(. /etc/os-release; echo "${PRETTY_NAME:-$NAME $VERSION}")"
fi
NOYAU="$(uname -r)"
ARCHITECTURE="$(uname -m)"
# Sommes-nous sur la clé USB, ou sur le système installé ?
#
# La distinction n'est pas cosmétique : les deux relevés répondent à deux
# questions différentes. Celui de la session live dit « peut-on installer sur
# cette machine ? » et se fait avant d'effacer quoi que ce soit. Celui d'après
# dit « qu'avons-nous installé, et sur quoi ». Les garder séparés permet de
# comparer — un disque qui allait bien le matin et qui se plaint le soir, cela
# se voit.
#
# Le signe qui ne trompe pas est la racine : une session live la monte en
# overlay au-dessus d'un squashfs, ce qu'aucun système installé ne fait. S'y
# ajoute « boot=casper » ou « boot=live » sur la ligne de commande du noyau.
#
# L'existence de /cdrom ou de /rofs, elle, ne prouve rien : l'installateur de
# Mint laisse un /cdrom vide derrière lui, et la première version de ce script
# a donc déclaré « session live » une machine parfaitement installée. On ne les
# retient que s'ils sont réellement montés.
MOMENT="post-installation"
if findmnt -n -o FSTYPE / 2>/dev/null | grep -qE '^(overlay|aufs|squashfs)$' \
|| grep -qE '(^| )boot=(casper|live)( |$)' /proc/cmdline 2>/dev/null \
|| mountpoint -q /rofs 2>/dev/null \
|| { mountpoint -q /cdrom 2>/dev/null \
&& findmnt -n -o FSTYPE /cdrom 2>/dev/null | grep -qE '^(iso9660|squashfs)$'; }; then
MOMENT="live"
fi
# --- La mémoire, barrette par barrette ------------------------------------
#
# La question qu'on pose vraiment devant une machine lente : peut-on en
# ajouter ? Elle demande trois réponses — combien de connecteurs, combien
# d'occupés, et de quel type. Un slot libre en DDR3 ne se remplit pas avec ce
# qu'on a dans le tiroir.
#
# dmidecode lit cela dans la table du BIOS. Il est présent par défaut sur
# Debian, Ubuntu et Mint ; s'il manque, on ne prétend rien plutôt que de
# deviner.
BARRETTES="["
SLOTS_TOTAL=0
SLOTS_PRIS=0
TYPE_MEMOIRE=""
VITESSE_MEMOIRE=""
if command -v dmidecode >/dev/null 2>&1 && [ "$(id -u)" = "0" ]; then
premier=1
# Chaque bloc « Memory Device » décrit un connecteur, occupé ou non.
while IFS= read -r bloc; do
emplacement="$(printf '%s' "$bloc" | sed -n 's/.*|Locator:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
taille="$(printf '%s' "$bloc" | sed -n 's/.*|Size:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
type_="$(printf '%s' "$bloc" | sed -n 's/.*|Type:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
vitesse="$(printf '%s' "$bloc" | sed -n 's/.*|Speed:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
forme="$(printf '%s' "$bloc" | sed -n 's/.*|Form Factor:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
reference="$(printf '%s' "$bloc" | sed -n 's/.*|Part Number:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
# La vitesse configurée est celle à laquelle la barrette travaille
# vraiment ; « Speed » n'est que ce dont elle serait capable. L'écart
# entre les deux explique bien des machines qui « rament » sans raison
# apparente : une DDR4-3200 bridée à 2133 par la carte mère, ou deux
# barrettes dépareillées qui s'alignent sur la plus lente.
vitesse_reelle="$(printf '%s' "$bloc" | sed -n 's/.*|Configured Memory Speed:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
fabricant="$(printf '%s' "$bloc" | sed -n 's/.*|Manufacturer:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
rang="$(printf '%s' "$bloc" | sed -n 's/.*|Rank:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
tension="$(printf '%s' "$bloc" | sed -n 's/.*|Configured Voltage:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
largeur="$(printf '%s' "$bloc" | sed -n 's/.*|Data Width:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
detail="$(printf '%s' "$bloc" | sed -n 's/.*|Type Detail:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
[ -z "$emplacement" ] && continue
SLOTS_TOTAL=$(( SLOTS_TOTAL + 1 ))
case "$taille" in
*"No Module"*|""|"Unknown") taille="" ;;
*) SLOTS_PRIS=$(( SLOTS_PRIS + 1 ))
[ -z "$TYPE_MEMOIRE" ] && [ "$type_" != "Unknown" ] && TYPE_MEMOIRE="$type_"
# Deux barrettes de vitesses différentes s'alignent sur la plus
# lente : c'est celle-là qu'il faut retenir pour la machine.
if [ -n "$vitesse_reelle" ] && [ "$vitesse_reelle" != "Unknown" ]; then
nombre="${vitesse_reelle%% *}"
case "$nombre" in
''|*[!0-9]*) ;;
*) if [ -z "$VITESSE_MEMOIRE" ] || [ "$nombre" -lt "$VITESSE_MEMOIRE" ]; then
VITESSE_MEMOIRE="$nombre"
fi ;;
esac
fi ;;
esac
[ "$premier" = 0 ] && BARRETTES="$BARRETTES,"
premier=0
BARRETTES="$BARRETTES{$(champ emplacement "$emplacement"),$(champ taille "$taille"),\
$(champ type "$type_"),$(champ vitesse "$vitesse"),$(champ vitesse_reelle "$vitesse_reelle"),\
$(champ forme "$forme"),$(champ reference "$reference"),$(champ fabricant "$fabricant"),\
$(champ rang "$rang"),$(champ tension "$tension"),$(champ largeur "$largeur"),\
$(champ detail "$detail")}"
done < <(dmidecode -t memory 2>/dev/null \
| awk '/^Memory Device$/{bloc=""; dedans=1; next}
/^$/{if (dedans && bloc != "") print bloc; dedans=0; bloc=""}
dedans {gsub(/^[ \t]+/, ""); bloc = bloc "|" $0}
END{if (dedans && bloc != "") print bloc}')
fi
BARRETTES="$BARRETTES]"
TABLE_DISQUES=""
# Remplir une colonne à la bonne largeur.
#
# « printf %-10s » compte les octets, pas les caractères : « mécanique » en
# pèse dix pour neuf signes, et la colonne suivante glisse d'un cran. Bash,
# lui, sait compter les caractères quand la locale est en UTF-8 — on lui
# laisse ce travail, et l'on force la locale plutôt que d'espérer.
colonne() {
local texte="$1" largeur="$2" cote="${3:-gauche}" n
n=${#texte}
if [ "$n" -gt "$largeur" ]; then
printf '%s' "${texte:0:$largeur}"
return
fi
local blancs=""
while [ "$n" -lt "$largeur" ]; do blancs="$blancs "; n=$((n + 1)); done
if [ "$cote" = droite ]; then printf '%s%s' "$blancs" "$texte"
else printf '%s%s' "$texte" "$blancs"; fi
}
# --- Les disques -----------------------------------------------------------
#
# lsblk donne la taille et le type ; le drapeau « rotational » du noyau
# distingue un disque mécanique d'un SSD sans rien installer. C'est la
# distinction qui compte : une machine lente avec un disque mécanique de dix
# ans ne s'arrangera pas avec un système plus léger.
DISQUES="["
premier=1
for chemin in /sys/block/*; do
nom="$(basename "$chemin")"
case "$nom" in loop*|ram*|sr*|zram*|dm-*) continue ;; esac
[ -r "$chemin/size" ] || continue
taille=$(( $(cat "$chemin/size") * 512 ))
[ "$taille" -lt 1000000000 ] && continue # moins d'un Go : pas un disque
rotatif="$(cat "$chemin/queue/rotational" 2>/dev/null || echo '?')"
modele="$(cat "$chemin/device/model" 2>/dev/null | sed 's/ *$//')"
# L'état SMART, s'il est lisible. Sans smartctl on ne prétend rien.
sante=""
reallocations=""
pending=""
illisibles=""
heures=""
usure=""
ecrit=""
if command -v smartctl >/dev/null 2>&1; then
sortie="$(smartctl -H -A "/dev/$nom" 2>/dev/null)"
sante="$(printf '%s' "$sortie" | grep -iE 'overall-health|SMART Health' \
| sed 's/.*: *//' | head -1)"
reallocations="$(printf '%s' "$sortie" \
| awk '/Reallocated_Sector_Ct|Reallocated_Event/{print $10; exit}')"
# Les secteurs que le disque n'arrive plus à lire. Ils manquaient à la
# fiche, et c'est ce qui a fait qu'alpicache annonçait « sain » un
# disque déclaré à remplacer : il ne les avait jamais reçus.
pending="$(printf '%s' "$sortie" | awk '$2 == "Current_Pending_Sector"{print $10; exit}')"
illisibles="$(printf '%s' "$sortie" | awk '$2 == "Offline_Uncorrectable"{print $10; exit}')"
heures="$(printf '%s' "$sortie" | awk '/Power_On_Hours/{print $10; exit}')"
# L'usure d'un SSD, en pour cent de vie consommée.
#
# Un disque mécanique s'use en tournant, un SSD en écrivant : les
# heures de marche ne disent rien de son état. Chaque constructeur a
# son attribut, et deux conventions coexistent — les uns comptent ce
# qui est consommé, les autres ce qui reste. On ramène tout à
# « consommé », qui est le sens qu'on veut lire. NVMe donne directement
# « Percentage Used » ; pour les SATA, la colonne VALUE part de 100 et
# descend.
usure="$(printf '%s' "$sortie" | awk '/Percentage Used:/{gsub(/[^0-9]/,"",$3); print $3; exit}')"
if [ -z "$usure" ]; then
usure="$(printf '%s' "$sortie" | awk '
/Media_Wearout_Indicator|Wear_Leveling_Count|SSD_Life_Left|Percent_Lifetime_Remain/ {
if ($4 ~ /^[0-9]+$/ && $4 <= 100) { print 100 - $4; exit }
}')"
fi
# Ce que le disque a écrit depuis sa mise en service : l'autre façon de
# juger un SSD, dont les modèles courants tiennent 100 à 600 To.
ecrit="$(printf '%s' "$sortie" | awk '
/Total_LBAs_Written|Host_Writes_32MiB|Total_Host_Writes/ {
if ($10 ~ /^[0-9]+$/) { print $10; exit }
}
/Data Units Written:/ { gsub(/[^0-9]/,"",$4); print $4; exit }')"
fi
# Les mêmes chiffres, mis en table pour être lus par quelqu'un. Le verdict
# ne dit que ce qui cloche ; on veut aussi voir ce qui va, et sur quoi le
# verdict se fonde.
taille_go="$(awk -v o="$taille" 'BEGIN{printf "%.1f", o/1000000000}' | tr . ,)"
secteurs="—"
for valeur in "${pending:-0}" "${illisibles:-0}" "${reallocations:-0}"; do
case "$valeur" in ''|*[!0-9]*) continue ;; esac
[ "$valeur" -gt 0 ] && secteurs="!"
done
[ "$secteurs" = "!" ] && secteurs="$(printf '%s/%s/%s' \
"${reallocations:-0}" "${pending:-0}" "${illisibles:-0}")"
usure_dite="—"
if [ "$rotatif" = 1 ]; then
case "$heures" in ''|*[!0-9]*) ;; *)
usure_dite="$heures h"
[ "$heures" -ge 8760 ] && usure_dite="$usure_dite ($(( heures / 8760 )),$(( (heures % 8760) * 10 / 8760 )) an)" ;;
esac
else
case "$usure" in ''|*[!0-9]*) ;; *) usure_dite="usé à $usure %" ;; esac
fi
TABLE_DISQUES="$TABLE_DISQUES$(
colonne "$nom" 7; printf ' '
colonne "${modele:-—}" 20; printf ' '
colonne "$([ "$rotatif" = 1 ] && echo mécanique || echo SSD)" 10; printf ' '
colonne "$taille_go Go" 10 droite; printf ' '
colonne "${sante:-—}" 8; printf ' '
colonne "$usure_dite" 16; printf ' '
printf '%s' "$secteurs")"$'\n'
[ "$premier" = 0 ] && DISQUES="$DISQUES,"
premier=0
DISQUES="$DISQUES{$(champ nom "$nom"),$(champ modele "$modele"),\
\"octets\":$taille,$(champ type "$([ "$rotatif" = 1 ] && echo mécanique || echo SSD)"),\
$(champ sante "$sante"),$(champ reallocations "$reallocations"),\
$(champ pending "$pending"),$(champ illisibles "$illisibles"),$(champ heures "$heures"),\
$(champ usure "$usure"),$(champ ecrit "$ecrit")}"
done
DISQUES="$DISQUES]"
# L'occupation, telle qu'elle est maintenant.
OCCUPATION="["
premier=1
while read -r point taille utilise dispo pourcent; do
[ "$premier" = 0 ] && OCCUPATION="$OCCUPATION,"
premier=0
OCCUPATION="$OCCUPATION{$(champ point "$point"),\"taille\":$taille,\
\"utilise\":$utilise,$(champ pourcent "$pourcent")}"
done < <(df -B1 --output=target,size,used,avail,pcent 2>/dev/null \
| awk 'NR>1 && $2+0 > 1000000000 {print $1, $2, $3, $4, $5}')
OCCUPATION="$OCCUPATION]"
# --- Le rapport ------------------------------------------------------------
# L'adresse matérielle de l'interface par laquelle on sort — celle que le
# serveur a vue en donnant le bail, donc celle qui permettra de rapprocher
# cette fiche de la machine servie. « ip -o link » a failli nous donner
# l'adresse de diffusion, qui est la même pour tout le monde.
INTERFACE="$(ip -o -4 route show default 2>/dev/null | awk '{print $5; exit}')"
[ -z "$INTERFACE" ] && INTERFACE="$(ls /sys/class/net 2>/dev/null \
| grep -v '^lo$' | head -1)"
MAC="$(cat "/sys/class/net/$INTERFACE/address" 2>/dev/null)"
RAPPORT="{$(champ machine "$(hostname)"),$(champ mac "$MAC"),\
$(champ marque "$MARQUE"),$(champ modele "$MODELE"),$(champ serie "$SERIE"),\
$(champ bios "$BIOS"),$(champ processeur "$PROCESSEUR"),\"coeurs\":${COEURS:-0},\
\"memoire_ko\":${MEMOIRE_KO:-0},$(champ systeme "$SYSTEME"),$(champ noyau "$NOYAU"),\
$(champ architecture "$ARCHITECTURE"),$(champ moment "$MOMENT"),\
$(champ type_memoire "$TYPE_MEMOIRE"),\
$(champ vitesse_memoire "$VITESSE_MEMOIRE"),\
\"slots_total\":$SLOTS_TOTAL,\"slots_pris\":$SLOTS_PRIS,\"barrettes\":$BARRETTES,\
\"disques\":$DISQUES,\"occupation\":$OCCUPATION}"
DUREE_MS=0
case "$DEBUT_NS" in
''|*[!0-9]*) ;;
0) ;;
*) FIN_NS="$(date +%s%N 2>/dev/null || echo 0)"
case "$FIN_NS" in
''|*[!0-9]*|0) ;;
*) DUREE_MS=$(( (FIN_NS - DEBUT_NS) / 1000000 )) ;;
esac ;;
esac
[ "$DUREE_MS" -lt 0 ] && DUREE_MS=0
RAPPORT="${RAPPORT%\}},\"duree_releve_ms\":$DUREE_MS}"
if [ "$MONTRER" = 1 ]; then
if command -v python3 >/dev/null 2>&1; then
printf '%s' "$RAPPORT" | python3 -m json.tool 2>/dev/null || printf '%s\n' "$RAPPORT"
else
printf '%s\n' "$RAPPORT"
fi
exit 0
fi
[ "$CHECK" = 1 ] && printf " %sLa fiche est envoyée à alpicache…%s\n\n" "$D" "$Z"
echo "Relevé : $([ "$MOMENT" = live ] && echo "session live, avant installation" \
|| echo "système installé")"
echo "Machine : $MARQUE $MODELE"
echo "Processeur : $PROCESSEUR ($COEURS cœurs)"
if [ "$SLOTS_TOTAL" -gt 0 ]; then
echo "Mémoire : $(( MEMOIRE_KO / 1024 )) Mo ${TYPE_MEMOIRE:+$TYPE_MEMOIRE }\
${VITESSE_MEMOIRE:+à $VITESSE_MEMOIRE MT/s }· \
$SLOTS_PRIS connecteur(s) occupé(s) sur $SLOTS_TOTAL"
else
echo "Mémoire : $(( MEMOIRE_KO / 1024 )) Mo"
fi
echo "Système : $SYSTEME"
echo
MOTIFS=""
# --- Le verdict ------------------------------------------------------------
#
# Trois réponses possibles, et une seule question : peut-on installer sur cette
# machine ? Les seuils viennent de ce qu'on rencontre en install party, pas
# d'un idéal — une machine de dix ans avec 4 Go et un disque sain fait très
# bien l'affaire, et c'est souvent celle qu'on nous apporte.
# Le verdict sort toujours, live ou non. C'est l'information qu'on est venu
# chercher : la première version ne le rendait qu'en session live, et le
# relevé d'après installation défilait sans rien dire de l'état des disques —
# précisément ce dont on voulait parler au propriétaire.
[ "$MONTRER" = 1 ] || CHECK=1
if [ "$CHECK" = 1 ]; then
GRAVITE=0 # 0 rien à signaler · 1 à surveiller · 2 ne pas installer
REMARQUES=""
note() {
[ "$1" -gt "$GRAVITE" ] && GRAVITE="$1"
REMARQUES="$REMARQUES$2"$'\n'
# Le même motif, débarrassé de ses couleurs et de ses retours à la
# ligne, pour qu'alpicache dise exactement ce qui a été dit ici.
if [ "$1" -ge 1 ]; then
# Une note tient sur plusieurs lignes à l'écran ; le motif, lui,
# est d'un seul tenant — il ira dans une case de tableau.
nu="$(printf '%s' "$2" | tr '\n' ' ' | sed -e 's/\x1b\[[0-9;]*m//g' \
-e 's/[[:space:]]\{1,\}/ /g' \
-e 's/^ *[!?✗·] *//' \
-e 's/^ *//' -e 's/ *$//')"
[ -n "$nu" ] && MOTIFS="$MOTIFS$nu"$'\n'
fi
}
# Les disques. C'est pour eux qu'on fait ce contrôle.
for chemin in /sys/block/*; do
nom="$(basename "$chemin")"
case "$nom" in loop*|ram*|sr*|zram*|dm-*) continue ;; esac
[ -r "$chemin/size" ] || continue
taille=$(( $(cat "$chemin/size") * 512 ))
[ "$taille" -lt 1000000000 ] && continue
if ! command -v smartctl >/dev/null 2>&1; then
note 1 " ${O}?${Z} état des disques inconnu — smartmontools n'est pas installé.
${D}sudo apt install -y smartmontools${Z} ${D}(il passera par le cache)${Z}"
break
fi
sortie="$(smartctl -H -A "/dev/$nom" 2>/dev/null)"
if printf '%s' "$sortie" | grep -qiE 'overall-health.*(FAILED|FAILING)'; then
note 2 " ${R}✗${Z} ${B}/dev/$nom se déclare défaillant${Z} — ne pas installer dessus."
fi
# Les trois attributs ne disent pas la même chose, et les confondre
# revient à sous-estimer les deux qui comptent.
#
# Reallocated_Sector_Ct des secteurs déjà remplacés par la réserve
# du disque. Quelques-uns sont de l'usure
# ordinaire ; c'est leur nombre qui parle.
# Current_Pending_Sector des secteurs que le disque n'arrive plus à
# lire et n'a pas encore pu déplacer. Ce
# qu'ils contenaient est en train d'être
# perdu. Un seul suffit à s'inquiéter.
# Offline_Uncorrectable des secteurs définitivement illisibles.
#
# Les deux derniers ne sont pas de l'usure mais une panne en cours :
# on ne les mesure pas à la même aune que le premier.
for attribut in Reallocated_Sector_Ct Current_Pending_Sector Offline_Uncorrectable; do
valeur="$(printf '%s' "$sortie" | awk -v a="$attribut" '$2 == a {print $10; exit}')"
case "$valeur" in ''|*[!0-9]*) continue ;; esac
[ "$valeur" = "0" ] && continue
case "$attribut" in
Reallocated_Sector_Ct)
libelle="secteur(s) déjà remplacé(s)"
if [ "$valeur" -ge 50 ]; then
note 2 " ${R}✗${Z} /dev/$nom : ${B}$valeur $libelle${Z} — disque à remplacer."
else
note 1 " ${O}!${Z} /dev/$nom : $valeur $libelle — à surveiller."
fi ;;
Current_Pending_Sector)
note 2 " ${R}✗${Z} /dev/$nom : ${B}$valeur secteur(s) illisible(s)${Z} en attente.
${R}Ce ne sont pas des secteurs usés mais des données que le disque
n'arrive plus à relire : le disque est en train de lâcher.${Z}
${D}Sauvegarder ce qui doit l'être avant toute autre chose, et
ne rien installer dessus — l'installation ne tiendrait pas.${Z}" ;;
Offline_Uncorrectable)
note 2 " ${R}✗${Z} /dev/$nom : ${B}$valeur secteur(s) définitivement perdu(s)${Z}.
${R}Le disque a renoncé à les relire. À remplacer.${Z}" ;;
esac
done
heures="$(printf '%s' "$sortie" | awk '/Power_On_Hours/{print $10; exit}')"
rotatif="$(cat "$chemin/queue/rotational" 2>/dev/null)"
# L'usure, recalculée ici : cette boucle est distincte de celle du
# relevé et ne voit pas ses variables.
usure="$(printf '%s' "$sortie" | awk '/Percentage Used:/{gsub(/[^0-9]/,"",$3); print $3; exit}')"
if [ -z "$usure" ]; then
usure="$(printf '%s' "$sortie" | awk '
/Media_Wearout_Indicator|Wear_Leveling_Count|SSD_Life_Left|Percent_Lifetime_Remain/ {
if ($4 ~ /^[0-9]+$/ && $4 <= 100) { print 100 - $4; exit }
}')"
fi
# Un disque mécanique se signale toujours, et pas seulement quand il
# est vieux. C'est le premier frein d'une machine par ailleurs
# correcte : remplacer un plateau par un SSD fait plus pour elle que
# n'importe quel réglage, et c'est le genre de conseil qu'on donne
# pendant qu'on a la machine ouverte devant soi.
# Les heures de marche, dites en heures et en années de fonctionnement.
# « 477 j 15 h » se lit mal : on croit à un disque épuisé alors que
# c'est une année et quelques mois de marche, ce qui est peu.
marche=""
case "$heures" in
''|*[!0-9]*) ;;
*) marche="$heures h"
if [ "$heures" -ge 8760 ]; then
marche="$marche, soit $(( heures / 8760 )),$(( (heures % 8760) * 10 / 8760 )) an de marche"
fi ;;
esac
if [ "$rotatif" = "1" ]; then
# Un disque mécanique se fatigue en tournant. Les constructeurs
# annoncent 300 000 à 600 000 heures de MTBF, mais le retour du
# terrain est plus sobre : au-delà de 30 000 heures — trois ans et
# demi de marche continue — les pannes deviennent sensiblement plus
# fréquentes, et à 45 000 on est dans le sursis.
gravite_disque=1
supplement=""
case "$heures" in
''|*[!0-9]*) ;;
*) if [ "$heures" -ge 45000 ]; then
gravite_disque=2
supplement="
${R}Au-delà de 45 000 heures : sauvegarder avant toute chose.${Z}"
elif [ "$heures" -ge 30000 ]; then
supplement="
${O}Plus de 30 000 heures de marche : il a fait son temps.${Z}"
fi ;;
esac
note "$gravite_disque" " ${O}!${Z} /dev/$nom est un ${B}disque mécanique${Z}${marche:+ · $marche}.
${D}Le remplacer par un SSD ferait plus pour cette machine que
tout le reste réuni — démarrage, ouverture des programmes, réactivité.${Z}$supplement"
else
# Un SSD ne s'use pas en tournant mais en écrivant. Les heures de
# marche ne disent rien de son état : c'est l'usure qu'on regarde.
case "$usure" in
''|*[!0-9]*)
[ -n "$marche" ] && note 0 " ${V}·${Z} /dev/$nom est un SSD · $marche.
${D}Usure non déclarée par ce modèle.${Z}" ;;
*) if [ "$usure" -ge 90 ]; then
note 2 " ${R}✗${Z} /dev/$nom : SSD usé à ${B}${usure} %${Z} — il approche de sa fin.
${R}Sauvegarder, et prévoir son remplacement.${Z}"
elif [ "$usure" -ge 70 ]; then
note 1 " ${O}!${Z} /dev/$nom : SSD usé à ${usure} %${marche:+ · $marche}.
${D}Encore utilisable, mais il faudra y penser.${Z}"
else
note 0 " ${V}·${Z} /dev/$nom : SSD usé à ${usure} % seulement${marche:+ · $marche}."
fi ;;
esac
fi
if [ "$taille" -lt 32000000000 ]; then
note 1 " ${O}!${Z} /dev/$nom ne fait que $(( taille / 1000000000 )) Go — c'est juste."
fi
done
# La mémoire, le processeur.
MEMOIRE_MO=$(( MEMOIRE_KO / 1024 ))
if [ "$MEMOIRE_MO" -lt 2048 ]; then
note 2 " ${R}✗${Z} ${B}${MEMOIRE_MO} Mo de mémoire${Z} — trop peu pour Mint.
${D}Envisager une version légère, ou ajouter de la mémoire.${Z}"
elif [ "$MEMOIRE_MO" -lt 4096 ]; then
libres=$(( SLOTS_TOTAL - SLOTS_PRIS ))
note 1 " ${O}!${Z} ${MEMOIRE_MO} Mo de mémoire — Mint tournera, mais sans aisance.$(
[ "$libres" -gt 0 ] && printf '\n %s' "${D}$libres connecteur(s) ${TYPE_MEMOIRE} libre(s) : la machine est extensible.${Z}")"
fi
if [ "$ARCHITECTURE" != "x86_64" ] && [ "$ARCHITECTURE" != "aarch64" ]; then
note 2 " ${R}✗${Z} processeur ${ARCHITECTURE} — les versions 64 bits ne s'installeront pas."
fi
echo
case "$GRAVITE" in
0) printf " %s%s BON POUR L'INSTALLATION %s\n" "$B$V" "✓" "$Z" ;;
1) printf " %s%s INSTALLATION POSSIBLE, AVEC DES RÉSERVES %s\n" "$B$O" "!" "$Z" ;;
2) printf " %s%s NE PAS INSTALLER EN L'ÉTAT %s\n" "$B$R" "✗" "$Z" ;;
esac
echo
if [ -n "$TABLE_DISQUES" ]; then
printf ' %s' "$D"
colonne "Disque" 7; printf ' '
colonne "Modèle" 20; printf ' '
colonne "Type" 10; printf ' '
colonne "Taille" 10 droite; printf ' '
colonne "SMART" 8; printf ' '
colonne "Marche / usure" 16; printf ' '
printf 'Secteurs R/P/I%s\n' "$Z"
printf '%s' "$TABLE_DISQUES" | sed "s/^/ /"
printf " %sR : remplacés · P : illisibles en attente · I : perdus%s\n" "$D" "$Z"
echo
fi
[ -n "$REMARQUES" ] && printf "%s\n" "$REMARQUES"
# Une pause, sans quoi tout cela défile et personne ne le lit. Sa durée
# suit la gravité, et l'on n'attend jamais indéfiniment : le script tourne
# aussi sans personne devant l'écran, appelé par install.sh.
if [ -t 0 ]; then
case "$GRAVITE" in
2) printf " %sEn parler au propriétaire AVANT de toucher à quoi que ce soit.%s\n" "$D" "$Z"
printf " %sEntrée pour continuer malgré tout%s " "$B" "$Z"
read -r -t "${ALPI_PAUSE_ALERTE:-120}" _ || echo ;;
1) printf " %sEntrée pour continuer (reprise automatique dans %ss)%s " \
"$D" "${ALPI_PAUSE:-20}" "$Z"
read -r -t "${ALPI_PAUSE:-20}" _ || echo ;;
*) sleep "${ALPI_PAUSE_OK:-3}" ;;
esac
echo
fi
fi
# Ce que l'on vient de dire à la personne part avec la fiche.
#
# Sans cela, alpicache rejugeait à partir des seuls champs reçus — et ne voyait
# pas les secteurs en attente, qui ne lui étaient pas envoyés. Il affichait
# « sain » un disque que ce script venait de déclarer à remplacer. Deux juges,
# deux verdicts : il n'y en a plus qu'un, et c'est celui-ci.
VERDICT="ok"
case "${GRAVITE:-0}" in
2) VERDICT="alerte" ;;
1) VERDICT="attention" ;;
esac
MOTIFS_JSON="["
premier=1
while IFS= read -r ligne; do
[ -n "$ligne" ] || continue
[ "$premier" = 0 ] && MOTIFS_JSON="$MOTIFS_JSON,"
premier=0
MOTIFS_JSON="$MOTIFS_JSON$(printf '%s' "$ligne" \
| sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/' -e 's/$/"/')"
done <<< "${MOTIFS:-}"
MOTIFS_JSON="$MOTIFS_JSON]"
RAPPORT="${RAPPORT%\}},$(champ verdict "$VERDICT"),\"motifs\":$MOTIFS_JSON}"
if command -v curl >/dev/null 2>&1; then
reponse="$(printf '%s' "$RAPPORT" | curl -s --max-time 15 \
-H 'Content-Type: application/json' --data-binary @- "$SERVEUR/fiche")"
elif command -v wget >/dev/null 2>&1; then
reponse="$(printf '%s' "$RAPPORT" | wget -qO- --timeout=15 \
--header='Content-Type: application/json' --post-file=- "$SERVEUR/fiche")"
else
echo "Ni curl ni wget : la fiche n'a pas pu être envoyée." >&2
exit 1
fi
if [ -n "$reponse" ]; then
echo "$reponse"
else
echo "Le serveur n'a pas répondu — la fiche n'est pas enregistrée." >&2
exit 1
fi

View file

@ -1,4 +1,26 @@
#!/bin/bash #!/bin/bash
##################################################
# Fiche matérielle pour le serveur de l'install party
#
# Placée au début, et non à la fin : si un disque faiblit, autant le savoir
# avant d'avoir passé une heure à installer dessus — c'est le moment où l'on
# peut encore en parler à son propriétaire. Et si l'installation s'arrête en
# route, on a tout de même la fiche.
#
# Le relevé est le script voisin du dépôt, lu et versionné comme celui-ci :
# rien n'est téléchargé ni exécuté depuis le réseau. S'il n'est pas là, on
# passe — la machine d'un particulier qui rejoue ce script chez elle ne doit
# rien avoir à faire de tout ceci.
#
# C'est la seconde passe, celle que « sudo » relance, qui l'exécute :
# dmidecode et smartctl demandent les droits d'administration.
##################################################
if [ "$(id -u)" = "0" ] && [ -x "$(dirname "$0")/alpi-fiche.sh" ]; then
"$(dirname "$0")/alpi-fiche.sh" || true
fi
# Script de post-installation pour Linux Mint 22.3 # Script de post-installation pour Linux Mint 22.3
# testé le 25/03/2026 # testé le 25/03/2026
@ -227,7 +249,7 @@ install_logiciels() {
DEBIAN_FRONTEND=noninteractive apt install -y ttf-mscorefonts-installer || abort "problème lors de l'installation des polices" DEBIAN_FRONTEND=noninteractive apt install -y ttf-mscorefonts-installer || abort "problème lors de l'installation des polices"
# mise à jour du système # mise à jour du système
echo "==> Mise à jour du système\n" echo -e "==> Mise à jour du système\n"
apt update || abort "impossible de récupérer les informations des dépôts" apt update || abort "impossible de récupérer les informations des dépôts"
apt full-upgrade -y || abort "erreur lors de la mise à jour des paquets" apt full-upgrade -y || abort "erreur lors de la mise à jour des paquets"
@ -289,7 +311,7 @@ install_logiciels() {
# Mises à jour # # Mises à jour #
################ ################
mises_a_jour() { mises_a_jour() {
echo -e "Configuraiton de dconf\n" echo -e "==> Configuration de dconf\n"
# Configuration de dconf pour permettre de modifier certaines configurations # Configuration de dconf pour permettre de modifier certaines configurations
# par défaut # par défaut
mkdir -p /etc/dconf/profile mkdir -p /etc/dconf/profile
@ -337,11 +359,32 @@ configuration_apt() {
# GRUB : sélection automatique de la dernière entrée utilisée # # GRUB : sélection automatique de la dernière entrée utilisée #
############################################################### ###############################################################
modif_grub() { modif_grub() {
# Rien à faire si c'est déjà en place.
#
# update-grub lance os-prober, qui part sonder toutes les partitions de la
# machine à la recherche d'autres systèmes, puis régénère la configuration
# entière : vingt à trente secondes qu'on ne rattrape pas, et qu'on payait
# jusqu'ici à chaque exécution, y compris quand il n'y avait rien à
# changer. Sur une install party de quinze machines, cela se voit.
if grep -q '^GRUB_DEFAULT=saved' /etc/default/grub \
&& grep -q '^GRUB_SAVEDEFAULT=true' /etc/default/grub; then
echo -e "==> GRUB est déjà configuré, rien à changer\n"
return 0
fi
echo -e "==> Modification de GRUB\n" echo -e "==> Modification de GRUB\n"
# On comment la ligne "GRUB_DEFAULT=0" et on ajoute les "bonnes" options # On commente la ligne d'origine, puis on ajoute les nôtres si elles
sed -i '/^GRUB_DEFAULT=0/ i#GRUB_DEFAULT=0\ # manquent. Les ajouter plutôt que les insérer règle deux choses : le
GRUB_DEFAULT=saved\ # fichier ne se retrouve plus avec des lignes indentées — GRUB les accepte,
GRUB_SAVEDEFAULT=true' /etc/default/grub # mais elles trompent la lecture quand on vient déboguer —, et la fonction
# fait toujours son travail lorsque « GRUB_DEFAULT=0 » a disparu, ce qui
# arrive dès qu'un autre outil est passé avant nous.
sed -i 's/^GRUB_DEFAULT=0/#&/' /etc/default/grub
grep -q '^GRUB_DEFAULT=saved' /etc/default/grub \
|| echo 'GRUB_DEFAULT=saved' >> /etc/default/grub
grep -q '^GRUB_SAVEDEFAULT=true' /etc/default/grub \
|| echo 'GRUB_SAVEDEFAULT=true' >> /etc/default/grub
# On met à jour GRUB # On met à jour GRUB
update-grub update-grub
} }

View file

@ -70,10 +70,13 @@ occupe l'association. Il part du serveur de l'association.
**Qui la reçoit.** Les adhérents, et toute personne qui a demandé à la recevoir. Une **Qui la reçoit.** Les adhérents, et toute personne qui a demandé à la recevoir. Une
inscription à une install party ne vous y abonne pas : il faut l'avoir demandé. Pour inscription à une install party ne vous y abonne pas : il faut l'avoir demandé. Pour
la recevoir sans être adhérent, écrivez à **secretaire@alpinux.org**. la recevoir sans être adhérent, abonnez-vous vous-même sur
**[messagerie.alpinux.org](https://messagerie.alpinux.org)** — la marche à suivre est
décrite dans [S'abonner et se désabonner](messagerie-abonnement.md#inscription).
**Comment s'en aller.** Chaque envoi porte un lien de désinscription. Un clic suffit, **Comment s'en aller.** Chaque envoi porte un lien de désinscription, en pied de page
il n'y a rien à justifier, et le retrait est immédiat. Vous continuerez à recevoir les et dans ses en-têtes. Un clic suffit, il n'y a rien à justifier, et le retrait est
immédiat. Vous continuerez à recevoir les
messages liés à vos inscriptions en cours — ce sont deux choses distinctes. messages liés à vos inscriptions en cours — ce sont deux choses distinctes.
--- ---

View file

@ -58,6 +58,7 @@ Lors de nos **Install Party**, les bénévoles d'**Alpinux** installent Linux av
## 🧰 Après l'installation ## 🧰 Après l'installation
- 🩹 [**Trousse de premiers secours Linux Mint**](../guides/linux-mint-trousse.md) — les réflexes utiles des premiers jours. - 🩹 [**Trousse de premiers secours Linux Mint**](../guides/linux-mint-trousse.md) — les réflexes utiles des premiers jours.
- 🖧 [**Le serveur qu'on emporte**](../technique/serveur-install-party.md) — le réseau de la salle, le cache de paquets, et ce qu'un bénévole doit en savoir.
- 🗣️ [**Ateliers et rencontres**](evenements.md) — pour progresser et échanger avec la communauté. - 🗣️ [**Ateliers et rencontres**](evenements.md) — pour progresser et échanger avec la communauté.
--- ---

View file

@ -0,0 +1,130 @@
---
description: Comment s'abonner à une liste de diffusion d'Alpinux, confirmer son adresse, se désabonner, et lire les messages déjà envoyés.
---
# ✉️ S'abonner et se désabonner
Ce guide s'adresse à toute personne qui veut **recevoir les annonces de
l'association**, ou cesser de les recevoir. Aucun compte n'est nécessaire.
**En une phrase :** vous vous abonnez et vous vous désabonnez vous-même, personne ne
peut le faire à votre place, et rien ne part vers une adresse qui ne l'a pas
demandé.
---
## S'abonner { #inscription }
Rendez-vous sur la page d'accueil de la messagerie de l'association.
![La page d'accueil : votre adresse, la liste, et c'est tout](../assets/messagerie/abonnement-01-accueil.png)
Indiquez votre adresse de courriel, choisissez la liste qui vous intéresse, puis
**Continuer**.
L'écran suivant affiche un **code** — de la forme `ALPX-7K3M9Q` — et vous demande de
faire une chose, une seule :
!!! tip "Envoyez-nous un message depuis l'adresse que vous venez de saisir"
Destinataire : **messagerie@alpinux.org**
Objet : **le code affiché**, recopié tel quel
Corps du message : ce que vous voulez, il n'est pas lu
![Le code à recopier, l'adresse où écrire, et l'heure limite](../assets/messagerie/abonnement-02-code.png)
Un bouton **Ouvrir mon logiciel de messagerie** prépare le message pour vous. Il ne
l'envoie pas : c'est à vous de le faire.
Vous avez **trente minutes**. Passé ce délai, le code ne vaut plus rien et la demande
s'efface d'elle-même — sans aucune conséquence, vous pouvez recommencer.
Dans la minute qui suit votre envoi, vous recevez une réponse qui confirme
l'inscription, rappelle la liste, et porte déjà le lien pour partir.
![La réponse reçue une fois le code envoyé](../assets/messagerie/abonnement-03-confirmation.png)
### Pourquoi est-ce à moi d'écrire ?
D'habitude, un site vous envoie un courriel de confirmation. Ici, c'est l'inverse.
Trois raisons.
- **Rien ne part vers une adresse non vérifiée.** Écrire à des adresses non
confirmées est ce qui abîme le plus la réputation d'un expéditeur — et quand elle
est abîmée, ce sont les messages de toute l'association qui finissent en
indésirable, chez tout le monde.
- **Inscrire l'adresse de quelqu'un d'autre ne lui envoie rien.** La demande reste en
attente puis s'efface. Personne ne peut vous abonner à votre insu, ni se servir de
ce formulaire pour vous faire parvenir un message.
- **Un message venu de votre boîte prouve mieux que vous la possédez** qu'un clic sur
un lien : certains antivirus d'entreprise cliquent les liens tout seuls.
La contrepartie, c'est une manipulation à faire. Elle est assumée.
### Je me suis déjà abonné, ou désabonné, par le passé
Le parcours est le même. C'est d'ailleurs **le seul moyen de revenir** : l'association
ne réinscrit jamais personne de sa propre initiative.
### Je n'ai pas reçu la confirmation
Trois choses à vérifier, dans cet ordre :
1. **Avez-vous écrit depuis l'adresse que vous aviez saisie ?** Le code ne vaut que
présenté depuis celle-ci. Un message envoyé depuis une autre boîte est refusé.
2. **Les trente minutes sont-elles passées ?** Recommencez, c'est sans conséquence.
3. **Le code était-il bien dans l'objet ?** Il est aussi reconnu dans le corps du
message, mais l'objet est le plus sûr.
---
## Se désabonner { #desabonnement }
**Chaque message reçu porte un lien de désabonnement**, à deux endroits : en pied de
page, et dans les en-têtes techniques du message.
C'est ce second emplacement qui fait apparaître un bouton **Se désabonner** en haut
du message dans les logiciels modernes — Gmail, Outlook, Thunderbird, Apple Mail. Un
clic suffit.
![Le bouton « Se désabonner » qu'affiche votre logiciel de messagerie](../assets/messagerie/abonnement-04-bouton-desabonner.png)
La page qui s'ouvre vous laisse choisir :
- quitter **cette liste seulement** ;
- ou quitter **toutes les listes** de l'association d'un coup.
![Quitter cette liste, ou toutes](../assets/messagerie/abonnement-05-desabonnement.png)
Aucune justification n'est demandée. Le retrait est immédiat, et il n'y a personne à
qui écrire.
!!! note "Vous n'avez plus aucun message sous la main ?"
Reprenez simplement le parcours d'inscription : il vous renverra un message, qui
portera le lien.
---
## Lire ce qui a déjà été envoyé { #archives }
Les listes ouvertes au public ont des **archives** consultables sans compte. C'est
utile avant de s'abonner, pour voir à quoi on s'engage.
![Les envois passés d'une liste publique](../assets/messagerie/abonnement-06-archives.png)
Seuls y figurent les messages **réellement envoyés** : ni les brouillons, ni les
envois annulés.
!!! info "Aucune adresse de courriel n'apparaît dans les archives"
Les adresses citées dans le corps d'un message — signature, contact d'un
événement, adresse d'un partenaire — y sont masquées : `s****@a****.xxx`. On voit
qu'un contact existait, sans pouvoir le reconstituer ni même savoir chez quel
fournisseur il est hébergé. Leurs titulaires n'ont pas consenti à une publication
sur le web, où un robot les ramasserait pour en faire du spam.
---
## Voir aussi
- [Vos données, vos adresses, nos envois](donnees-personnelles.md)
- [Écrire et recevoir des messages](messagerie-membre.md) — la messagerie entre
membres, qui est autre chose que les listes.

View file

@ -0,0 +1,300 @@
---
description: Guide du gestionnaire de listes Alpinux — créer une liste, préparer et envoyer un message, gérer les abonnés, comprendre les adresses désactivées et surveiller la réputation d'envoi.
---
# 📣 Gérer une liste de diffusion
Ce guide s'adresse aux personnes qui **écrivent à toute une liste** : le bureau, et
les administrateurs de l'outil.
**En une phrase :** vous rédigez un brouillon, vous le mettez en file d'envoi, et un
automate le distribue en lissant le débit pendant que vous faites autre chose.
!!! warning "N'envoyez plus de message en copie cachée depuis votre logiciel de messagerie"
Les deux méthodes ne se coordonnent pas : l'outil ne saurait rien de ce qui est
parti par l'autre chemin, ni des rebonds que cela aurait provoqués. Et c'est
précisément ce mode d'envoi qui saturait le serveur — au-delà d'un certain
nombre de messages par heure, il refuse les suivants et une partie de l'envoi
est perdue sans que personne le voie.
---
## Envoyer un message à une liste { #envoyer }
### 1. Rédiger
Menu **Listes → Campagnes**, puis **Nouvelle campagne**.
![La rédaction, avec l'aperçu de ce qui partira](../assets/messagerie/gestionnaire-01-redaction.png)
Choisissez la liste destinataire, écrivez l'objet et le corps.
Le corps s'écrit en **Markdown** — la même syntaxe que ce wiki, voir
[Écrire en Markdown](../contribuer/markdown.md). Un aperçu s'affiche à côté ; le
bouton **Enregistrer et actualiser l'aperçu** le rafraîchit.
Trois variables sont remplacées à l'envoi, pour chaque destinataire :
| Variable | Remplacée par |
|---|---|
| `{{nom}}` | le nom de l'abonné, s'il est connu |
| `{{email}}` | son adresse |
| `{{desabonnement}}` | le lien de désabonnement qui lui est propre |
**Enregistrer le brouillon** vous permet de revenir plus tard : rien ne part tant que
vous n'avez rien demandé.
!!! danger "Le lien de désabonnement n'est pas optionnel"
Si vous ne placez pas `{{desabonnement}}` vous-même, un lien est ajouté
automatiquement en pied de message. Ce comportement ne se désactive pas : un
envoi de masse sans lien de désabonnement est signalé comme spam, et c'est
ensuite toute l'association qui n'arrive plus à écrire à personne.
### 2. Vérifier avant d'envoyer
Relisez l'aperçu. C'est le dernier moment confortable : une fois la file remplie, ce
qui est parti est parti.
Deux choses valent le coup d'œil :
- **les liens** — un lien faux dans un message envoyé à toute la liste ne se
rattrape pas ;
- **le nombre de destinataires**, affiché sur la fiche de la liste.
### 3. Envoyer
Bouton **Mettre en file d'envoi…**. Une confirmation vous est demandée, avec le
nombre de destinataires.
![Le dernier écran avant que la file se remplisse](../assets/messagerie/gestionnaire-02-confirmation.png)
L'envoi se fait ensuite tout seul, une minute sur l'autre, **sous un plafond horaire**
fixé pour ne pas saturer le serveur. Une liste de quelques centaines de personnes
part en une heure environ.
Vous pouvez fermer la page : l'envoi continue.
### 4. Suivre
La fiche de la campagne affiche la progression : envoyés, en attente, échecs.
![L'envoi en cours : remis, en attente, échecs](../assets/messagerie/gestionnaire-03-progression.png)
Si le compteur semble figé, c'est probablement le plafond horaire : l'envoi reprend
de lui-même à l'heure suivante.
### Arrêter, recommencer
- **Annuler les envois restants…** abandonne ce qui n'est pas encore parti. Ce qui
est déjà distribué ne revient pas.
- **Dupliquer en brouillon** recrée une copie modifiable d'un envoi passé — pratique
pour une lettre dont la structure ne change pas d'un mois sur l'autre.
- **Supprimer ce brouillon…** efface un brouillon abandonné. Une confirmation est
demandée, et il n'y a pas de corbeille. Seul un brouillon se supprime : une
campagne partie reste, puisque des gens l'ont reçue et qu'elle figure dans les
archives.
---
## Créer une liste { #creer }
Menu **Listes → Listes de diffusion**, puis le formulaire de création.
![Créer une liste : le nom et la description suffisent](../assets/messagerie/gestionnaire-04-creer-liste.png)
Seuls **le nom** et **la description** sont demandés. Le reste est automatique :
- l'**identifiant technique** se déduit du nom ;
- l'**expéditeur** est celui de l'association, suivi du nom de l'auteur à
l'envoi — « Alpinux (de la part de Prénom Nom) » ;
- les **réponses** reviennent à l'adresse d'expédition, qui est relevée.
Rien de cela ne se règle liste par liste : l'association écrit sous une seule
identité. Multiplier les expéditeurs éparpille la réputation du domaine autant que
cela désoriente les destinataires.
!!! note "Le nom d'une liste n'est pas modifiable après création"
Relisez-le avant de valider.
---
## Gérer les abonnés { #abonnes }
Menu **Listes → Listes de diffusion**, puis la liste voulue.
![La fiche d'une liste, adresses masquées par défaut](../assets/messagerie/gestionnaire-05-fiche-liste.png)
Quatre compteurs résument l'état : destinataires joignables, inscrits au total,
désabonnés, adresses mortes.
### Les adresses sont masquées
Elles s'affichent sous la forme `c••••••e@exemple.fr`. Le domaine reste lisible — il
suffit au diagnostic — sans étaler à l'écran un carnet d'adresses exploitable.
Un lien **Afficher les adresses en clair** lève le masque quand c'est nécessaire.
### Ajouter des abonnés
Deux moyens, dans le volet **Ajouter des abonnés** :
- **coller une liste d'adresses**, une par ligne. Les formes `nom@exemple.fr`,
`nom@exemple.fr;Prénom Nom` et `Prénom Nom <nom@exemple.fr>` sont acceptées ;
- **importer depuis Dolibarr** les adhérents à jour de cotisation.
!!! danger "Un import ne réactive jamais une adresse partie"
Désabonnée, morte, ou ayant signalé un spam : l'outil refuse de la remettre en
service, même si elle figure dans le fichier importé. Seule la personne
concernée peut revenir, par le parcours d'inscription.
Réinscrire quelqu'un qui est parti est le meilleur moyen de se faire signaler
comme spam — et cette fois définitivement.
---
## Ouvrir une liste au public { #publique }
*Réservé aux administrateurs.*
Sur la fiche de la liste, cochez **Liste publique** et enregistrez.
![Ouvrir une liste à l'inscription libre](../assets/messagerie/gestionnaire-06-liste-publique.png)
Trois conséquences, à peser avant de cocher :
- n'importe qui peut **demander à s'abonner** depuis la page d'accueil ;
- les envois passés deviennent **lisibles publiquement** dans les archives ;
- le **nom et la description** de la liste sont visibles de tout visiteur.
Ce dernier point n'est pas anodin pour une liste interne. Le geste est enregistré
dans le journal de l'outil.
### Les demandes en attente
Quand une liste est publique, la fiche affiche les **demandes d'inscription en
attente de confirmation**, avec leur heure d'expiration.
![Les demandes en attente de confirmation](../assets/messagerie/gestionnaire-07-demandes.png)
Ces adresses **ne sont pas inscrites** et ne le seront que si leurs titulaires
confirment depuis leur propre boîte. Il n'y a rien à faire : celles qui restent
s'effacent seules.
---
## Les adresses désactivées { #desactivees }
Menu **Listes → Adresses désactivées**.
L'écran comporte deux tableaux, et la différence entre les deux est importante.
### Rejets définitifs et plaintes
Ces adresses ont été retirées de **toutes** les listes : le serveur d'en face a
répondu que la boîte n'existait pas, ou la personne nous a signalés comme spam.
![Les rejets définitifs, avec le texte exact du refus](../assets/messagerie/gestionnaire-08-rejets.png)
La colonne de droite affiche **le texte exact du refus**, extrait du rapport reçu —
« utilisateur inconnu », « boîte pleine », le nom d'une liste noire. Le rapport
complet est dépliable si vous voulez tout lire.
Continuer à écrire à ces adresses abîmerait la réputation du serveur pour tous les
autres envois. C'est pourquoi l'outil les écarte sans demander.
### Blocages d'opérateur
**Ces adresses-là sont conservées**, et c'est délibéré.
Le message a été refusé non pas parce que la boîte a disparu, mais parce qu'un
opérateur refuse *notre* serveur — un grand fournisseur de messagerie qui nous a mis
sur une liste d'attente, ou un serveur qui interroge une liste noire.
![Les blocages d'opérateur : ces adresses restent inscrites](../assets/messagerie/gestionnaire-09-blocages.png)
La distinction est vitale. Une part notable des abonnés d'une association tient ses
adresses chez deux ou trois grands fournisseurs : les traiter comme mortes parce que
l'un d'eux nous bloque un jour ferait perdre d'un coup une fraction de la liste, en
silence, pour une faute qui n'est pas la leur.
Quand cette section se remplit, ce n'est donc pas un problème d'abonnés mais de
réputation d'envoi. Le délistage se demande auprès de l'opérateur concerné.
---
## Surveiller la réputation d'envoi { #postmaster }
*Réservé aux administrateurs.* Menu **Configuration → Postmaster**.
Les grands opérateurs de messagerie envoient chaque jour un rapport disant **quelles
adresses IP ont émis du courrier au nom du domaine de l'association**, et si ces
envois étaient correctement signés. L'outil les dépouille tout seul.
![Qui émet au nom du domaine, et si c'est aligné](../assets/messagerie/gestionnaire-10-postmaster.png)
Deux lectures valent le détour :
- **une adresse IP inconnue dans la liste** : quelqu'un émet en notre nom. Usurpation,
ou service de l'association qu'on avait oublié ;
- **une adresse IP connue mais jamais alignée** : elle émet sans être autorisée par
nos enregistrements, et ses messages tombent en indésirable chez tout le monde.
Le second tableau liste le courrier reçu à l'adresse *postmaster* que l'automate n'a
pas su traiter — avis d'opérateur, signalement — et qui attend une lecture humaine.
Le bouton **Marquer comme lu** le retire de la liste une fois traité.
---
## Attribuer les rôles { #droits }
*Réservé aux administrateurs.* Menu **Configuration → Droits**.
![Les trois rôles, cumulables](../assets/messagerie/gestionnaire-11-droits.png)
Trois rôles indépendants, cumulables :
| Rôle | Donne accès à |
|---|---|
| Rédacteur | la section **Listes** : campagnes et abonnés |
| Administrateur | les listes publiques, les imports, **Droits** et **Postmaster** |
| Messagerie interne | la section **Messagerie** |
Un administrateur ne peut pas se retirer lui-même l'administration : l'outil refuse
de se retrouver sans personne pour le gérer. Demandez-le à un autre administrateur.
---
## Questions fréquentes
??? question "Mon envoi semble arrêté à mi-parcours"
C'est le plafond horaire. L'envoi reprend de lui-même à l'heure suivante, et la
fiche de la campagne affiche ce qui reste en attente.
??? question "Une adresse apparaît comme désactivée alors qu'elle fonctionne"
Regardez dans quel tableau elle se trouve. Dans **Blocages d'opérateur**, elle
n'est pas désactivée et il n'y a rien à faire de ce côté. Dans **Rejets
définitifs**, lisez le texte du refus : il dit précisément ce que le serveur
distant a répondu.
??? question "Quelqu'un veut revenir après s'être désabonné"
Il doit refaire le parcours d'inscription lui-même. Ne le réinscrivez pas à la
main : l'outil l'interdit, et pour de bonnes raisons.
??? question "Puis-je renvoyer exactement le même message plus tard ?"
Oui, **Dupliquer en brouillon** sur la campagne passée. Vérifiez les dates et les
liens avant de renvoyer : ce qui était vrai il y a un mois ne l'est plus
forcément.
??? question "Comment savoir qui a ouvert le message ?"
On ne le sait pas, et c'est voulu. Les messages ne contiennent ni image de suivi
ni lien piégé. Ce que l'on sait, c'est ce qui a été **remis** et ce qui a
**rebondi**.
---
## Voir aussi
- [S'abonner et se désabonner](messagerie-abonnement.md) — ce que voient les
personnes à qui vous écrivez.
- [Vos données, vos adresses, nos envois](donnees-personnelles.md)
- [Écrire en Markdown](../contribuer/markdown.md)

View file

@ -0,0 +1,120 @@
---
description: La messagerie interne d'Alpinux — écrire à un autre membre sans connaître son adresse, lire ses messages, régler ses avis par courriel.
---
# 💬 Écrire et recevoir des messages
Ce guide s'adresse aux **membres** de l'association qui veulent s'écrire entre eux.
C'est autre chose que les listes de diffusion : ici, personne ne diffuse à tout le
monde. On écrit à une personne, et elle seule.
**En une phrase :** vous écrivez à quelqu'un sans connaître son adresse, et il vous
répond sans connaître la vôtre.
!!! info "Il faut être connecté"
La messagerie interne demande le **compte de l'association**, le même que pour
le portail. Le rôle correspondant est attribué par un administrateur.
---
## Lire mes messages { #boite }
Menu **Messagerie → Mes messages**.
![La boîte de réception ; les messages non lus sont en gras](../assets/messagerie/membre-01-boite.png)
Le nombre de messages non lus apparaît à côté du mot **Messagerie** dans le menu :
vous le voyez donc depuis n'importe quel écran, sans avoir à aller voir.
Un message s'ouvre d'un clic sur son objet. Il est marqué lu à ce moment-là.
---
## Écrire à quelqu'un { #ecrire }
Menu **Messagerie → Écrire**.
![Le destinataire se choisit dans l'annuaire, sans connaître son adresse](../assets/messagerie/membre-02-ecrire.png)
Choisissez le destinataire dans la liste proposée : c'est l'annuaire des membres.
**Vous n'avez pas besoin de son adresse** — vous ne la verrez pas, et il ne verra pas
la vôtre.
Écrivez l'objet et le message, puis envoyez.
### Ce que reçoit votre destinataire
Un **avis** par courriel, qui dit qu'un message l'attend, de la part de qui, et sur
quel sujet. **Le texte du message n'est pas recopié dans cet avis** : il faut se
connecter pour le lire.
C'est volontaire. Un avis qui recopierait le message rendrait la messagerie interne
inutile — autant s'écrire directement.
---
## Régler mes avis par courriel { #avis }
Menu **Configuration → Mon profil**.
![Les avis par courriel et le résumé quotidien se règlent ici](../assets/messagerie/membre-03-profil.png)
Vous y choisissez :
- **si** vous voulez être prévenu par courriel quand un message arrive ;
- **à quelle adresse** ces avis doivent partir.
### Le résumé quotidien
Si des messages restent non lus, un **résumé** part une fois par jour — et seulement
les jours où quelque chose est arrivé. Il dit qui vous a écrit et sur quels sujets.
Ni le corps des messages, ni les adresses des auteurs n'y figurent.
Il se coupe depuis le même écran que les avis.
---
## Emporter mes messages { #export }
Menu **Messagerie → Copie de mes messages**.
Vous téléchargez l'ensemble de vos messages — reçus et envoyés — dans une archive.
Chaque message y est au format standard des courriels, lisible par n'importe quel
logiciel de messagerie.
C'est ce qu'il faut utiliser pour garder une trace hors de l'outil, ou pour exercer
votre droit d'accès à vos données.
---
## Questions fréquentes
??? question "Mon correspondant verra-t-il mon adresse ?"
Non. Les messages ne circulent pas par courriel entre vous : ils restent dans
l'outil. Seuls les **avis** partent par courriel, et chacun part vers l'adresse
de son propre destinataire.
??? question "Puis-je répondre depuis mon logiciel de messagerie ?"
Non. L'avis est une notification, pas le message. Il faut se connecter pour lire
et répondre — c'est la contrepartie de ne pas divulguer les adresses.
??? question "Je ne reçois plus d'avis"
Vérifiez d'abord **Configuration → Mon profil** : les avis y sont peut-être
coupés. S'ils sont actifs et que rien n'arrive, c'est probablement que votre
adresse a rebondi ; l'outil suspend alors les avis pour ne pas s'acharner.
Signalez-le à un administrateur, qui le verra dans ses écrans.
??? question "Puis-je écrire à quelqu'un qui n'est pas dans l'annuaire ?"
Non. L'annuaire ne contient que les membres à qui un rôle a été attribué. Pour
écrire à quelqu'un d'autre, il faut son adresse et un logiciel de messagerie
ordinaire.
---
## Voir aussi
- [S'abonner et se désabonner](messagerie-abonnement.md) — les listes de diffusion,
qui sont autre chose.
- [Vos données, vos adresses, nos envois](donnees-personnelles.md)

View file

@ -0,0 +1,50 @@
---
description: La messagerie d'Alpinux — trois guides selon que vous voulez vous abonner, écrire aux membres, ou gérer une liste de diffusion.
---
# 📬 La messagerie d'Alpinux
La messagerie de l'association sert à deux choses qu'il ne faut pas confondre :
- **les listes de diffusion**, par lesquelles le bureau écrit à tout le monde ;
- **la messagerie interne**, par laquelle les membres s'écrivent entre eux sans se
communiquer leurs adresses.
Selon ce que vous voulez faire, la suite n'est pas la même.
---
## Je veux…
| Ce que vous voulez faire | Le guide qu'il vous faut |
|---|---|
| Recevoir les annonces de l'association, ou arrêter de les recevoir | [S'abonner et se désabonner](messagerie-abonnement.md) |
| Lire ce qui a déjà été envoyé | [S'abonner et se désabonner](messagerie-abonnement.md#archives) |
| Écrire à un autre membre, ou lire mes messages | [Écrire et recevoir des messages](messagerie-membre.md) |
| Envoyer un message à toute une liste | [Gérer une liste de diffusion](messagerie-gestionnaire.md) |
| Ajouter des abonnés, ouvrir une liste au public | [Gérer une liste de diffusion](messagerie-gestionnaire.md) |
| Comprendre pourquoi une adresse ne reçoit plus rien | [Gérer une liste de diffusion](messagerie-gestionnaire.md#desactivees) |
---
## Qui peut quoi
L'accès aux écrans dépend d'un rôle, attribué par un administrateur dans
**Configuration → Droits**.
| Vous êtes… | Vous voyez… |
|---|---|
| Une personne sans compte | l'inscription et les archives des listes publiques |
| Membre | la section **Messagerie** |
| Rédacteur | la section **Listes** |
| Administrateur | tout, plus **Configuration → Droits** et **Configuration → Postmaster** |
Une même personne peut cumuler les trois.
---
## Voir aussi
- [Vos données, vos adresses, nos envois](donnees-personnelles.md) — ce que
l'association recueille, combien de temps elle le garde, et ce que vous pouvez
exiger.

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

23
docs/deploiement.md Normal file
View file

@ -0,0 +1,23 @@
# wiki.alpinux.org
Documentation publique Alpinux — MkDocs Material.
## Déploiement
```bash
cd org.alpinux.owni/wiki
mkdocs build --strict
rsync -rlcz --delete site/ alpinux.org:/var/www/wiki.alpinux.org/web/
```
## Développement local
```bash
pip install mkdocs-material
mkdocs serve # http://localhost:8000
```
## Notes
- Pas de service systemd — site statique servi directement par Apache
- Les docs publiques sont dans `wiki/docs/technique/`

View file

@ -104,10 +104,6 @@ Clic droit sur **Démarrer** → *Terminal (administrateur)*, puis :
powercfg /hibernate off powercfg /hibernate off
``` ```
!!! tip "Le dernier arrêt de Windows"
Au moment d'éteindre Windows pour insérer la clé USB, maintenez **Maj (Shift)**
enfoncée en cliquant sur *Démarrer → Arrêter*. Cela force un arrêt complet.
### Gestion de l'alimentation de la carte Wi-Fi ### Gestion de l'alimentation de la carte Wi-Fi
Certaines cartes Wi-Fi (puces Realtek ou Broadcom notamment) restent dans l'état de basse Certaines cartes Wi-Fi (puces Realtek ou Broadcom notamment) restent dans l'état de basse
@ -188,6 +184,8 @@ Linux. Il faut le basculer sur **AHCI**, dans la section *Storage*, *SATA Config
conserver Windows, faites le changement avec un bénévole. Si Windows est de toute conserver Windows, faites le changement avec un bénévole. Si Windows est de toute
façon remplacé, la bascule est sans conséquence. façon remplacé, la bascule est sans conséquence.
--- ---
## 2. Préparer son support d'installation ## 2. Préparer son support d'installation
@ -246,8 +244,11 @@ Avec [Ventoy](ventoy.md), la préparation tient en un copier-coller :
### Branchements ### Branchements
- Insérez la clé Ventoy dans un **port USB de la machine**, en évitant les hubs. - Insérez la clé Ventoy dans un **port USB de la machine**, en évitant les hubs.
- **Branchez un câble Ethernet.** Mint pourra télécharger mises à jour, codecs et paquets - **Branchez un câble Ethernet.** Le script install.sh de Alpinux effectue des tests.
de langue pendant l'installation — cela évite beaucoup de réglages ensuite.
!!! tip "Le dernier arrêt de Windows - Comment arrêter Windows pour démarrer sur la clé USB"
Au moment d'éteindre Windows pour insérer la clé USB, maintenez **Maj (Shift)**
enfoncée en cliquant sur *Démarrer → Arrêter*. Cela force un arrêt complet.
### Accéder au menu de démarrage ### Accéder au menu de démarrage
@ -341,16 +342,25 @@ fatigué. On ne le découvre pas au milieu du partitionnement.
En session live, taper les commandes une à une est fastidieux. Un script d'Alpinux fait En session live, taper les commandes une à une est fastidieux. Un script d'Alpinux fait
l'inventaire et rend un verdict : l'inventaire et rend un verdict :
**Sur le réseau de l'install party**, où les scripts sont servis par
[le serveur qu'Alpinux emporte](../technique/serveur-install-party.md) :
```bash ```bash
wget https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-wiki/raw/branch/main/code/linux/linux-mint/verif-disque.sh wget http://alpicache.lan/verif-disque.sh && chmod +x verif-disque.sh && ./verif-disque.sh
chmod +x verif-disque.sh
./verif-disque.sh
``` ```
Sur le réseau de l'install party, plus court : !!! tip "Si le nom ne répond pas"
Le serveur répond aussi à `alpicache`, `apt-proxy` et `10.0.0.1` — cette
dernière adresse fonctionne même quand la résolution de noms n'est pas encore
en place :
`wget http://10.0.0.1/verif-disque.sh`
**Ailleurs, depuis Internet :**
```bash ```bash
wget http://10.0.0.1/verif-disque.sh && chmod +x verif-disque.sh && ./verif-disque.sh wget https://gitea.alpinux.org/alpinux.cedrica5l/alpinux.scripts/raw/branch/stable/poste/verif-disque.sh
chmod +x verif-disque.sh
./verif-disque.sh
``` ```
Il **ne modifie rien** — il lit, il affiche, il conseille. Il demande le mot de passe pour Il **ne modifie rien** — il lit, il affiche, il conseille. Il demande le mot de passe pour
@ -368,6 +378,40 @@ les vérifications qui le nécessitent, puis passe en revue :
Il finit par deux listes : *à régler avant d'installer*, et *à savoir*. Il finit par deux listes : *à régler avant d'installer*, et *à savoir*.
### Relever la fiche de la machine
En install party, un second script complète l'état des lieux : il relève la **fiche
matérielle** — modèle, processeur, mémoire et ses connecteurs, disques et leur santé —
et l'envoie au serveur de l'association.
```bash
wget http://alpicache.lan/alpi-fiche.sh && chmod +x alpi-fiche.sh && sudo ./alpi-fiche.sh --check
```
Hors réseau de l'install party, il se prend sur Internet :
```bash
wget https://gitea.alpinux.org/alpinux.cedrica5l/alpinux.scripts/raw/branch/stable/poste/alpi-fiche.sh
```
Il n'aura alors aucun serveur à qui remettre la fiche : il l'affiche, et c'est tout.
Lancé depuis la session live, il rend en plus un verdict en trois états : **bon pour
l'installation**, **possible avec des réserves**, ou **ne pas installer en l'état** — un
disque qui se déclare défaillant, des secteurs réalloués, moins de 2 Go de mémoire, un
processeur 32 bits. C'est le dernier moment où l'on peut encore renoncer et en parler au
propriétaire.
Le même script, relancé après l'installation (sans `--check`), enregistre ce qui a
finalement été installé. Les deux relevés se comparent : un disque qui allait bien le
matin et se plaint le soir, cela se voit.
!!! warning "Dites-le à la personne dont vous installez la machine"
La fiche part sur le réseau local, vers le serveur de l'association, et n'en sort
pas. Elle ne contient ni fichiers, ni historique, ni nom d'utilisateur — mais elle
contient le **nom de la machine**, souvent un prénom, et l'**adresse matérielle** de
la carte réseau. C'est son ordinateur : cela se demande, cela ne se suppose pas.
!!! danger "BitLocker : ne touchez à rien" !!! danger "BitLocker : ne touchez à rien"
Si le script annonce une partition chiffrée par BitLocker, **arrêtez-vous là**. Le Si le script annonce une partition chiffrée par BitLocker, **arrêtez-vous là**. Le
chiffrement doit être suspendu depuis Windows — *Paramètres → Confidentialité et chiffrement doit être suspendu depuis Windows — *Paramètres → Confidentialité et
@ -568,25 +612,34 @@ Le système est installé ; quelques réglages le rendent réellement utilisable
### Le script de post-installation Alpinux ### Le script de post-installation Alpinux
Il automatise ce qui manque presque toujours après une installation standard. Ouvrez un Il automatise ce qui manque presque toujours après une installation standard. Ouvrez un
terminal (`Ctrl + Alt + T`) et récupérez-le — **sur le réseau local de l'Install Party** : terminal (`Ctrl + Alt + T`) et récupérez-le — **sur le réseau local de l'Install Party**,
où [le serveur d'Alpinux](../technique/serveur-install-party.md) le sert et met en cache
tout ce que la machine téléchargera ensuite :
```bash ```bash
wget http://10.0.0.1/install.sh wget http://alpicache.lan/install.sh http://alpicache.lan/alpi-fiche.sh
``` ```
**ou depuis Internet :** **ou depuis Internet :**
```bash ```bash
wget https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-wiki/raw/branch/main/code/linux/linux-mint/install.sh wget https://gitea.alpinux.org/alpinux.cedrica5l/alpinux.scripts/raw/branch/stable/poste/install.sh \
https://gitea.alpinux.org/alpinux.cedrica5l/alpinux.scripts/raw/branch/stable/poste/alpi-fiche.sh
``` ```
Puis, dans les deux cas : Puis, dans les deux cas :
```bash ```bash
chmod +x install.sh chmod +x install.sh alpi-fiche.sh
./install.sh ./install.sh
``` ```
!!! note "Pourquoi deux fichiers"
`install.sh` commence par relever la fiche matérielle, et il ne le fait **que s'il
trouve `alpi-fiche.sh` à côté de lui** — il ne va rien chercher sur le réseau de
lui-même. Téléchargé seul, il s'installera très bien, simplement sans relevé. En
install party, prenez les deux.
Le mot de passe est demandé au passage (`alpinux73` en Install Party) : les caractères ne Le mot de passe est demandé au passage (`alpinux73` en Install Party) : les caractères ne
s'affichent pas, c'est normal. s'affichent pas, c'est normal.

View file

@ -109,16 +109,31 @@ Les **Préférences** s'organisent en trois onglets :
!!! tip "Le piège du `/boot` plein" !!! tip "Le piège du `/boot` plein"
Les anciens noyaux s'accumulent et finissent par saturer la partition de démarrage, Les anciens noyaux s'accumulent et finissent par saturer la partition de démarrage,
ce qui bloque toute nouvelle mise à jour. L'onglet *Automatisation* permet de ce qui bloque toute nouvelle mise à jour. L'onglet *Automatisation* permet de
programmer leur suppression ; à défaut, retirez-les de temps en temps depuis programmer leur suppression ; à défaut, passez de temps en temps par
l'*Analyseur de noyaux* du gestionnaire. **Affichage → Noyaux Linux**, qui les liste et permet de retirer les plus anciens.
!!! warning "Changer de noyau n'est pas anodin"
La même fenêtre *Noyaux Linux* s'ouvre sur un avertissement qu'il vaut la peine de
lire : un noyau plus récent peut faire disparaître le Wi-Fi, l'accélération graphique,
ou donner un écran noir au démarrage — surtout avec des pilotes propriétaires.
La sortie de secours tient en quatre gestes, à connaître **avant** d'en installer un :
redémarrer, maintenir **Maj** pour afficher le menu GRUB, choisir *Advanced options*
puis un noyau antérieur, et désinstaller le plus récent une fois revenu.
### Activer le pare-feu ### Activer le pare-feu
Le pare-feu est installé, mais **désactivé par défaut**. Pour un ordinateur portable qui Le pare-feu est installé, mais **désactivé par défaut**. Pour un ordinateur portable qui
se connecte à des réseaux publics, il vaut mieux l'allumer : se connecte à des réseaux publics, il vaut mieux l'allumer :
**Paramètres du système → Pare-feu** (famille *Administration*), puis basculez **État** sur *Activé*. Le profil *Public* convient : **Paramètres du système → Pare-feu** (famille *Administration*). L'ouverture demande votre
il bloque les connexions entrantes et laisse passer tout ce que vous initiez. mot de passe, puis il suffit de basculer **État** sur *Activé*.
Le profil proposé par défaut, **Domestique**, convient : les deux lignes en dessous
annoncent la règle appliquée — *Entrant : Refuser*, *Sortant : Autoriser*. Autrement dit,
rien ne rentre sans y avoir été invité, tout ce que vous lancez sort normalement. Les
profils *Bureau* et *Public* servent à mémoriser d'autres jeux de règles ; sans besoin
précis, n'y touchez pas.
### Vérifier ses pilotes ### Vérifier ses pilotes

View file

@ -181,14 +181,20 @@ Une exécution réussie encadre le build par une ligne `Deploy started` et une l
--- ---
## Ce que le build publie pour la page d'accueil ## Ce que le build publie pour alpinux.org
Chaque build écrit, à la racine du site, un fichier **`derniers-articles.json`** : les Chaque build écrit deux fichiers à la racine du site, avec pour chaque page son titre,
vingt dernières pages créées ou modifiées, avec leur titre, leur adresse et la date du son adresse et la date du commit qui a touché son contenu :
commit qui a touché leur contenu. C'est ce fichier que lit la page d'accueil
d'alpinux.org pour afficher « Le wiki, fraîchement mis à jour ».
Il est produit par le hook `hooks/derniers_articles.py`, déclaré dans `mkdocs.yml`. | Fichier | Ce qu'il porte | Qui le lit |
|---|---|---|
| `derniers-articles.json` | Les vingt dernières pages créées ou modifiées | L'accueil d'alpinux.org, section « Le wiki, fraîchement mis à jour » |
| `toutes-les-pages.json` | Toutes les pages, sans limite, avec leur rubrique | La page des mises à jour d'alpinux.org |
Le second est la liste entière, le premier son début : un seul parcours de l'historique
git les produit tous les deux, et ils portent la même date de génération.
Ils sont produits par le hook `hooks/derniers_articles.py`, déclaré dans `mkdocs.yml`.
Trois choses à savoir : Trois choses à savoir :
- **Un déplacement n'est pas une mise à jour.** Les dates viennent de `git log` avec - **Un déplacement n'est pas une mise à jour.** Les dates viennent de `git log` avec
@ -198,14 +204,15 @@ Trois choses à savoir :
typiquement la redirection laissée derrière un article déplacé — n'apparaît jamais typiquement la redirection laissée derrière un article déplacé — n'apparaît jamais
dans la liste. dans la liste.
- **Le hook n'échoue jamais.** Sans historique git, il n'écrit rien et le build - **Le hook n'échoue jamais.** Sans historique git, il n'écrit rien et le build
continue ; l'accueil d'alpinux.org masque alors simplement sa section wiki. Ses continue ; alpinux.org masque alors simplement ce qu'il ne peut pas remplir. Ses
messages sont pour cela de niveau *info* et jamais *warning* : le déploiement messages sont pour cela de niveau *info* et jamais *warning* : le déploiement
construit avec `--strict`, où un seul avertissement suffit à interrompre le build, construit avec `--strict`, où un seul avertissement suffit à interrompre le build,
et donc la publication. et donc la publication.
Le même hook pose l'en-tête `Access-Control-Allow-Origin` sur ce seul fichier, au Le même hook pose l'en-tête `Access-Control-Allow-Origin` sur ces deux fichiers, et
moyen d'un `.htaccess` déposé à la racine du site construit. Sans lui, le navigateur sur eux seuls, au moyen d'un `.htaccess` déposé à la racine du site construit. Sans
refuserait à alpinux.org le droit de lire une réponse venue de wiki.alpinux.org. lui, le navigateur refuserait à alpinux.org le droit de lire une réponse venue de
wiki.alpinux.org.
!!! warning "Deux conditions" !!! warning "Deux conditions"
**Le clone doit avoir son historique.** Un clone `--depth 1` ne porte qu'un **Le clone doit avoir son historique.** Un clone `--depth 1` ne porte qu'un
@ -220,6 +227,7 @@ refuserait à alpinux.org le droit de lire une réponse venue de wiki.alpinux.or
```bash ```bash
# vérifier ce que le dernier build a publié # vérifier ce que le dernier build a publié
curl -s https://wiki.alpinux.org/derniers-articles.json | head -20 curl -s https://wiki.alpinux.org/derniers-articles.json | head -20
curl -s https://wiki.alpinux.org/toutes-les-pages.json | head -20
``` ```
--- ---
@ -256,4 +264,5 @@ Ouvrez [http://localhost:8000](http://localhost:8000) — MkDocs recharge automa
| Déployer à la main (webhook en panne) | `deploy-wiki.sh`, sur le serveur | | Déployer à la main (webhook en panne) | `deploy-wiki.sh`, sur le serveur |
| Voir le journal des déploiements | `tail -20 /var/log/wiki-deploy.log` | | Voir le journal des déploiements | `tail -20 /var/log/wiki-deploy.log` |
| Vérifier la liste envoyée à l'accueil | `curl -s https://wiki.alpinux.org/derniers-articles.json` | | Vérifier la liste envoyée à l'accueil | `curl -s https://wiki.alpinux.org/derniers-articles.json` |
| Vérifier l'inventaire complet publié | `curl -s https://wiki.alpinux.org/toutes-les-pages.json` |
| Régénérer les fichiers du logo (poste local) | `python3 scripts/build-assets.py` | | Régénérer les fichiers du logo (poste local) | `python3 scripts/build-assets.py` |

View file

@ -0,0 +1,169 @@
---
description: Le serveur qu'Alpinux emporte en install party — partage de connexion, réseau local, cache de paquets APT, page de santé, et ce qu'un bénévole doit en savoir sur place.
---
# Le serveur qu'on emporte en install party
Quinze ordinateurs qui s'installent le même matin, c'est quinze fois les mêmes
cinq cents mégaoctets tirés par la ligne de la salle — quand il y en a une. Alpinux
apporte donc sa propre machine, **alpicache**, qui partage l'accès à Internet, sert un
réseau local aux postes en cours d'installation, et **garde en cache les paquets déjà
téléchargés**.
!!! note "Pour qui est cette page"
Pour les bénévoles qui installent, et pour qui prépare la salle. La configuration
complète de la machine — interfaces, services, dépannage détaillé — vit dans son
propre dépôt, `alpinux.alpicache`, et non ici.
---
## Le réseau de la salle
```mermaid
flowchart LR
A[Prise murale<br/>ou box de la salle] --> B[alpicache<br/>10.0.0.1]
B --> C[Switch]
C --> D[Poste 1]
C --> E[Poste 2]
C --> F[Poste n]
```
La machine tient les deux bouts : d'un côté elle demande un accès à la salle, de l'autre
elle distribue un réseau en **10.0.0.x** aux postes branchés sur le switch. Les rôles ne
sont pas figés à l'avance : elle désigne comme sortie la première prise où on lui répond,
et sert le réseau local sur l'autre.
!!! warning "Les postes se branchent sur le switch, jamais sur la box"
Un poste branché directement sur la box de la salle reçoit une adresse de la salle :
il ne voit plus le cache, ne trouve plus les scripts, et fait tomber les compteurs à
zéro. C'est l'erreur la plus fréquente, et elle ne se voit pas tout de suite.
---
## Ce que la machine offre, une fois branchée
| Adresse | À quoi ça sert |
| --- | --- |
| **<http://alpicache.lan/>** | La page de santé : réseau, disques, machines servies, cache |
| **`http://apt-proxy:3142`** | Le cache de paquets, à déclarer comme mandataire |
| **`http://10.0.0.1/`** | Les scripts d'Alpinux, servis sur place — dont `verif-disque.sh` et `install.sh` |
La page de santé se rafraîchit seule ; c'est le tableau de bord de la journée. Quatre
cartes à surveiller :
| Carte | Ce qu'on veut y voir |
| --- | --- |
| **Réseau** | une sortie *et* un réseau local nommés, « Internet joignable » |
| **Disques** | le disque du cache présent, avec de la place |
| **Machines servies** | le nombre grandit à mesure que les postes arrivent |
| **Cache de paquets** | « Économisé » qui monte au fil de la matinée |
**« Machines servies » qui reste à zéro** alors que des postes sont branchés, c'est le
signe que le réseau local ne distribue pas d'adresses : prévenez la personne qui tient la
machine plutôt que de chercher sur le poste du participant.
---
## Faire passer une installation par le cache
C'est là que tout se joue, et le geste n'est pas le même selon le système.
### Debian
L'installateur demande un mandataire à l'écran **« Configurer le miroir de l'archive
Debian »**. Saisissez-y :
```
http://apt-proxy:3142
```
C'est **le seul moment** où on peut le faire, et c'est celui qui compte : toute
l'installation passe alors par le cache.
### Linux Mint et Ubuntu
Leur installateur ne demande pas de mandataire : l'installation elle-même sortira sur
Internet. Le gain arrive juste après — sur les mises à jour et les logiciels ajoutés,
c'est-à-dire sur l'essentiel du volume.
Une fois la machine redémarrée sur son nouveau système :
```bash
sudo apt install auto-apt-proxy
```
Ce paquet cherche un cache sur le réseau local et s'en sert quand il en trouve un — sans
rien à configurer, et sans gêner la machine une fois rentrée chez son propriétaire.
!!! tip "Le script de post-installation le fait déjà"
Le [script d'Alpinux](../guides/linux-mint-depuis-windows.md#le-script-de-post-installation-alpinux)
installe `auto-apt-proxy` parmi ses réglages : si vous le lancez, c'est fait.
---
## Ce qui coince, et la parade
**La salle n'a pas de réseau.** Un téléphone Android en partage de connexion USB fait
l'affaire : branché sur la machine, il devient la sortie. Prévoyez le câble.
**Le disque du cache n'est pas vu.** Il doit être branché **avant** d'allumer la machine.
Après coup, le cache démarre sans rien dire et ne garde rien — la carte *Disques* de la
page de santé le montre tout de suite.
**Un poste n'obtient pas d'adresse.** Vérifiez d'abord le câble et le switch, puis la
carte *Machines servies*. Une adresse en `169.254.x.x` signifie que le poste n'a reçu
aucune réponse : il s'en est inventé une.
**`apt` renvoie des « 503 Connection closed » en série.** Le cache accepte la requête mais
ne sait pas où aller la chercher — c'est arrivé le 24/09/2026, et une installation Mint
s'y est arrêtée. Le piège est qu'**interroger le cache directement répond parfaitement** :
les deux chemins ne suivent pas la même route à l'intérieur du serveur. Ne cherchez donc
pas sur la machine du participant : prévenez la personne qui tient le serveur.
---
## Pour préparer une install party
La veille, la machine s'allume chez quelqu'un, sur une ligne rapide, et l'on garnit le
cache avec ce qu'on va installer — si la version de Mint a changé depuis la dernière
sortie, autant la tirer maintenant.
!!! warning "Le cache ne sert que ce qu'il connaît"
Le pré-chargement vise des versions nommées, et une version absente de la liste ne
profite à personne. Le 24/09/2026, la première Mint 22.3 de la soirée a tiré **1,3 Go
d'Internet pour 82 Mo servis par le cache** : le pré-chargeur couvrait encore les
versions précédentes.
Avant chaque sortie, vérifiez donc que la liste des versions pré-chargées correspond à
celle que vous allez réellement installer. C'est par le **nom de code** que le
pré-chargement les désigne, et un décalage d'un cran suffit à garnir le cache de la
mauvaise version :
| Version | Nom de code |
|---|---|
| Mint 22 | `wilma` |
| Mint 22.1 | `xia` |
| Mint 22.2 | `zara` |
| Mint 22.3 | `zena` |
Pour mémoire, la série précédente : `vanessa` (21), `vera` (21.1), `victoria` (21.2),
`virginia` (21.3).
Sur place, l'ordre de branchement n'est pas une superstition : le disque externe d'abord,
puis le câble Internet **seul**, puis la machine, et seulement ensuite le switch et les
postes. Brancher la sortie seule d'abord, c'est ôter à la machine toute occasion de se
tromper de rôle.
La procédure complète — ce qu'on emporte, les vérifications, le dépannage, la fiche à
cocher pour chaque participant et l'affiche à scotcher près du switch — est dans le dépôt
`alpinux.alpicache`. Demandez-la à un membre du bureau avant une sortie.
---
## Voir aussi
- [Install Party](../alpinux/install-party.md) — les dates, et comment y participer
- [Installer Linux Mint depuis Windows](../guides/linux-mint-depuis-windows.md) — le guide
suivi sur place
- [Préparation d'un serveur Debian 13](serveur-debian-13.md) — pour la machine, la base
système est la même

View file

@ -1,8 +1,15 @@
"""Publie `derniers-articles.json` : ce que le wiki a écrit ou repris récemment. """Publie ce que le wiki a écrit ou repris, pour qui l'affiche ailleurs.
La page d'accueil d'alpinux.org lit ce fichier pour afficher « Le wiki, fraîchement Deux fichiers, écrits à chaque build à côté des pages et servis avec l'en-tête CORS
mis à jour ». Il est donc écrit à chaque build, à côté des pages, et servi avec sans lequel un autre domaine ne peut pas les lire :
l'en-tête CORS sans lequel un autre domaine ne peut pas le lire.
- `derniers-articles.json` — les vingt dernières pages touchées, que la page d'accueil
d'alpinux.org lit pour sa section « Le wiki, fraîchement mis à jour » ;
- `toutes-les-pages.json` — le même inventaire, sans limite de nombre et avec la
rubrique de chaque page, pour la page des mises à jour d'alpinux.org.
Les deux sortent du même calcul : le second est la liste entière, le premier son
début.
Les dates viennent de git, pas du système de fichiers : un `git clone` repose tous les Les dates viennent de git, pas du système de fichiers : un `git clone` repose tous les
fichiers à la même seconde, et une page recopiée depuis Obsidian aurait l'air neuve. fichiers à la même seconde, et une page recopiée depuis Obsidian aurait l'air neuve.
@ -20,10 +27,11 @@ from urllib.parse import urljoin
from mkdocs.plugins import log from mkdocs.plugins import log
FICHIER = "derniers-articles.json" FICHIER_RECENTS = "derniers-articles.json"
FICHIER_TOUTES = "toutes-les-pages.json"
# Ce que le fichier porte au plus. L'accueil n'en affiche que six, mais le fichier # Ce que le premier fichier porte au plus. L'accueil n'en affiche que six, mais le
# sert aussi à qui voudrait en faire autre chose. # fichier sert aussi à qui voudrait en faire autre chose. Le second ne plafonne pas.
NB_MAX = 20 NB_MAX = 20
# En deçà, un article est annoncé comme « publié » plutôt que « mis à jour ». # En deçà, un article est annoncé comme « publié » plutôt que « mis à jour ».
@ -34,10 +42,24 @@ EXCLUS = {"index.md"}
_pages: dict[str, dict[str, str]] = {} _pages: dict[str, dict[str, str]] = {}
_dans_la_nav: set[str] = set() _dans_la_nav: set[str] = set()
_rubriques: dict[str, str] = {}
def _rubrique_par_page(items, rubrique=""):
"""Associe chaque page de la navigation au titre de la section qui la porte.
On garde la section de premier niveau — « Guides », « Contribuer » — plutôt que la
plus proche : c'est l'échelle à laquelle un lecteur range les pages.
"""
for item in items:
if item.is_section:
yield from _rubrique_par_page(item.children, rubrique or item.title)
elif item.is_page:
yield item.file.src_uri, rubrique
def on_nav(nav, config, files): def on_nav(nav, config, files):
"""Retient les pages de la navigation. """Retient les pages de la navigation, et sous quelle rubrique elles vivent.
Ce qui en est absent — les redirections laissées derrière un article déplacé, par Ce qui en est absent — les redirections laissées derrière un article déplacé, par
exemple — n'a pas à être annoncé comme une nouveauté. exemple — n'a pas à être annoncé comme une nouveauté.
@ -45,6 +67,9 @@ def on_nav(nav, config, files):
_dans_la_nav.clear() _dans_la_nav.clear()
_dans_la_nav.update(page.file.src_uri for page in nav.pages) _dans_la_nav.update(page.file.src_uri for page in nav.pages)
_rubriques.clear()
_rubriques.update(_rubrique_par_page(nav.items))
def on_page_context(context, page, config, nav): def on_page_context(context, page, config, nav):
"""Retient le titre et l'URL de chaque page rendue.""" """Retient le titre et l'URL de chaque page rendue."""
@ -106,24 +131,30 @@ def _historique(depot: Path, dossier: str) -> tuple[dict, dict]:
return modifiees, creees return modifiees, creees
def _autoriser_la_lecture_croisee(site_dir: Path) -> None: def _autoriser_la_lecture_croisee(site_dir: Path, fichiers: list[str]) -> None:
"""Pose l'en-tête CORS sur le seul fichier qui en a besoin. """Pose l'en-tête CORS sur les seuls fichiers qui en ont besoin.
Le fichier est lu depuis alpinux.org : sans cet en-tête, le navigateur refuse la Ils sont lus depuis alpinux.org : sans cet en-tête, le navigateur refuse la
réponse et la section reste masquée. Le bloc s'ajoute à un `.htaccess` déjà là réponse et la section reste masquée. Le bloc s'ajoute à un `.htaccess` déjà là
plutôt que de le remplacer — on ne sait pas ce qu'il porte d'autre. plutôt que de le remplacer — on ne sait pas ce qu'il porte d'autre — et n'est
écrit que pour les fichiers qui n'y figurent pas encore.
""" """
bloc = (
"<IfModule mod_headers.c>\n"
f' <Files "{FICHIER}">\n'
' Header set Access-Control-Allow-Origin "*"\n'
" </Files>\n"
"</IfModule>\n"
)
fichier = site_dir / ".htaccess" fichier = site_dir / ".htaccess"
existant = fichier.read_text(encoding="utf-8") if fichier.exists() else "" existant = fichier.read_text(encoding="utf-8") if fichier.exists() else ""
if FICHIER in existant:
manquants = [nom for nom in fichiers if nom not in existant]
if not manquants:
return return
bloc = "<IfModule mod_headers.c>\n"
for nom in manquants:
bloc += (
f' <Files "{nom}">\n'
' Header set Access-Control-Allow-Origin "*"\n'
" </Files>\n"
)
bloc += "</IfModule>\n"
separateur = "\n" if existant and not existant.endswith("\n") else "" separateur = "\n" if existant and not existant.endswith("\n") else ""
fichier.write_text(existant + separateur + bloc, encoding="utf-8") fichier.write_text(existant + separateur + bloc, encoding="utf-8")
@ -144,7 +175,7 @@ def on_post_build(config):
log.info("derniers-articles : clone superficiel, les dates ne sont pas fiables") log.info("derniers-articles : clone superficiel, les dates ne sont pas fiables")
seuil = datetime.now(timezone.utc) - timedelta(days=JOURS_NOUVEAU) seuil = datetime.now(timezone.utc) - timedelta(days=JOURS_NOUVEAU)
articles = [] pages = []
for chemin, modifiee in modifiees.items(): for chemin, modifiee in modifiees.items():
prefixe = docs_dir.name + "/" prefixe = docs_dir.name + "/"
@ -162,30 +193,43 @@ def on_post_build(config):
except ValueError: except ValueError:
nouveau = False nouveau = False
articles.append({ pages.append({
"titre": page["titre"], "titre": page["titre"],
"url": page["url"], "url": page["url"],
"date": creee if nouveau else modifiee, "date": creee if nouveau else modifiee,
"nouveau": nouveau, "nouveau": nouveau,
"rubrique": _rubriques.get(src_uri, ""),
}) })
if not articles: if not pages:
log.info("derniers-articles : aucune page datée, fichier non écrit") log.info("derniers-articles : aucune page datée, fichier non écrit")
return return
articles.sort(key=lambda article: article["date"], reverse=True) pages.sort(key=lambda page: page["date"], reverse=True)
articles = articles[:NB_MAX]
genere = datetime.now(timezone.utc).replace(microsecond=0).isoformat() genere = datetime.now(timezone.utc).replace(microsecond=0).isoformat()
contenu = json.dumps({"genere": genere, "articles": articles},
ensure_ascii=False, indent=2) + "\n" # L'accueil lit ce fichier depuis longtemps : il garde ses quatre clés, sans la
# rubrique dont il n'a que faire.
recents = [
{clef: page[clef] for clef in ("titre", "url", "date", "nouveau")}
for page in pages[:NB_MAX]
]
ecrits = {
FICHIER_RECENTS: {"genere": genere, "articles": recents},
FICHIER_TOUTES: {"genere": genere, "pages": pages},
}
try: try:
(site_dir / FICHIER).write_text(contenu, encoding="utf-8") for nom, contenu in ecrits.items():
_autoriser_la_lecture_croisee(site_dir) texte = json.dumps(contenu, ensure_ascii=False, indent=2) + "\n"
(site_dir / nom).write_text(texte, encoding="utf-8")
_autoriser_la_lecture_croisee(site_dir, list(ecrits))
except OSError as erreur: except OSError as erreur:
log.info("derniers-articles : écriture impossible (%s), fichier non écrit", log.info("derniers-articles : écriture impossible (%s), fichier non écrit",
erreur) erreur)
return return
log.info("derniers-articles : %d article(s) publié(s) dans %s", len(articles), FICHIER) log.info("derniers-articles : %d page(s) publiée(s) dans %s, %d dans %s",
len(pages), FICHIER_TOUTES, len(recents), FICHIER_RECENTS)

View file

@ -89,6 +89,11 @@ nav:
- J'organise une install party: alpinux/installparty-organisateur.md - J'organise une install party: alpinux/installparty-organisateur.md
- Présentations passées: alpinux/presentations.md - Présentations passées: alpinux/presentations.md
- Événements & RDV: alpinux/evenements.md - Événements & RDV: alpinux/evenements.md
- Messagerie et listes:
- alpinux/messagerie.md
- S'abonner et se désabonner: alpinux/messagerie-abonnement.md
- Écrire et recevoir des messages: alpinux/messagerie-membre.md
- Gérer une liste de diffusion: alpinux/messagerie-gestionnaire.md
- Vos données, vos adresses, nos envois: alpinux/donnees-personnelles.md - Vos données, vos adresses, nos envois: alpinux/donnees-personnelles.md
- Présentations: - Présentations:
- presentations/libreoffice.md - presentations/libreoffice.md
@ -114,6 +119,7 @@ nav:
- technique/matrix.md - technique/matrix.md
- technique/arduino-ide.md - technique/arduino-ide.md
- technique/serveur-debian-13.md - technique/serveur-debian-13.md
- technique/serveur-install-party.md
- technique/bigbluebutton.md - technique/bigbluebutton.md
- technique/ateliers.md - technique/ateliers.md
- Communication: - Communication: