Nouvelle version de Zola et petites corrections
This commit is contained in:
1 parent
7e09d12345
commit
58bcc189ab
9 files changed
+92
-22
No files matched your search
@@ -0,0 +1 @@
|
||||
FRONTEND_PORT=8002
|
||||
@@ -1,3 +1,5 @@
|
||||
.env
|
||||
|
||||
# Site généré par Zola
|
||||
src/public/
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -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" %}
|
||||
|
||||
Reference in new issue
Block a user