Nouvelle version de Zola et petites corrections

This commit is contained in:
julien committed 2026-09-30 10:30:36 +02:00
1 parent 7e09d12345
commit 58bcc189ab
9 files changed
+92 -22

No files matched your search

+1
View File
@@ -0,0 +1 @@
FRONTEND_PORT=8002
+2
View File
@@ -1,3 +1,5 @@
.env
# Site généré par Zola
src/public/
+3
View File
@@ -1,4 +1,6 @@
lescarnetsdakkadien.netig.net {
encode zstd gzip
@assets path /main.css /giallo.css /images/* /fonts/*
@documents {
not path /main.css /giallo.css /images/* /fonts/*
@@ -7,6 +9,7 @@ lescarnetsdakkadien.netig.net {
header {
Content-Security-Policy "default-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; object-src 'none'"
Referrer-Policy "strict-origin-when-cross-origin"
Permissions-Policy "camera=(), geolocation=(), microphone=()"
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
+6 -6
View File
@@ -4,13 +4,13 @@ COPY scripts/check-content.py scripts/_schema.py ./scripts/
COPY src ./src
RUN python3 scripts/check-content.py
FROM ghcr.io/getzola/zola:v0.23.1 AS builder
WORKDIR /project
COPY --from=validator /project/src ./
RUN ["/zola", "build"]
FROM ghcr.io/getzola/zola:v0.23.6 AS build
WORKDIR /site
COPY --from=validator /project/src /site
RUN ["zola", "build", "--output-dir", "/public"]
FROM ghcr.io/static-web-server/static-web-server:2
FROM ghcr.io/static-web-server/static-web-server:2.44.0
COPY --from=build /public /public
ENV SERVER_ROOT=/public \
SERVER_CACHE_CONTROL_HEADERS=false \
SERVER_SECURITY_HEADERS=false
COPY --from=builder /project/public /public
+6 -5
View File
@@ -29,7 +29,7 @@ Les commandes publiques sont :
- `new-content.py` : création d’un brouillon ;
- `update-content.py` : mise à jour de l’horodatage `updated` ;
- `check-content.py` : validation structurelle des fiches.
- `check-content.py` : validation des noms et de la structure des fiches.
`_schema.py` est leur module commun et ne se lance pas directement.
@@ -424,6 +424,9 @@ Zola génère `giallo.css`, tandis que le HTML rendu utilise les classes corresp
Pour chaque fiche, il vérifie :
- le nom de fichier en Unicode NFC ;
- les identifiants en minuscules avec lettres Unicode, chiffres et tirets ;
- la forme `mzl-NNN.md` pour les signes ;
- la lecture en UTF-8 ;
- la présence d’un front matter délimité par `+++` ;
- la validité du TOML ;
@@ -453,11 +456,9 @@ Le validateur accepte les chaînes et tableaux vides. Il ne contrôle pas :
- la qualité ou l’exactitude linguistique des valeurs ;
- les champs supplémentaires ;
- la forme des noms de fichiers ;
- la correspondance entre le numéro MZL et le nom du fichier ;
- la normalisation Unicode des fichiers existants.
- la correspondance entre le numéro MZL du front matter et le nom du fichier.
Zola reste responsable des liens, taxonomies et templates. Le générateur reste responsable de produire des noms de fichiers valides et des squelettes complets.
Zola reste responsable des liens, taxonomies et templates. Le générateur et le validateur appliquent les mêmes conventions de nommage ; le générateur reste responsable de produire des squelettes complets.
## Vérification avant publication
+23 -7
View File
@@ -4,7 +4,7 @@
Site public : <https://lescarnetsdakkadien.netig.net>
Le site est généré avec **Zola 0.23.1** et **Tera 2** à partir de fichiers Markdown. Il est entièrement statique : aucune base de données, aucune interface d’administration et aucune exécution côté serveur ne sont nécessaires.
Le site est généré avec **Zola 0.23.6** et **Tera 2** à partir de fichiers Markdown. Il est entièrement statique : aucune base de données, aucune interface d’administration et aucun code applicatif côté serveur ne sont nécessaires.
## Contenu
@@ -21,7 +21,7 @@ La page d’accueil présente le dernier article et les trois fiches les plus r
Pour le développement local :
- Python 3.11 ou ultérieur ;
- Zola 0.23.1 ;
- Zola 0.23.6 ;
- Git, si le dépôt est versionné avec Git.
Pour le déploiement décrit ici :
@@ -83,7 +83,7 @@ Les contenus sont stockés dans `src/content/`. Trois commandes accompagnent leu
- `new-content.py` crée un brouillon complet pour une section donnée ;
- `update-content.py` actualise son horodatage `updated` ;
- `check-content.py` vérifie la présence et le type des champs prévus.
- `check-content.py` vérifie les noms de fichiers ainsi que la présence et le type des champs prévus.
Le module `_schema.py` centralise les modèles utilisés par le générateur et le validateur ; il n’est pas exécuté directement.
@@ -93,6 +93,7 @@ Les commandes, conventions et modèles de front matter sont détaillés dans le
```text
.
├── .env.example
├── Caddyfile.example
├── Dockerfile
├── compose.yml
@@ -128,8 +129,20 @@ Les commandes, conventions et modèles de front matter sont détaillés dans le
L’image est construite en trois étapes :
1. validation des fiches avec Python ;
2. génération du site avec Zola 0.23.1 ;
3. copie des seuls fichiers publics dans un serveur HTTP statique.
2. génération du site avec Zola 0.23.6 ;
3. copie des seuls fichiers publics dans static-web-server 2.44.0.
Créer la configuration locale à partir de l’exemple :
```sh
cp .env.example .env
```
Le seul réglage nécessaire est le port HTTP publié sur l’hôte :
```dotenv
FRONTEND_PORT=8002
```
Vérifier puis démarrer le service :
@@ -138,12 +151,14 @@ docker compose config
docker compose up -d --build
```
Le conteneur est exposé uniquement sur l’interface locale de l’hôte :
Par défaut, le conteneur est exposé uniquement sur l’interface locale de l’hôte :
```text
http://127.0.0.1:8002
```
Le port peut être changé dans `.env`. Si Caddy est utilisé, adapter alors aussi le port de `reverse_proxy` dans `Caddyfile.example` ou dans la configuration de l’hôte.
Pour mettre à jour le déploiement :
```sh
@@ -163,10 +178,11 @@ Caddy prend en charge le domaine public, HTTPS, les en-têtes de sécurité et l
[`Caddyfile.example`](Caddyfile.example) fournit le bloc correspondant au port publié par `compose.yml`. Il applique :
- la compression `zstd` ou `gzip` selon le client ;
- `Cache-Control: no-cache` aux documents ;
- un cache navigateur d’une journée aux feuilles de style, images et polices ;
- une CSP compatible avec les ressources locales du site ;
- HSTS, `Referrer-Policy`, `X-Content-Type-Options` et `X-Frame-Options`.
- HSTS, `Referrer-Policy`, `Permissions-Policy`, `X-Content-Type-Options` et `X-Frame-Options`.
Le fichier peut être importé au niveau principal du Caddyfile de l’hôte ou servir de modèle pour un bloc existant. Sa syntaxe peut être contrôlée avec :
+2 -2
View File
@@ -3,6 +3,6 @@ name: les-carnets-d-akkadien
services:
zola:
build: .
ports:
- "127.0.0.1:8002:80"
restart: unless-stopped
ports:
- "127.0.0.1:${FRONTEND_PORT:-8002}:80"
+46
View File
@@ -1,8 +1,10 @@
#!/usr/bin/env python3
"""Vérifie que chaque fiche contient les champs prévus pour son modèle."""
import re
import sys
import tomllib
import unicodedata
from datetime import datetime
from pathlib import Path
from typing import Any
@@ -25,6 +27,8 @@ COMMON_FIELDS = {
"draft": bool,
}
MZL_FILENAME_RE = re.compile(r"mzl-([0-9]{3})")
class Validator:
def __init__(self) -> None:
@@ -68,6 +72,46 @@ def read_front_matter(
return None
def check_filename(
path: Path,
section: str,
validator: Validator,
) -> None:
stem = path.stem
normalized = unicodedata.normalize("NFC", stem)
if stem != normalized:
validator.error(path, "nom de fichier non normalisé en Unicode NFC")
if section == "signes":
match = MZL_FILENAME_RE.fullmatch(stem)
if match is None or not 1 <= int(match.group(1)) <= 999:
validator.error(path, "un signe doit être nommé mzl-NNN.md (001 à 999)")
return
allow_leading_hyphen = section == "vocabulaire"
body = stem[1:] if stem.startswith("-") else stem
if stem.startswith("-") and not allow_leading_hyphen:
validator.error(
path,
"seul le vocabulaire accepte un nom commençant par un tiret",
)
return
if (
not body
or stem in {".", "..", "_index"}
or stem != stem.lower()
or stem.endswith("-")
or "--" in stem
or not all(character.isalnum() or character == "-" for character in body)
):
validator.error(
path,
"nom de fichier invalide : utiliser des lettres Unicode minuscules, "
"des chiffres et des tirets",
)
def check_type(
value: Any,
expected_type: type,
@@ -189,6 +233,8 @@ def main() -> int:
validator.error(path, f"section inconnue : {section}")
continue
check_filename(path, section, validator)
result = read_front_matter(path, validator)
if result is None:
continue
+3 -2
View File
@@ -1,7 +1,8 @@
{% extends "base.html" %}
{% set term_label = term.name | replace(from="-", to=" ") if taxonomy.name != "lectures" else term.name %}
{% block title %}{% if taxonomy.name == "natures" %}Mots : {{ term_label }}{% elif taxonomy.name == "lectures" %}Lecture « {{ term_label }} »{% elif taxonomy.name == "genres" %}Textes : {{ term_label }}{% elif taxonomy.name == "themes" %}Grammaire : {{ term_label }}{% else %}{{ term_label }}{% endif %} · {{ config.title }}{% endblock title %}
{% block title %}
{% set term_label = term.name | replace(from="-", to=" ") if taxonomy.name != "lectures" else term.name %}{% if taxonomy.name == "natures" %}Mots : {{ term_label }}{% elif taxonomy.name == "lectures" %}Lecture « {{ term_label }} »{% elif taxonomy.name == "genres" %}Textes : {{ term_label }}{% elif taxonomy.name == "themes" %}Grammaire : {{ term_label }}{% else %}{{ term_label }}{% endif %} · {{ config.title }}{% endblock title %}
{% block content %}
{% set term_label = term.name | replace(from="-", to=" ") if taxonomy.name != "lectures" else term.name %}
{% set heading = term_label %}{% set description = "Entrées classées dans cet index." %}{% set back_url = get_url(path=taxonomy.name, trailing_slash=true) %}{% set back_label = "Retour à l’index" %}
{% if taxonomy.name == "natures" %}
{% set heading = "Mots : " ~ term_label %}{% set description = "Fiches de vocabulaire classées comme « " ~ term_label ~ " »." %}{% set back_label = "Toutes les natures grammaticales" %}